begin process at 2008 08 08 21:46:20
1 223 607 membres
365 nouveaux aujourd'hui
14 230 membres club

Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum.
Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

XNA FRAMEWORK : LES BASES DE L'AFFICHAGE 2D / CRÉATION D'UN GAMECOMPONENT


Information sur le tutorial

Catégorie :XNA Tutorial .NET ( DotNet ) Date de création : 03/10/2006 21:28:30 Vu : 8 713 fois

Note :
9,67 / 10 - par 3 personnes
9,67 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (1)
Ajouter un commentaire et/ou une note


Description

.

Tutorial

Ce tutorial n'est valable que pour la beta 1 de XNA Game Studio Express. Réécriture en cours pour couvrir les changements apportés par la version 1.0

Mes sources sont quant a elles mises à jour et couvrent deja ces changements.

 

 

XNA Framework :
Les bases de l'affichage 2D / Création d'un GameComponent


Introduction sur le XNA Framework

XNA Framework est une plate-forme permettant de développer des jeux en C# sur PC et XBOX 360, le tout avec un pourcentage infime de changements dans le code (estimé à 5-10% selon l'équipe de développement, à ce stade de l'évolution du Framework).

XNA Framework se base sur le .Net Compact Framework 2.0, et pour le moment n'est supporté que sur Windows XP malheureusement. Mais nous n'en sommes qu'à une beta 1 de XNA Game Studio Express.

 

Effectuer un rendu 2D

Ce petit tutorial n'aura pas pour but d'explorer toutes les facettes de l'affichage 2D, mais d'expliquer comment effectuer simplement la gestion et le rendu de plusieurs sprites à travers un mini-projet : un « Scrolling Background ».

Nous allons donc développer un petit composant (GameComponent) qui permettra l'affichage d'un fond multi-plan (un sprite = un plan), chaque plan ayant sa propre vitesse de défilement. Ceci afin de créer un effet de profondeur.

Voici les classes du XNA Framework que nous emploieront :

· Game :  La classe principale d'un jeu doit hériter de la classe Game.Cette classe contient 3 méthodes qui nous intéressent particulièrement

Nom

Description

protected void Draw()

C'est dans cette méthode que seront placées toutes les instructions de rendu d'un jeu

protected void Update()

C'est dans cette méthode que sera gérée toute la logique d'un jeu (Mise à jour de la position des objets, ...)

protected virtual  void OnStarting()

Cette méthode est appelée au démarrage du jeu, juste avant l'affichage de la première image à l'écran

  Elle contient également une propriété GameComponents, qui est une collection de GameComponent appartenant au jeu.

· GameComponent  : C'est la classe de base pour tous les composants d'un jeu.
 
Cette classe contient 2 méthodes publiques qui nous intéressent : Draw et Update, ayant la même utilité que celles de la classe Game. Cette classe contient aussi une propriété Game, permettant d'accéder à la classe Game associée à notre GameComponent.
· GraphicsComponent  : C'est le composant qui gère la configuration et la gestion de notre device graphique
· GraphicsDevice : Le device graphique
· Vector2D  : Un vecteur a 2 composantes
· Texture2D  : son nom est assez explicite : Une texture 2D
· SpriteBatch  : C'est la classe qui permet le rendu de nos textures

Le squelette de notre classe principale

Comme dit plus haut, la classe principale de tout jeu doit hériter de la classe Game. Il faut ensuite lui attribuer une instance de la classe GraphicsComponent qui s'occupe, entre autres, de la gestion de notre carte graphique.

Note : contrairement au Managed DirectX, il n'y a pas besoin de configurer notre device graphique. On instancie la classe GraphicsComponent, et le Framework s'occupe du reste.


Une fois le GraphicsComponent instancié, il suffit de l'ajouter à la collection GameComponents de notre classe héritant de Game et... C'est tout !


private GraphicsComponent graphics; // Le component qui gère la partie graphique
public TestScrollingBackground()

   InitializeComponent();
}

