Programmation graphique

Chapitre 2 : Les Sprites : Affichage

 

Tutoriel présenté par : Robert Gillard (Gondulzak)
Publication : 26 novembre 2013
Dernière mise à jour : 11 novembre 2015

 

   Préliminaires

   Cette série de chapitres va nous permettre de nous plonger désormais dans la programmation graphique avec Xna. Une connaissance élémentaire des classes du C++ ou du C# seraient un « plus » à la compréhension de certains éléments de ces tutoriels. smiley

   Je vais commencer par l'affichage et les différents moyens de faire défiler un sprite (principes d'animation). Je terminerai le deuxième chapitre par un mini jeu, nous allons chasser le lièvre (ou la sorcière, nous verrons...) à l'aide de la souris. Ai-je bien dit la souris ? Oui, c'est la souris qui va chasser, ah ah ! cool

   Les chapitres suivants nous conduiront à découvrir d'autres aspects de la programmation graphique, soit la création d'une simple caméra, ainsi que les différents effets de profondeur et de zoom. cool

   Mais rien ne sert de courir, nous allons commencer par le commencement (c'est mieux non ? wink), c'est-à-dire par l'affichage d'un sprite à l'écran.

 

Vous pourrez télécharger ici l'archive correspondant aux projets de ce chapitre

 

   1 – Afficher un sprite à l'écran

   Un sprite (un des assets / une des ressources utilisé(e)s par un programme) est une image 2d que Xna utilise pour permettre l'affichage de la source (le sprite, pas l'eau...) vers une destination (une surface de l'écran). On utilisera de préférence le format png, car il offre une bonne compression et est non-destructeur (contrairement au jpeg par exemple dont l'utilisation dégraderait la qualité de vos graphismes, ce qui serait dommage ! laugh).      

 

Données de la source Données de la destination
  Type   Type
 Image
 Localisation
 Origine
Texture2D 
Rectangle
Vector2 

  Localisation sur l'écran et échelle

 Rectangle ou rectangle de destination
 Rotation
 Couleur 
 Profondeur 
 Effets 
Vector2 et float
Rectangle
float
RGBA
float
SpriteEffects

                                                                  

   Nous allons bientôt voir les différentes façon de dessiner avec la classe SpriteBatch qui je vous le rappelle, est utilisée pour le dessin des textures.
   Je ne reviendrai pas sur la « Découverte et l'Installation de Xna de Microsoft ». Je vous conseille de relire en entier le premier chapitre que Jay avait commencé sur le « Big Tuto Xna » et de créer un projet comme il l'explique dans son tuto. Vous nommerez ce projet AfficheUnSprite.

   Vous savez aussi que si vous compilez votre projet à cet instant vous obtiendrez une fenêtre vide, de couleur Color.CornflowerBlue, comme représentée ci-dessous.

 

   Mais une fenêtre vide ne nous intéresse pas car nous désirons y afficher des images, Ok ? devil

   Maintenant que votre projet est ouvert dans la fenêtre de votre IDE de Visual Studio, nous allons déclarer quelques variables qui nous seront utiles. smiley

   A l'intérieur de notre classe Game1, juste en dessous de SpriteBatch spriteBatch, nous déclarons une variable spritePosition de type Vector2 et qui représente la position en x et y sur l'écran du sprite que nous allons y afficher. Nous déclarons ensuite une variable spriteTexture, de type Texture2D et qui représente la surface qu'utilise notre sprite. (Nous verrons plus tard que dans certains cas nous allons avoir besoin des dimensions de nos sprites calculées en pixels).

   A l'intérieur de notre fonction Initialize(), nous donnons des valeurs en x et y à notre variable spritePosition de type Vector2 en entrant les deux lignes suivantes juste au-dessus de l'instruction base.Initialize();

spritePosition.X = 150;
spritePosition.Y = 150;

   Nous chargeons ensuite notre sprite dans le « Content Project » en entrant l'instruction :

spriteTexture = Content.Load<Texture2D>("monster2");

 

   Juste en dessous de l'objet spriteBatch créé par l'instruction :

spriteBatch = new SpriteBatch(GraphicsDevice); 

 

monster2.png

 

   Nous n'avons rien à faire pour l'instant dans la fonction Update() car nous ne faisons qu'afficher un sprite immobile. Par contre nous devons le dessiner, et c'est la fonction Draw() qui va s'en charger. Et dans cette fonction Draw() vous ajoutez ensuite les trois lignes suivantes :

 

	spriteBatch.Begin();
	spriteBatch.Draw(spriteTexture, spritePosition, Color.White);
	spriteBatch.End();

 