privatevoid InitializeComponent()

   this .graphics = new GraphicsComponent (); 
   this .graphics.BackBufferWidth = 800; // Largeur du BackBuffer 
   this .graphics.BackBufferHeight = 600; // Hauteur du BackBuffer 
   
this .GameComponents.Add(graphics);
}


La méthode OnStarting de la classe Game est appelée au démarrage de jeu, avant que la première image ne soit dessinée à l'écran. C'est dans cette méthode que nous allons appeler le chargement de nos ressources. Nous en profiterons aussi pour nous inscrire aux événements DeviceReset et DeviceCreated de notre GraphicsComponent.


// Cette méthode s'execute quand le jeu
// est initialisé, mais avant l'affichage de la première image
protected override void OnStarting()
{
   // Se declenche lors de la creation d'un nouveau Device 
   this .graphics.DeviceCreated += new EventHandler (graphics_DeviceCreated);
   // Se declenche lorsqu'on switch en mode Plein écran/fenétré, ou lorsqu'on reduit la fenetre 
   this .graphics.DeviceReset += new EventHandler (graphics_DeviceReset);
   LoadResources();
}

private void graphics_DeviceReset( object sender, EventArgs e)
{
   LoadResources();
}

private void graphics_DeviceCreated( object sender, EventArgs e)
{
   LoadResources();
}

// Charge les ressources utilisées dans cette applications
// (Textures, ...)
private void LoadResources()
{
}

Et passons finalement aux méthodes Update et Draw, commentées ci-dessous.


// On met a jour toutes les données
// Cetté méthode est appelée avant la méthode Draw()
protected override void Update()
{
   
//Mise a jour des GameComponents 
   UpdateComponents();
}

// Cette méthode est appelée après la méthode Update()
// et dessine nos textures a l'ecran
protected override void Draw()
{
   
//On s'assure qu'on a un device graphique valide 
   
if (!graphics.EnsureDevice())
      
return ;

   // On repeint l'ecran en bleu 
   this .graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
   this .graphics.GraphicsDevice.BeginScene();

   //Rendu des composants 
   DrawComponents();

   graphics.GraphicsDevice.EndScene();
   graphics.GraphicsDevice.Present();
}

Voici le squelette de notre « jeu ».  Nous le compléterons à la fin de notre article afin d'y intégrer notre Background  .

Un peu plus dans le vif du sujet

Développement de notre GameComponent

Pour réaliser notre Scrolling Background, nous allons créer 2 classes :
- La classe ScrollingBackgroundComponent, qui héritera de GameComponent  et qui s'occupera de gérer et d'ordonner l'affichage de nos BackgroundSprites.
- La classe BackgroundSprite, qui est en fait un sprite appartenant à notre Background. Elle contiendra les coordonnées du sprite, sa texture et sa vitesse de défilement.

 Bien que cette classe n'héritera pas de la classe GameComponent,  nous l'agrémenterons des méthodes Draw et Update afin de garder une certaine logique dans le nommage / utilité de nos méthodes

Nous ajouterons aussi une petite énumération, qui permettra de définir dans quelle direction se fait le scrolling. (Horizontal ou Vertical).


// Direction du scroll
public enum ScrollDirection
{
   
// Scrolling horizontal 
   
Horizontal = 0,

// Scrolling vertical
Vertical
}


 

La classe ScrollingBackgroundComponent

Comme dit plus haut, cette classe va nous permettre d'afficher plusieurs sprites à la fois (représentés par la classe BackgroundSprite, décrite plus bas), chaque sprite ayant un coefficient de défilement différent, afin de créer un effet de profondeur (plus les sprites sont éloignés, plus ils défilent lentement). 

Pour pouvoir faire du rendu graphique depuis notre classe, nous devons récupérer le device graphique défini dans la classe principale. C'est une chose très facile à faire depuis un GameComponent. Pour cela, on peut se servir de sa propriété GameComponent.Game, qui nous permet d'accéder à l'instance de la classe Game associée à notre GameGomponent de cette façon :

graphicsDevice = this.Game.GameServices.GetService<IGraphicsDeviceService>().GraphicsDevice;

 Voici le squelette de base d'un GameComponent graphique :


public class MyGameComponent : GameComponent

   
private GraphicsDevice graphicsDevice;

   public
MyGameComponent()
   {
      InitializeComponent();
   }

   public override void Start() 
   { 
      
this .graphicsDevice = this .Game.GameServices.GetService< IGraphicsDeviceService >().GraphicsDevice; 
   } 

   
public override void Update() 
   { 
   
}

   public override void Draw() 
   { 
   }
}


Complétons cette classe pour réaliser notre ScrollingBackground.
Notre Background est composé de plusieurs BackgroundSprites. Pour respecter l'ordre d'affichage de nos sprites, nous allons utiliser la classe générique SortedDictionary <int, BackgroundSprite>.
De quelles autres variables aurons-nous besoin ?  La direction du scroll, la vitesse de défilement et une instance de la classe SpriteBatch pour le rendu.
Ce qui nous donne :


private SortedDictionary < int , BackgroundSprite> backgroundSprites;
private IDictionary < int , BackgroundSprite> syncLock;
private SpriteBatch spriteBatch;
private GraphicsDevice graphicsDevice;
private ScrollDirection scrollDirection;
private int scrollSpeed;

// Direction du scroll
public ScrollDirection ScrollDirection
{
   
get { return this .scrollDirection; }
   
set { this .scrollDirection = value ; }
}

// Vitesse de défilement
public int ScrollSpeed
{
   
get { return this .scrollSpeed; }
   
set { this .scrollSpeed = value ; }
}


Passons à l'initialisation de notre GameComponent. Nous avons vu un peu plus haut comment récupérer notre Device graphique. Complétons l'initialisation avec les nouvelles variables ajoutées :


public ScrollingBackground()
{
   this .backgroundSprites = new SortedDictionary < int , BackgroundSprite>();
   this .syncLock = ( IDictionary < int , BackgroundSprite>) this .backgroundSprites;
}

public override void Start()
{
   
this .graphicsDevice = this .Game.GameServices.GetService().GraphicsDevice;
   
this .spriteBatch = new SpriteBatch( this .graphicsDevice);
   
this .scrollDirection = ScrollDirection .Horizontal;
   
this .scrollSpeed = 1;
}


 

La méthode AddBackground ajoute un sprite à notre background. Elle prend en paramètres   l'ordre d'affichage (zorder) du sprite, le chemin de sa texture, ses coordonnées sous forme de 2 entiers et son coefficient de défilement. Cette méthode crée une instance de BackgroundSprite et l'ajoute à notre SortedDictionnary.

A noter que si on ajoute un sprite avec un ordre d'affichage existant déjà dans notre collection de sprites, celui-ci est ignoré.


public int AddBackground( int textureZOrder, string texturePath, int xpos, int ypos, int speedCoeff)
{
   if (! this .backgroundSprites.ContainsKey(textureZOrder))   
   {
      BackgroundSprite sprite = BackgroundSprite.CreateBackgroundSprite(
this .graphicsDevice, texturePath, xpos, ypos, speedCoeff);
      
if (sprite != null )
      {
         
lock (syncLock)
         {
            
this .backgroundSprites.Add(textureZOrder, sprite);
         }
      }
   }
   
return this .backgroundSprites.Count;
}

 

Terminons avec les méthodes Update et Draw. Celles-ci se contentent respectivement d'ordonner tous nos sprites de mettre à jour leurs coordonnées (en tenant compte de la direction du scroll et de la vitesse) et de s'afficher.

Cependant, dans la méthode Draw, nous devons préalablement appeler la méthode SpriteBatch.Begin(). Cet appel prépare le device graphique à effectuer le rendu des sprites. Nous lui fournissons le paramètre SpriteBlendMode.AlphaBlend pour lui spécifier qu'il faut activer l'AlphaBlending, ce qui signifie que l'on tient compte de la transparence des textures.