Maintenant le code de votre projet devrait ressembler à ceci (N.B. : pensez à enlever l'espace de la ligne: spriteTexture = Content.Load<Texture2D>("monster2"); si vous faites du copier/coller. Je suis obligé de l'ajouter sinon le navigateur croit que c'est une balise HTML... cheeky) : 

 

//Programmation graphique
// 1 - Affichage d'un sprite

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace AfficheUnSprite
{
    
    // Classe principale du projet
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        private Vector2 spritePosition;
        Texture2D spriteTexture;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        
        // Fonction d'initialisation du projet
        protected override void Initialize()
        {
            // TODO: On ajoute ici la logique d'initialisation
            spritePosition.X = 150;
            spritePosition.Y = 150;

            base.Initialize();
        }

        // Fonction de chargement des divers medias
        protected override void LoadContent()
        {
            // Crée un nouveau SpriteBatch qui sera utilisé pour créer les textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: utiliser this.Content pour charger le contenu du projet ici
            spriteTexture = Content.Load< texture2d>("monster2");
        }

       
        protected override void UnloadContent()
        {
            // TODO: Décharger tout le contenu non – ContentManager ici 
        }


        //Fonction Update() - Mise à jour du programme, du monde, tests de collision,
        //gestion des entrées et de l'audio
        protected override void Update(GameTime gameTime)
        {
            // Permet la sortie du jeu
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Ajoutez la logique de vos mises à jour ici

            base.Update(gameTime);
        }


        // Fonction Draw() Appelée pour dessiner le monde      
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Ajoutez vos dessins ici
            spriteBatch.Begin();
            spriteBatch.Draw(spriteTexture, spritePosition, Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

 

   Maintenant, dans l'explorateur de solution (fenêtre du haut à droite), faites un clic droit sur AfficheUnSpriteContent(Content) :

 

   Ceci va avoir pour effet d'ouvrir deux nouvelles fenêtres. Faites alors un clic gauche sur Ajouter - > Element existant... afin d'ouvrir votre dossier AfficheUnSpriteContent dans lequel vous allez charger notre sorcière (monster2.png) qui va venir s'intégrer dans votre projet (voir dernière ligne de l'image ci-dessus).

   Il ne vous reste plus alors qu'à sauver votre projet, et ensuite le compiler en cliquant sur Déboguer - > Générer la solution.

   Pressez ensuite la touche F5 pour admirer le résultat de votre travail. heart

 

 

          Mais avant d'aborder une seconde façon de dessiner avec la classe SpriteBatch nous allons devoir nous pencher quelque peu sur la conception élémentaire d'un programme Xna.


    2 – Un peu de théorie...

            a – Structure d'un programme

   Tout en haut de votre programme vous apercevez toute une série d'instructions dénommées using ( using System , using System.Collections.Generic... ). Ces directives intègrent à votre programme des fichiers supplémentaires générés par Xna et qui permettront la gestion des « audio », « input », « media » et autres systèmes nécessaires au bon déroulement de votre projet (un peu comme les #include en C).

   Vient ensuite un espace de noms dénommé namespace AfficheUnSprite. Ces espaces de noms rassemblent, sous une même étiquette, une collection de classes et de fonctions se rapportant généralement à un même ensemble. Notez que le C# permet de créer plusieurs espaces de noms dans un même programme quand la logique le préconise.

   A l'intérieur de l'espace de noms nous rencontrons la classe principale du projet (la classe Game1).
   Nous voyons que cette classe est elle-même une classe dérivée de la classe Microsoft.Xna.Framework.Game. Cette classe est déclarée avec l'option public car sa visibilité doit être étendue à tout le programme.

   Le constructeur Game1() déclaré public également, initialise le système graphique et ensuite, indique au programme où il pourra trouver les images, les sons et autres assets qui seront utilisés dans le jeu.

   Nous voyons ensuite que la méthode Initialize() est déclarée protected (protégée), ce qui veut dire que ses membres ne pourront être utilisés qu'à l'intérieur de la classe ou des classes dérivées.
   Nous voyons justement que la fonction est également déclarée avec le mot-clé override (permet une implémentation totalement différente d'une méthode dans une classe dérivée).

   En C#, dans une classe de base, une méthode peut-être soit virtuelle soit abstraite et dans la classe dérivée la méthode doit-être déclarée override afin de pouvoir en surcharger les membres.

   La méthode LoadContent(), nous l'avons vu, charge tous les médias nécessaires au déroulement du programme. Elle crée en premier lieu un objet spriteBatch qui sera utilisé par le système graphique pour créer les textures.

   La méthode UnloadContent() n'a pas d'intérêt pour nous actuellement, Xna s'occupant généralement de « faire le ménage » lui-même au niveau de la mémoire (faudrait penser à un truc pareil pour nos vieux jours!! laugh).

   Nous avons également vu que la méthode Update() faisait continuellement la mise à jour de nos données, la fonction Draw() se chargeant quant à elle de dessiner le tout à l'écran.

 

 

             b – Création de l'aire du jeu

   Une fois un projet créé dans l'IDE, il faut bien sûr déterminer quelles seront les variables nécessaires au bon déroulement du programme. Vous déclarerez celles-ci dans la classe principale de votre projet (Game1).

   Selon la portée que vous voudrez donner à vos variables, vous les déclarerez soit private soit public. Vous pourrez les initialiser au choix dans la classe Game1(), à la suite de l'instruction SpriteBatch spriteBatch ou à l'intérieur de la méthode Initialize(), au début de celle-ci.


                      La couleur du background

   Vous remarquerez que Xna a pour habitude de colorier le background avec la couleur CornflowerBlue qui est un objet de la classe Color (voir dans la fonction Draw() , GraphicsDevice.Clear(Color.CornflowerBlue)).


   La classe Color contient de nombreuses couleurs d'origine et vous pouvez en voir la liste en tapant un point (dot) après le mot-clé Color.                                                 

 

   Vous devez pourtant savoir que vous pouvez créer vos propres couleurs RGB en définissant vous-même une nouvelle variable.

   A l'intérieur de la classe Game1(), en dessous de la ligne Texture2D spriteTexture, déclarez la variable suivante :

   Color myColor;

 

   et initialisez-la dans la fonction Initialize(), (non, ce n'est pas un jeu de mots... cheeky).
   Avant l'instruction base.Initialize(); Tapez par exemple :

myColor = new Color(200, 255, 25);

 

   et dans la fonction Draw(), remplacez la ligne GraphicsDevice.Clear(Color.CornflowerBlue); par : GraphicsDevice.Clear(myColor);

 

Sauvez, compilez, et pressez F5, vous obtiendrez la fenêtre suivante :  

 

  Oui je sais, cela ressemble plutôt à un jaune-vert sale, mais ce n'est qu'un exemple... cheeky

 

              c – Une fonction surchargée de la classe SpriteBatch

   Bien, nous pouvons maintenant montrer une autre manière de dessiner avec la classe SpriteBatch.
   Nous avons vu une fonction draw prenant trois arguments (une position, une texture et une couleur). Voici maintenant un exemple de surcharge de cette fonction qui utilise un nombre plus élevé d'arguments.

   

   Reprenons notre code :

   Dans la classe Game1(), effacez les deux lignes : 

                     private Vector2 spritePosition;
                     Texture2D spriteTexture; 

 

   Et remplacez-les par le tout le code suivant :

                     //Données sources
                     private Texture2D spriteTexture;
                     private Rectangle localisation;
                     private Vector2 origine;

                     //Données de destination
                     public Vector2 spritePosition;
                     public Vector2 sprite2Position;
                     public Color destColor;
                     public float rotation;
                     public float echelle;
                     public float profondeur;
                     public SpriteEffects spriteEffet;

                     Color myBackgroundColor;

 

  Remplacez ensuite toute la fonction Initialize() par celle-ci :

                     protected override void Initialize()
                    {
                       // TODO: Add your initialization logic here
                       spritePosition.X = 250;
                       spritePosition.Y = 150;
                       sprite2Position.X = 120;
                       sprite2Position.Y = 180;

                       myBackgroundColor = new Color(200, 0, 0);
                       rotation = 0.30f;
                       echelle = 2.5f;
                       localisation = new Rectangle(0, 0, 60, 50);
                       origine = Vector2.Zero;
                       spriteEffet = SpriteEffects.FlipHorizontally;
                       profondeur = 1.0f;
                       destColor = Color.White;

                       base.Initialize();
                    }

 

   Vous voyez que nous remplaçons en outre notre background par une nouvelle couleur... wink  Bien, nous arrivons au bout, remplacez maintenant la fonction Draw() par celle-ci :
 

                     protected override void Draw(GameTime gameTime)
                   {
                      GraphicsDevice.Clear(myBackgroundColor);

                      // TODO: Add your drawing code here
                      spriteBatch.Begin();

                      spriteBatch.Draw(spriteTexture, spritePosition, Color.White);

                      spriteBatch.Draw(spriteTexture,
                                       sprite2Position,
                                       localisation,
                                       destColor,
                                       rotation,
                                       origine,
                                       echelle,
                                       spriteEffet,
                                       profondeur);
                      spriteBatch.End();

                      base.Draw(gameTime);
                   }



 

   Sauvez le projet, compilez, pressez F5 et aaaaaaaah ! devil  Nous obtenons la fenêtre suivante : 

 

 

   Bien, ceci mérite quelques explications car nous avons ajouté des variables. Voyons ce que représentent celles-ci.

   J'ai scindé les définitions des variables en « Données sources » et « Données de destination ».
   Je m'explique :

 

      Que représentent les données source ?

     

Variable  Type  Utilité
spriteTexture  Texture2D  Représente le sprite qui sera affiché à l'écran, ici « monster2.png »
localisation  Rectangle  Il s'agit des paramètres de localisation du sprite, soit son origine, sa largeur et sa hauteur.
origine  Vector2  Point d'origine (0, 0)

     

   Mais avant de passer aux données de destination je voudrais faire ici, une mise au point. En effet, certains d'entre vous pourraient avoir quelque problème pour appréhender les notions des variables « localisation » et « origine » car elles font intervenir toutes deux un point d'origine.

 

     Remarque importante

   Vous aurez remarqué que j'ai utilisé un sprite déjà découpé pour le faire afficher par la fonction Draw(). Il se fait que j'aurais aussi pu utiliser un tileset dans lequel j'aurais découpé un sprite.

   Prenons par exemple le tileset utilisé par Jay dans le « Big tuto SDL 1.2 » et dont voici la représentation : 

    Nous savons que chacune des tiles a une dimension de 32 x 32 pixels, la tile 1 (vide) étant en position 0, la tile 2 (feuillage) en position 1, etc...

    Si nous regardons la fonction Initialize() actuelle, nous lisons :

                        localisation = new Rectangle(0, 0, 60, 50);
                        origine = Vector2.Zero;

   En effet, la localisation du sprite a bien son origine (indépendamment de tout système de coordonnées) en (0, 0), et le rectangle qui représente le sprite est bien un rectangle d'origine (0, 0), de largeur 60 et de hauteur 50 (pixels). Vector2.Zero étant l'origine de notre fenêtre (0, 0).

   Supposons que je veuille afficher la tile 1 (feuillage) à l'écran, à la place de notre sorcière.
   Si nous prenons le cas de la tile 1 du tileset ci-dessus, nous pouvons dire que sa localisation est (32, 0, 32, 32) sur le tileset et que le point d'origine de la fenêtre à partir duquel nous voulons dessiner est Vector2.Zero, soit (0, 0). La tile sera donc affichée en x = 120 et y = 180 à partir des coordonnées d'origine de la fenêtre (0, 0).

   Expérimentons ceci en modifiant les valeurs de localisation sans toucher aux autres variables actuellement soit : 

   localisation = new Rectangle(32, 0, 32, 32);

   et en modifiant la variable spriteTexture dans notre fonction LoadContent() :

   spriteTexture = Content.Load<Texture2D>("tileset");

 

Sauvez le projet, compilez et pressez F5 , vous obtiendrez alors la figure suivante :

 

 

 Ouf... Nous pouvons maintenant passer aux « Données de destination ».

 

   Que représentent les données de destination ?

 Variable  Type  Utilité
 spritePosition  Vector2     Position en x,y du sprite à l'échelle 1:1
 sprite2Position  Vector2     Position en x,y du sprite à l'échelle 2,5:1
 destColor  Color    permet la coloration de l'image avec une couleur (white = pas de coloration), utile pour faire clignoter un sprite. wink
 rotation  float    Rotation du sprite autour d'un point (à définir).
 echelle  float    Echelle du sprite (utile pour les zooms).
 profondeur  float    Permet de placer le sprite sur des couches différentes (layers). Sa valeur peut-être comprise entre 0 et 1 mais est ignorée par défaut.
 spriteEffet  SpriteEffects    Retourne le sprite horizontalement ou vertivalement. Ses valeurs sont : FlipHorizontally, FlipVertically ou None (grâce à ça, il suffit de dessiner votre sprite tourner d'un côté pour avoir l'autre côté automatiquement wink).

 

   Voilà, je pense que ceci est déjà un bon début pour nos tutoriels de Programmation Graphique.
   Dans le prochain chapitre, nous verrons la gestion des « input » pour le déplacement des sprites, ainsi que notre mini-jeu de « Chasse aux sorcières » où les sprites se déplacent tout seuls. A bientôt !

 

 

Connexion

CoalaWeb Traffic

Today85
Yesterday182
This week561
This month4377
Total1738592

28/03/24