Une fois le rendu de nos sprites terminé, nous appellons la méthode SpriteBatch.End() qui rend au device son état initial.


public override void Update()
{
   
foreach (BackgroundSprite sprite in this .backgroundSprites.Values)
      sprite.Update(
this .scrollDirection, this .scrollSpeed);
}

public override void Draw()
{
   
this .spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
   foreach (BackgroundSprite sprite in this .backgroundSprites.Values)
      sprite.Draw(
this .spriteBatch, this .scrollDirection);
   this .spriteBatch.End();
}

La classe BackgroundSprite

Je disais un peu plus haut que cette classe allait contenir les coordonnées du sprite, sa texture, et sa vitesse de défilement (ou plutôt, son coefficient de défilement).  Voici les champs et propriétés déclarés :


private Vector2 position;
private Texture2D texture;
private int speedCoeff;

// La texture du BackgroundSprite
public Texture2D Texture
{
   
get { return this .texture; }
   
set { this .texture = value ; }
}

// Le coefficient de défilement du sprite
public int SpeedCoeff
{
   
get { return this .speedCoeff; }
   
set { this .speedCoeff = value ; }
}

// La coordonnée X su sprite à l'écran
public int XPos
{
   
get { return ( int ) this .position.X; }
   
set { this .position.X = value ; }
}

// La coordonnée Y du sprite a l'écran
public int YPos
{
   
get { return ( int ) this .position.Y; }
   
set { this .position.Y = value ; }
}

Nous n'allons pas exposer de constructeur public pour notre classe, mais plutôt créer une méthode statique qui se chargera d'instancier le BackgroundSprite à notre place. Cette méthode prendra comme paramètres les coordonnées du sprite, le chemin vers la texture que nous lui appliquerons, son coefficient de défilement, ainsi que le device graphique qui nous permettra  de dessiner ce sprite. Ce paramètre est nécessaire, car il est requis à la création de la texture.


public static BackgroundSprite CreateBackgroundSprite( GraphicsDevice graphicsDevice, string spritePath, int xpos, int ypos, int speedCoeff)

   
if ( File .Exists(spritePath)) 
   { 
      
return new BackgroundSprite (graphicsDevice, spritePath, xpos, ypos, speedCoeff); 
   } 
   
return null ;
}

private BackgroundSprite(GraphicsDevice graphicsDevice, string spritePath, int xpos, int ypos, int speedCoeff)
{
   
this .speedCoeff = speedCoeff;
   
this .position = new Vector2(xpos, ypos);
   
this .texture = Texture2D.FromFile(graphicsDevice, spritePath);
}

Et finalement, nous allons attaquer les 2 méthodes les plus importantes, à savoir Update et Draw.
La méthode Update va nous permettre de faire avancer ou reculer nos sprites.  Rien de très compliqué en soit. Nous allons lui fournir 2 paramètres, un premier qui indiquera la direction du défilement (horizontale ou verticale) et un second pour définir la vitesse de défilement. Cette vitesse sera multipliée par le coefficient de défilement du sprite.
.


public void Update( ScrollDirection scrollDirection, int scrollSpeed)
{
   
switch (scrollDirection)
   {
      
case ScrollDirection .Vertical:
         
this .YPos += this .speedCoeff * scrollSpeed;
         
if ( Math .Abs( this .YPos) >= this .texture.Height) this .YPos = 0;
         
break ;
      
case ScrollDirection .Horizontal:
         
this .XPos -= this .speedCoeff * scrollSpeed;
         
if ( Math .Abs( this .XPos) >= this .texture.Width) this .XPos = 0;
         
break ;
      }
}

La méthode Draw va quant à elle simplement dessiner notre sprite de façon a ce qu'il soit répété en continu, de gauche a droite ou de haut en bas. Nous lui passerons en paramètres le SpriteBatch de notre GameComponent qui s'occupera du rendu des sprites, et la direction du scroll, afin de déterminer le sens dans lequel effectuer le rendu.


public void Draw(SpriteBatch spriteBatch, ScrollDirection scrollDirection)
{
   
if (scrollDirection == ScrollDirection .Horizontal)
   {   
      spriteBatch.Draw(
this .texture, new Vector2( this .XPos - this .Texture.Width, this .YPos), Color.White);
      spriteBatch.Draw(
this .texture, new Vector2( this .XPos, this .YPos), Color.White);
      spriteBatch.Draw(
this .texture, new Vector2( this .XPos + this .Texture.Width, this .YPos), Color.White);
   }
   
else
   
{
      spriteBatch.Draw(
this .texture, new Vector2( this .XPos, this .YPos - this .texture.Height), Color.White);
      spriteBatch.Draw(
this .texture, new Vector2( this .XPos, this .YPos), Color.White);
      spriteBatch.Draw(
this .texture, new Vector2( this .XPos, this .YPos + this .texture.Height), Color.White);
   }
}


 

Intégration dans notre classe principale

Nous allons maintenant  modifier notre classe principale afin d'y intégrer notre ScrollingBackground.  Ceci est très simple. Ajoutons à notre classe un champ private ScrollingBackground background . Il suffit d'ajouter dans la méthode InitializeComponent ces 2 lignes :


// Instanciation de notre Scrolling Background
this .background = new ScrollingBackground();
// Que l'on ajoute a la collection de GameComponents
this .GameComponents.Add( this .background);

Ensuite, utilisons la méthode LoadResources pour lui ajouter ses sprites. :


// Le ciel sera le plan le plus eloigné, et aura un coefficient de défilement de 2
this .background.AddBackground(0, @"Images\Sky.png" , 0, 0, 2);
// Les arbres seront dessinés après le ciel, et auront un coefficient de defilement de 3
this .background.AddBackground(1, @"Images\Trees.png" , -500, 200, 3);

Si on compile et qu'on lance le projet, notre Background s'affiche bien... mais reste statique.
Nous allons donc simplement utiliser la méthode Update afin que l'utilisateur puisse interagir.


protected override void Update()
{
   
int direction = 0;
   
// On vérifie les touches pressées pour faire avancer l'écran 
   
// (Fleche gauche ou droite) 
   
Keys[] pressedKeys;
   
if ((pressedKeys = Keyboard.GetState().GetPressedKeys()).Length != 0)
   {
      
switch (pressedKeys[0])
      {
         
case Keys.Left:
               direction = -1;
               
break ;
  
       case Keys.Right:
               direction = 1;
               
break ;
      }
   }

   // On affecte cette direction en tant que vitesse de 
   
// défilement au background. 
    
this .background.ScrollSpeed = direction;
   UpdateComponents();
}


Si on recompile, le background réagit maintenant aux touches gauche et droite du pavé fléché.
Et voilà, notre ScrollingBackground est terminé !
Vous pouvez retrouver les sources complètes à cet endroit :  http://www.csharpfr.com/code.aspx?ID=39790

Liens Utiles

XNA Developer Center
Blog de l?équipe XNA
Télécharger XNA Game Studio Express Beta 1
Télécharger Visual C# Express (requis pour faire tourner XNA GSE 1)
Le SDK DirectX Utile pour les outils par encore intégrés dans XNA Game Studio (XACT, ...)

03 octobre 2006 21:32:32 :
Mise en forme
03 octobre 2006 22:09:38 :
Continuation de la mise en forme...
03 octobre 2006 22:38:41 :
Mise en forme
03 octobre 2006 22:43:29 :
Ajout du lien vers le code-source
02 novembre 2006 14:02:42 :
Avertissement Beta 2
14 février 2007 09:50:24 :
.
  • signaler à un administrateur
    Commentaire de izel le 25/02/2007 14:33:16

    Sympa le tuto merci pour le travail

Ajouter un commentaire

Pub



Appels d'offres

CalendriCode

Août 2008
LMMJVSD
    123
45678910
11121314151617
18192021222324
25262728293031

VS Express FR Gratuit !

VS Express en français et 100% gratuit !

Boutique

Boutique de goodies CodeS-SourceS