Astuces DotNet (Sébastien Courtois)

05/10/2010

[XNA 4] Tutoriel 3D 1 : Hello World 3D

Filed under: .NET, 3D, C# 4, Débutant, Intermediaire, XBOX 360, XNA — Étiquettes : , , , , , , , , — sebastiencourtois @ 16:31

Après une série de tutoriels sur la programmation 2D en XNA, nous allons passer à une partie plus intéressante et surtout plus complexe … la 3D. Qui n’a pas rêvé de recréer soi-même un Crysis ou encore un Gran Tourismo.

new-crysis-dx10-screenshot-20061110001326035 gran_turismo_5
Copie d’écran de Crysis Copie d’écran de Gran Tourismo 5

Il faut bien se rendre compte que ces jeux, magnifique graphiquement, ont été réalisés par des équipes composés de dizaines de programmeurs, artistes 2D/3D  qui ont travaillé pendant des mois pour arriver à un tel réalisme. Même si être ambitieux/optimiste  aide dans ce métier, il faut se rendre à l’évidence que vous ne pourrez pas seul et avant de nombreuses années arriver à un tel résultat.

Toutefois, si cela peut vous consoler, nous allons utiliser des outils/concepts proches de ce que les développeurs de ces jeux utilisent et nous seront confrontés (à notre niveau) aux mêmes types de problèmes inhérent à la création de jeu vidéo.

  • Un peu de vocabulaire/concept

Le monde de la 3d est un monde qui parait assez obscur et mathématiques avec son propre vocabulaire et ses propres concepts. Toutefois, ceux-ci peuvent être rattachés à des éléments du quotidien (on vit dans un univers en 3d, non :)).

Un Vertex (pluriel : vertices) : Un vertex est un point dans un univers 3d. C’est l’élément de base dans la construction de formes 3D primitives (lignes, triangle). Il est généralement décrit par sa position dans l’univers 3d ainsi que sa couleur, normals et d’autres informations nécessaires au rendu 3d que nous verrons plus tard.

Un ligne : Primitive 3D composé de deux vertices

Un triangle : Primitive 3d composé de 3 vertices. C’est la primitive la plus évolué qu’une carte graphique peut dessiner. Les modèles 3d que vous pouvez voir dans les jeux vidéo ne sont généralement qu’une accumulation de triangles mis cote à cote.

eames_wireframe Remarque : Les cartes graphiques peuvent aussi dessiner des primitives à 4 vertices mais cette fonctionnalité n’est pas disponible dans XNA 4.

Graphics Device : Ce terme représente l’accès à la carte graphique. Toute les opérations graphiques passeront par des appels au graphics Device (notamment la partie de rendu 3d).

  • Premier projet 3D en XNA

Dans ce tutoriel, nous allons créer un triangle.

Pour commencer, il faut créer un nouveau projet XNA (Windows Game). XNA nous crée déjà le graphics Device nécessaire à la communication avec la carte graphique ainsi que les outils pour la gestion du contenu (ContentManager).

Pour une scène 3d, il est nécessaire d’avoir au minimum 2 choses :

    • Un modèle 3D

Dans notre exemple, notre modèle 3D va être relativement simple car il s’agira d’un triangle. Pour cela, il nous suffit de créer un tableau de 3 vertices. Chaque vertex aura une position et une couleur. Dans notre classe Game, nous allons créer un tableau pour contenir les vertices ainsi qu’une méthode créant les données du triangle.

VertexPositionColor[] vertices;
//Création du triangle
private void CreateTriangle()
{
   this.vertices = new VertexPositionColor[]
   {
       new VertexPositionColor( new Vector3(-1,-1,0), Color.Red),
       new VertexPositionColor( new Vector3(0,1,0), Color.Green),
       new VertexPositionColor( new Vector3(1,-1,0), Color.Blue),
   };
}

Pour contenir les données d’un vertex, XNA fournit une série de classe de base permettant de stocker correctement ces données. Dans notre cas, nous souhaitons définir seulement la position et la couleur des points. Nous utiliserons donc la structure VertexPositionColor prenant en paramètre une position dans un univers 3D (sous la forme d’une structure Vector3) et une couleur. Nous stockons le tout dans un tableau défini au préalable dans notre classe Game.

Remarque : D’autres types de structures de stockage de vertices existent dans XNA (VertexPositionColor / VertexPositionColorTexture / VertexPositionNormalTexture / VertexPositionTexture). Vous pouvez créer votre propre classe en implémentant l’interface IVertexType. Nous verrons dans un  prochain tutoriel la création de notre propre structure de stockage.

    • Une visualisation (ou caméra)

Une fois le modèle 3D défini, il est nécessaire d’indiquer comment on souhaite le voir. Pour cela, il est nécessaire de fournir la position de l’observateur, l’endroit qu’il regarde et d’autres informations comme l’angle de vision. Ces informations seront ensuite stockées sous forme de matrices mathématiques qui serviront à la carte graphique pour le rendu 3D.

Matrix View;
Matrix Projection;
Matrix World;

Vector3 CameraPosition = new Vector3(0.0f, 0.0f, 5.0f);
Vector3 CameraTarget = Vector3.Zero;
Vector3 CameraUp = Vector3.Up;

//Création de la caméra
private void CreateCamera()
{
    View = Matrix.CreateLookAt(CameraPosition, CameraTarget, CameraUp);
    Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, this.GraphicsDevice.Viewport.Width / this.GraphicsDevice.Viewport.Height, 0.01f, 1000.0f);
    World = Matrix.Identity;
}

La première matrice est la matrice View (dite de “Visualisation”). Elle sert à positionner la caméra et la façon dont celle-ci regarde la scène. On utilise une méthode d’aide fourni par XNA (CreateLookAt) qui prend en premier paramètre la position de l’utilisateur dans l’univers 3D (sous la forme d’un Vector3). Le paramètre suivant est l’endroit que regarde l’utilisateur. Le troisième paramètre indique “l’orientation” de la caméra. Dans notre exemple, nous souhaitons que le dessus de la camera soit orienté vers le haut (coordonnées {0,1,0} ce qui équivaut au Vector3.Up).

La deuxième matrice est la matrice de projection. Celle-ci indique les caractéristique de la caméra (lentille, distance focale). Une méthode d’aide XNA permet de créer cette matrice facilement (CreatePerspectiveFieldOfView). Le premier paramètre est l’angle d’ouverture (ou angle de vision) de la caméra. Par exemple, un humain a un angle de vision allant de 1° (angle d’attention) jusqu’à 180 ° (angle de perception).Cela veut dire qu’il peut, sans bouger la tête voir tout ce qui l’entoure à 180 °. Arbitrairement, nous allons décider d’utiliser un angle de vision de 45° ou PI/4 (car les angles sont décrits en radians en XNA). Le paramètre suivant est le ratio d’aspect. Cela indique le format de la caméra (16/9 comme au cinéma, 4/3). Cela correspond tout simplement à la largeur de la zone visible divisé par la hauteur. Les deux derniers paramètres correspondent à la distance minimale et maximale d’affichage. Ainsi tous les objets trop proche ou trop loin de la caméra ne seront pas affichés.

La troisième matrice nous permettra de placer les objets dans l’univers 3d. Pour l’instant, nous lui assignons la valeur de base (matrice identité).

    • Méthode d’initialisation

Nous avons maintenant tous les éléments nécessaire pour réaliser un scène 3d. Nous allons maintenant appelé les deux méthodes décrites plus haut dans la méthode Initialize().

protected override void Initialize()
{
   this.CreateTriangle();
   this.CreateCamera();
   base.Initialize();
}
  • Affichage d’un triangle

Afin de réaliser l’affichage, nous allons utiliser un shader. Un shader est un programme créé spécifiquement pour être exécuté sur la carte graphique. En effet, la carte graphique permet la réalisation de calcul mathématique très rapidement. La création de shader sera l’objet de plusieurs tutoriels mais, dans ce premier article sur la 3d, nous allons utilisé celui fournit par XNA : BasicEffect.

BasicEffect effect;

protected override void LoadContent()
{
  spriteBatch = new SpriteBatch(GraphicsDevice);
  this.effect = new BasicEffect(this.GraphicsDevice);
}

BasicEffect se crée en fournissant uniquement le graphicsDevice et il sera utilisé lors de la partie affichage (méthode Draw).

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

    effect.View = View;
    effect.Projection = Projection;
    effect.World = World;
    effect.VertexColorEnabled = true;

    foreach(EffectPass pass in effect.CurrentTechnique.Passes)
    {
        pass.Apply();
        this.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length / 3);
    }

    base.Draw(gameTime);
}

Première étape dans la méthode Draw. Nettoyer l’écran avec une couleur de fond de la même façon que pour la 2D. On fournit ensuite les trois matrices de projections au shader BasicEffect. On active aussi le booléen VertexColorEnabled pour indiquer que l’on souhaite que les couleurs définis sur les vertices soient prises en compte. On utilise ensuite la propriété CurrentTechnique pour utiliser les différentes passes du shaders (le fonctionnement exacte des shaders sera expliqué dans un autre tutoriel et sa compréhension n’est pas indispensable pour celui-ci).

La méthode la plus importante de ce morceau de code est le DrawUserPrimitivers qui va prendre en paramètre le type de primitive. Il s’agit de la façon dont nous allons associer les vertices.

Initialising_Article_triangles

Dans notre exemple, nous utiliserons TriangleList. Cette primitive prend 3 vertex pour en faire un triangle puis les 3 vertex suivants pour un autre triangle et ainsi de suite.

Le deuxième paramètre de DrawUserPrimitives est le tableau de vertices proprement dit. Le paramètre suivant est l’index de départ du tableau de vertices. Dans notre cas, on souhaite utiliser tous les vertices donc on indique que le premier vertex est en position 0. Le dernier paramètre indiquent le nombre de primitives (ici triangle) que l’on souhaite dessiner. On prend le nombre total de vertices de notre tableau (3) et on divise par le nombre de vertex nécessaire pour faire un triangle (3). On indique que l’on souhaite dessiner un seul triangle.

Si vous lancer le projet, vous devriez obtenir l’écran suivant :

xna_tuto3d1_1

On remarque que le fond est bleu ce qui correspond à la couleur d’effacement au début de notre méthode Draw (Clear(Color.CornFlowerBlue)). Le triangle est visible mais on remarque que les couleurs, correctes aux sommets, fusionnent entre elles en s’écartant des sommets. Il s’agit du comportement de base de DirectX. Si l’on souhaite faire un triangle uni, il faut que les sommets aient la même couleur.

  • Un peu de mouvement

Nous allons introduire une rotation sur ce triangle afin de prouver que nous sommes bien dans un univers 3D. Pour cela, on va rajouter une variable de rotation que nous allons incrémenter dans la méthode Update.

float RotateZ = 0.0f;
protected override void Update(GameTime gameTime)
{
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
        this.Exit();

    RotateZ += MathHelper.ToRadians(2.0f);

    base.Update(gameTime);
}

Nous incrémentons donc la variable de 2° par appel à la méthode Update (60 x par secondes par défaut sur XNA). Il est à noter que la valeur est toujours à stocker en Radians. MathHelper fournit des méthodes d’aides pour traduire les angles de degré en radians et inversement.

Une fois la variable modifié, il suffit de modifier la matrice World qui est chargé de placer le triangle dans l’espace.

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

    effect.View = View;
    effect.Projection = Projection;
    effect.World = World * Matrix.CreateRotationY(RotateZ);
    effect.VertexColorEnabled = true;

    foreach(EffectPass pass in effect.CurrentTechnique.Passes)
    {
        pass.Apply();
        this.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length / 3);
    }

    base.Draw(gameTime);
}

Il suffit de multiplier la matrice World avec une matrice de rotation créé à partir de l’angle RotateZ pour réaliser cette effet. Si vous relancer l’application le triangle tourne…

  • Mon triangle disparait puis réapparait sans arrêt.

Lorsque vous lancez l’application le triangle commence à tourner mais il disparait un moment au bout d’un demi-tour puis réapparait. Cela est dû à une optimisation de la carte graphique nommé Backface Culling. En effet, les cartes graphiques n’affiche pas les faces (triangles) ne leur faisant pas face. Vous trouverez une explication plus complète sur le fonctionnement  ici. Il est possible de désactiver cette optimisation en modifiant la propriété RasterisationState du graphicsDevice. Cela se réalise généralement dans la méthode Initialize().

this.GraphicsDevice.RasterizerState = new RasterizerState()
{
   CullMode = CullMode.None,
   FillMode = FillMode.Solid
};

Remarque : Vous pouvez aussi régler par la propriété FillMode si vous souhaitez une vision des objets “solides” ou en fil de fer.

Vous pouvez remarquer que CullMode à 3 valeurs possibles : CullClockwiseFace ,CullCounterClockwiseFace, None. Par défaut, le CullMode est à CullClockwise ce qui veut dire que les vertex doivent être définis dans l’ordre des aiguilles d’une montre pour que la face soit visible.

TriangleAll
Représentation d’un triangle dont les vertices sont dans le sens des aiguilles d’une montre (tiré d’un tutoriel anglais XNA : http://www.toymaker.info/Games/XNA/html/xna_simple_triangle.html).

Lorsque le triangle a fait son premier demi-tour, les vertex passent dans le sens inverses. N’étant plus dans le sens des aiguilles d’une montre, le triangle n’est plus affiché.

Une autre solution, qui permet de conserver le Backface Culling, serait de créer un deuxième triangle avec les vertices dans l’ordre inverse. Ainsi, si on modifie le tableau de vertices comme suit  :

//Création du triangle
private void CreateTriangle()
{
    this.vertices = new VertexPositionColor[]
    {
        //Triangle 1
        new VertexPositionColor( new Vector3(-1,-1,0), Color.Red),
        new VertexPositionColor( new Vector3(0,1,0), Color.Green),
        new VertexPositionColor( new Vector3(1,-1,0), Color.Blue),
        //Triangle 2
        new VertexPositionColor( new Vector3(1,-1,0), Color.Blue),
        new VertexPositionColor( new Vector3(0,1,0), Color.Green),
        new VertexPositionColor( new Vector3(-1,-1,0), Color.Red),

    };
}

On obtient un triangle (en fait deux triangles qui s’interchange) qui tourne correctement.

  • Conclusion

Après ce tutoriel un peu long et complexe, je pense que vous aurez compris que la route est longue avant de pouvoir faire une jeu aussi techniquement abouti que les jeux présentés plus haut. Vous savez maintenant faire un triangle ce qui est la base de toutes les applications 3D. Dans les prochains tutoriels, nous verrons comment afficher un cube, le texturer puis se déplacer dans un univers 3d. Nous verrons aussi quelques astuces pour optimiser l’affichage.

N’hésitez pas à donner vos commentaires et poser vos questions sur ce tutoriel.

Lien vers la solution

20/09/2010

[XNA 4] Tutoriel 5 : Création d’un composant XNA : Compteur d’images affichées par secondes

Filed under: Non classé — sebastiencourtois @ 16:53

EDIT 22/09/2010 : Une correction a été faite pour le calcul des FPS.

Nous allons nous intéresser aux composants réutilisables XNA. XNA fournit un Framework d’extensibilité permettant de créer et d’intégrer facilement un composant à une application XNA. Durant cet article, nous allons intégrer un compteur d’images affichées par seconde sur l’application quasiment sans toucher le code original.

Projet de départ

  • Architecture des composants XNA

xna_tuto5_2

L’architecture “composant” de XNA comprend 3 Interfaces :

  1. IGameComponent : Interface de base des composants XNA. Contient une méthode d’initialisation (Initialize)
  2. IUpdateable : Interface pour les composants non graphique. Contient une méthode Update similaire à celle de Game vu dans les tutoriels précédents.
  3. IDrawable : Interface pour les composants graphiques. Contient une méthode Draw similaire à celle de Game.

Deux classes implémentent ces interfaces afin de fournir des composants de base :

  1. GameComponent : Composant de base non graphique implémentant IGameComponent et IUpdateable. Il contient donc une méthode d’initialisation et une méthode de mise à jour.
  2. DrawableGameComponent : Composant de base graphique implémentant GameComponent et IDrawable. Il contient donc une méthode d’initialisation, une méthode de mise à jour et une méthode d’affichage.
  • Création d’un composant graphique : Compteur de FPS (Frames Per Second)

Un composant XNA n’est rien d’autre qu’une classe dérivant de GameComponent ou DrawableGameComponent. Pour commercer, il faut créer un nouvel élément.

xna_tuto5_1

Il existe un templace “GameComponent” pour créer des composants. Vous pouvez l’utiliser ou créer une nouvelle classe vide. Nous appellerons ce composant CompteurFPS. Nous devons ensuite modifier la classe afin qu’elle hérite de DrawableGameComponent. En effet, on souhaite calculer et afficher le nombre d’images par secondes. Le squelette de code pour un composant graphique est le suivant.

public class CompteurFPS : Microsoft.Xna.Framework.DrawableGameComponent
{
    public CompteurFPS(Game game) : base(game)
    {
        // TODO : Code
    }

    public override void Initialize()
    {
        // TODO : Code
        base.Initialize();
    }

    public override void Update(GameTime gameTime)
    {
        // TODO : Code
        base.Update(gameTime);
    }

    public override void Draw(GameTime gameTime)
    {
        // TODO : Code
        base.Draw(gameTime);
    }
}

Pour ce composant, nous avons besoin d’un certain nombre propriétés.

//Composant de dessin
public SpriteBatch spriteBatch;
//Police de caractêres
public SpriteFont spriteFont;
//Nombre d'images par secondes
public double FPS = 0.0f;

La propriété spriteBatch est l’utilitaire qui va nous permettre de dessiner à l’écran (voir Tutoriel 2). La propriété spriteFont est la font que nous allons utiliser pour afficher le texte à l’écran (voir Tutoriel 3).La propriété FPS est une variable pour stocker le nombre d’images par seconde courant.

On va tout d’abord initialiser/charger les deux premières propriétés.

public override void Initialize()
{
    this.spriteBatch = new SpriteBatch(this.Game.GraphicsDevice);
    base.Initialize();
}

protected override void LoadContent()
{
    this.spriteFont = this.Game.Content.Load<SpriteFont>("MyFont");           
    base.LoadContent();
}

On calcule le nombre d’images affichés pour une secondes puis on affiche le résultat sous forme de texte que l’on affichage en haut à droite de l’écran.

public override void Draw(GameTime gameTime)
{
     this.FPS = 1000.0d / gameTime.ElapsedGameTime.TotalMilliseconds; 
    //Formatage de la chaine
    string texte = string.Format("{0:00.00} images / sec", this.FPS);
    //Calcul de la taille de la chaine pour la police de caractère choisie
    Vector2 taille = this.spriteFont.MeasureString(texte);
    //Affichage de la chaine
    this.spriteBatch.Begin();
    this.spriteBatch.DrawString(this.spriteFont, texte, new Vector2(this.GraphicsDevice.Viewport.Width - taille.X, 5), Color.Green);
    this.spriteBatch.End();
    base.Draw(gameTime);
}

Remarque : Le calcul du nombre d’images par secondes aurait pu être fait dans la méthode Draw.Toutefois afin de séparer ce qui est traitement non graphique de ce qui est graphique, j’ai décidé de mettre ce code dans la méthode Update.

Remarque 2 : J’utilise ici la méthode MeasureString de la propriété spritefont qui me permet de calculer la taille du texte que je souhaite afficher  en fonction de la police de caractère sélectionné. Cela me permet de définir où je dois positionner la chaine de caractères pour qu’elle soit visible entièrement dans le coin haut droit de l’écran.

  • Ajout du composant à l’application

Une fois un composant créé, il suffit de l’ajouter à la classe Game au travers de la liste Component.

fps = new CompteurFPS(this);
this.Components.Add(fps);

On se retrouve avec le type d’écran suivant :

xna_tuto5_3

  • Quand les méthodes des composants sont-elles appelées ?

Une fois ajoutés dans la liste des composants, chacun des méthodes d’un cycle XNA (Initialize,LoadContent,Update,Draw…) sont appelées par la classe Game. Game appelle tout d’abord sa propre méthode Initialize puis appelle celle de ses composants.  Chacun des  méthodes Initialize  des composants appellent leurs propres méthodes LoadContent. Une fois ce processus terminé, la classe Game appelle sa propre méthode LoadContent et lance la boucle de jeu.

La boucle de jeu appelle la méthode Update de Game puis celle de ses composants. Il en va de même pour la méthode Draw (celle de Game étant appelée en premier).

Lorsque plusieurs composants sont intégrés, les propriétés DrawOrder et UpdateOrder permettent de définir l’ordre d’appels des méthodes des composants. Le nombre le plus faible est appelé en premier. Si tous les composants sont au même niveau, l’ordre d’ajout dans la liste des composants est l’ordre par défaut (le premier composant ajouté sera appelé en premier).

Les propriétés Enable et Visible permettent d’activer ou désactiver les composants.Ceux-ci sont à true par défaut (activé).

  • Bonus : Mon FPS est bloqué à 60

Par défaut, XNA bride la vitesse d’affichage à 60 images par secondes (30 sur Windows Phone). Cela permet de garantir une bonne fluidité tout en gardant un temps fixe pour tous les calculs annexes (physiques par exemple).

Si vous souhaitez désactiver cette fonctionnalité, il suffit d’ajouter au constructeur de Game les lignes suivantes :

this.IsFixedTimeStep = false;
this.graphics.SynchronizeWithVerticalRetrace = false;
this.graphics.ApplyChanges();

Vous aurez alors le FPS réel de votre application.

xna_tuto5_4

  • Conclusion

Vous savez maintenant créer des composants réutilisables pour XNA 4. De nombreux composants sont disponibles sur Internet et sur le site officiel XNA.

Code Source du tutoriel

18/09/2010

[XNA 4] Tutoriel 4 : Gestion des entrées : Clavier, Souris, Manette XBOX 360, Windows Phone Touch

Filed under: .NET, C# 4, Débutant, Jeux vidéos, XBOX 360, XNA — Étiquettes : , , , , , , , , , , , — sebastiencourtois @ 13:50

Jusqu’a présent, nous avons fait des applications démos tournant automatiquement. Même si certaines démos commerciales ou films d’animations peuvent être agréables à regarder, un jeu vidéo est intéressant grâce à l’interaction joueur/univers du jeu.

Code Source pour commencer ce tutoriel

Au travers de ce tutoriel, nous allons voir quatre outils d’interactions avec le joueur pour déplacer le tank du tutoriel précédent :

    • Clavier (PC & XBOX 360)
    • Souris (PC uniquement)
    • Touch (Windows Phone uniquement)
    • Manette XBOX 360 (PC / XBOX 360)

Remarques : Il est possible de brancher un clavier USB à une XBOX 360 et récupérer les entrées du clavier comme s’il s’agissait d’un pc. Pour le touch, XNA ne prend pas en charge que le toucher des écrans multitouch PC vendus dans le commerce.

Comme tout les codes de mise à jour du jeu non graphique, la gestion des interactions avec l’utilisateur se réalise dans la méthode Update() de la boucle de jeu.

  • Gestion du clavier

La première chose à faire est de récupérer l’état du clavier à un instant T. Cela se réalise par la méthode statique GetState() de la classe Keyboard

KeyboardState kbState = Keyboard.GetState();

Remarque : GetState() peut avoir un paramètre PlayerIndex (allant de Player.One à Player.Four). Cela est utilisé pour identifier la manette de l’utilisateur utilisant un Chatpad (clavier se branchant directement sur la manette). Pour le PC, on utilisera la version sans paramètre.

On récupère un structure KeyboardState contenant les informations sur les touches pressées ou non. KeyboardState contient 3 méthodes :

    • GetPressedKeys() : Méthode renvoyant un tableau des touches enfoncées (enum Keys)
    • IsKeyUp(Keys key) : Indique si la touche du clavier passée en paramètre est relaché (true si relâchée)
    • IsKeyDown(Keys key) : Indique si la touche du clavier passée en paramètre est enfoncée (true si enfoncée)

L’énumération Keys est une énumération de l’ensemble des touches du clavier (Keys.Up correspond à la flèche du haut, Keys.B à la touche B…). Il est possible d’utiliser les valeurs numériques correspondant à ces alias.

Partant des principes ci-dessus, on souhaite déplacer le tank en utilisant les touches du clavier et doubler la vitesse si, en plus on appuie sur la touche majuscule. On divisera par deux la vitesse si on appuie sur la touche control pendant le déplacement.

//Déplacement remise à 0 du déplacement
TankVelocity = Vector2.Zero;
//Vitesse maximun du tank
int TankMaximunSpeed = 2;

//Gestion du clavier
KeyboardState kbState = Keyboard.GetState();

if (kbState.IsKeyDown(Keys.Up))
    TankVelocity.Y -= TankMaximunSpeed;
if (kbState.IsKeyDown(Keys.Down))
    TankVelocity.Y += TankMaximunSpeed;
if (kbState.IsKeyDown(Keys.Left))
    TankVelocity.X -= TankMaximunSpeed;
if (kbState.IsKeyDown(Keys.Right))
    TankVelocity.X += TankMaximunSpeed;
if (kbState.IsKeyDown(Keys.LeftShift) || kbState.IsKeyDown(Keys.RightShift))
    TankVelocity *= 2;
if (kbState.IsKeyDown(Keys.LeftControl) || kbState.IsKeyDown(Keys.RightControl))
    TankVelocity /= 2;

La variable TankMaximunSpeed est une constante définie comme la vitesse maximum du tank dans une direction donnée. TankVelocity décrit le vecteur de déplacement du tank pour l’itération courante.

  • Gestion de la souris

Comme nous l’avons fait pour le clavier, la première tâche pour récupérer l’état de la souris.

//Gestion de la souris
MouseState mouseState = Mouse.GetState();

La structure MouseState contient trois propriétés importantes :

    • Propriétés *Button : Permet de choisir un des boutons de la souris et de savoir s’il est pressé ou non (Enumération ButtonState).
    • Propriétés X, Y : Indique la position (X,Y) du curseur sur l’écran de l’ordinateur (la position en dehors de la fenêtre de jeu est transmise).
    • Propriété ScrollWheelValue : Indique le nombre d’utilisation du scroll depuis le début du jeu.(positif si scroll vers l’avant).

Si l’on souhaite déplacer le tank vers le clic de l’utilisateur, on utilisera le code suivant (le bouton gauche de la souris doit rester enfoncé pour faire avancer le tank).

if (mouseState.LeftButton == ButtonState.Pressed)
{
    Vector2 PositionMouse = new Vector2(mouseState.X, mouseState.Y);
    TankVelocity = PositionMouse - TankPosition;
    TankVelocity.Normalize();
    TankVelocity *= TankMaximunSpeed;
}

Par défaut, XNA ne gère pas les doubles clics ou les clics longs. C’est au développeur de gérer ces cas. Nous verrons, dans un prochain post, comment réaliser ces opérations particulières.

  • Gestion du Touch Windows Phone

Pour utiliser le Touch, il faut tout d’abord vérifier si le matériel peut gérer cette capacité.

//Gestion du touch
TouchPanelCapabilities touchCap = TouchPanel.GetCapabilities();
if (touchCap.IsConnected)
{
   [...]
}

Un fois la compatibilité matérielle vérifié, on récupère l’état de l’interface tactile.

TouchCollection touches = TouchPanel.GetState();

La struncture TouchCollection est un tableau de TouchLocation contenant chacune un identifiant, une position et un état TouchLocationState parmi les 4  états suivants :

    • Pressed : Un nouveau point de pression est disponible
    • Released : Un point pression existant a disparu (l’utilisateur a retiré son doigt de l’écran
    • Moved : Un point de pression existant lors de l’itération précédente est toujours là et la position a été mise à jour
    • Invalid : Problème de reconnaissance des points de pressions (souvent un nouveau point de pression qui est pris pour un point existant).

Si l’on souhaite réaliser la même chose que l’exemple précédent avec la souris, on utilisera le code suivant : (on ne prend que le premier contact tactile pour simuler un curseur de souris).

//Gestion du touch
TouchPanelCapabilities touchCap = TouchPanel.GetCapabilities();
if (touchCap.IsConnected)
{
   TouchCollection touches = TouchPanel.GetState();
   if (touches.Count >= 1)
   {
      Vector2 PositionTouch = touches[0].Position;
      TankVelocity = PositionTouch - TankPosition;
      TankVelocity.Normalize();
      TankVelocity *= TankMaximunSpeed;
   }
}
XNA_WindowsPhoneInput 

L’utilisation de l’interface Touch du Windows Phone sera décrit plus en détail dans un prochain post (notamment l’utilisation de gestures et du multitouch).

  • Gestion de la manette XBOX 360

La manette XBOX 360 est accessible au travers de l’API XNA. La  première étape consiste à voir si une ou plusieurs manettes sont connectées puis de récupérer son état. Dans notre exemple, nous ferons cela juste pour le premier joueur.

GamePadState gamepadState = GamePad.GetState(PlayerIndex.One);
if (gamepadState.IsConnected)
{
    [...]
}

La manette XBOX 360 n’est pas la seule utilisable avec XNA. Un certain nombre d’autres manettes peuvent être vu par XNA (sur PC uniquement). Afin de vérifier les éléments disponibles sur ces manettes, la classe GamepadCapabilities permet d’indiquer la présence ou non des joysticks/boutons.

GamePadCapabilities gamepadCaps = GamePad.GetCapabilities(PlayerIndex.One);

if (gamepadCaps.HasLeftXThumbStick && gamepadCaps.HasLeftYThumbStick)
    TankVelocity = gamepadState.ThumbSticks.Left * TankMaximunSpeed;

else if (gamepadCaps.HasLeftXThumbStick && gamepadCaps.HasLeftYThumbStick)
    TankVelocity = gamepadState.ThumbSticks.Right * TankMaximunSpeed;

else if (gamepadCaps.HasDPadUpButton && gamepadCaps.HasDPadLeftButton && gamepadCaps.HasDPadRightButton && gamepadCaps.HasDPadDownButton)
{
    if (gamepadState.IsButtonDown(Buttons.DPadUp))
       TankVelocity.Y -= TankMaximunSpeed;
    if (gamepadState.IsButtonDown(Buttons.DPadDown))
       TankVelocity.Y += TankMaximunSpeed;
    if (gamepadState.IsButtonDown(Buttons.DPadLeft))
       TankVelocity.X -= TankMaximunSpeed;
    if (gamepadState.IsButtonDown(Buttons.DPadRight))
       TankVelocity.X += TankMaximunSpeed;
}

Pour notre exemple, nous utilisons le joystick gauche s’il existe sinon nous utilisons le joystick droit s’il existe sinon nous utilisons la croix directionnelle (si elle existe). Vous pouvez remarquer que le GamepadState a deux méthodes IsButtonDown/IsButtonUp qui fonctionne de la même façon que le clavier.

Les joysticks (Thumbstick) ont un fonctionnement légèrement différent.La propriété ThumbSticks regroupe l’ensemble des joysticks (2 joysticks au maximum : Left et Right). Chaque joystick est un Vector2 indiquant le décalage  (X,Y) par rapport au point central. Le point central est (0,0) et les “extrémités” étant –1 et 1. On obtient donc une valeur comprise entre –1 et 1 pour chacun des axes.

Certaines manettes (la manette 360 notamment) sont équipées de moteurs pour faire vibrer celle-ci lors de certains évènements. Pour cela, on utilisera la méthode statique SetVibration de la classe GamePad.

if (gamepadCaps.HasLeftVibrationMotor && gamepadCaps.HasRightVibrationMotor)
    GamePad.SetVibration(PlayerIndex.One, 0.7, 0.25);

Les valeurs possibles pour chacun des moteurs vont de 0 (aucune vibration) à 1 (vibration maximum).

Un utilitaire disponible sur le site XNA montre plus clairement la gestion de la manette.

  • Conclusion

Vous pouvez maintenant récupérer les informations provenant de l’utilisateur et modifier l’environnement du jeu en conséquence. Cela conclut la première partie des tutoriels 2D de base sur XNA. Les prochains tutoriels auront pour thème les animations, le son, l’IA, les effets 2D et la gestion de contenu avancé (Content Pipeline) pour arriver sur la 3D.

Code Source de ce tutoriel

  • Exercices pour aller plus loin
  1. Changer le système de déplacement clavier/manette afin que les touches gauche/droite fasse tourner le tank et que les touches haut/bas fasse avancer/reculer le tank en fonction de son orientation.
  2. Faire tourner le tank dans la direction du clic de souris (ou du touch) pendant le déplacement (l’avant du tank doit toujours être “face” à sa destination).

La solution sera fournis prochainement.

17/09/2010

[XNA 4] Tutoriel 3 : Affichage de texte

Filed under: .NET, C# 4, Débutant, Jeux vidéos, XBOX 360, XNA — Étiquettes : , , , , , , , — sebastiencourtois @ 13:39

Après avoir appris à afficher et déplacer des images, nous allons apprendre à écrire du texte à l’écran. Pour ce tutoriel, nous partirons de ce projet (Solution des exercices du Tutoriel 2).

  • Création d’une police de caractère (font)

Afin de pouvoir afficher du texte, il faut indiquer quel police de caractères (aussi appelé font), on souhaite utiliser. Pour cela, on ajoute un nouvel élément au projet Content (Les fonts sont généralement stockés dans le projet Content comme l’ensemble des ressources XNA).

xna_tuto3_1

On sélectionne un élément de type “SpriteFont” en indiquant un nom et on clique sur OK.

xna_tuto3_2

A l’instar des images, les fonts ont aussi un assetName visible et modifiable par la fenêtre Propriétés. 

Si l’on ouvre le fichier contenant la police de caractères, on obtient le fichier XML suivant :

<?xml version="1.0" encoding="utf-8"?>
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
  <Asset Type="Graphics:FontDescription">
    <FontName>Kootenay</FontName>
    <Size>14</Size>
    <Spacing>0</Spacing>
    <UseKerning>true</UseKerning>
    <Style>Regular</Style>
    <CharacterRegions>
      <CharacterRegion>
        <Start>32</Start>
        <End>126</End>
      </CharacterRegion>
    </CharacterRegions>
  </Asset>
</XnaContent>

La balise FontName permet de définir le nom de la police utilisée. Les fonts disponibles sont uniquement des fonts de type TrueType (Liste). La communauté a créé des générateur de font personnalisé (Exemple : FontBuilder). Les plus utilisés sont souvent Arial ou Verdana.

La balise Size indique la taille du texte comme cela se fait dans les suites bureautiques.

La balise Spacing permet d’indiquer si l’on souhaite un espace entre les lettres (valeur en pixel).

La balise UseKerning permet de définir si l’on autorise le kerning. Pour faire simple, le kerning est le fait que deux lettres puissent “se superposer” (voir l’article et cette image qui décrit le kerning).

La balise Style indique si l’on souhaite mettre en gras/italique. Valeurs possibles : Regular, Bold, Italic ou Bold Italic.

La balise CharacterRegions défini les plages de valeurs ascii utilisable par la police (ici de 32 à 126 ce qui correspond au caractères de ‘ ‘ à ‘~’ en incluant les lettres en minuscules et majuscules, les chiffres ainsi que certains signes de ponctuations (cf tableau ASCII). Nous verrons qu’il est possible de rajouter d’autres plages.

  • Chargement de la police de caractères

Une fois défini, il est nécessaire de charger la police de caractères lors du lancement de l’application. Pour cela, on utilise le même principe que pour les textures.

SpriteFont textFont;

protected override void LoadContent()
{
    [...]
    this.textFont = Content.Load<SpriteFont>("MyFont");
}

Rappel : On utilise toujours le AssetName du fichier avec la méthode Load(). On doit donner aussi le type de sortie (SpriteFont pour les polices de caractères).

  • Affichage de texte

L’affichage du texte se déroule dans la méthode Draw() en utilisant le spriteBatch comme pour les textures.

Pour l’exemple, nous allons réutiliser l’exemple du chapitre précédent pour afficher en temps réel les coordonnées du tank, son angle de rotation ainsi que l’angle de rotation de son canon.

protected override void Draw(GameTime gameTime)
{
   GraphicsDevice.Clear(Color.CornflowerBlue);
   spriteBatch.Begin();

   string text1 = string.Format("Position du tank : X = {0}   Y = {1}",this.TankPosition.X,this.TankPosition.Y);
   spriteBatch.DrawString(this.textFont, text1, Vector2.Zero, Color.Red);
   string text2 = string.Format("Rotation du tank : {0}", TankRotation);
   spriteBatch.DrawString(this.textFont, text2, new Vector2(0,20), Color.DarkGreen);
   string text3 = string.Format("Rotation du canon : {0}", CanonRotation);
   spriteBatch.DrawString(this.textFont, text3, new Vector2(0, 40), Color.Yellow);

   spriteBatch.Draw(this.tank, TankPosition, null, Color.White, MathHelper.ToRadians(TankRotation), TankOriginPoint, 1.0f, SpriteEffects.None, 0);            
   spriteBatch.Draw(this.canon, TankPosition, null, Color.White, MathHelper.ToRadians(CanonRotation), new Vector2(canon.Width / 2 - 4.0f, canon.Height / 2), 1.0f, SpriteEffects.None, 0);
            
   spriteBatch.End();
   base.Draw(gameTime);
} 

On utilise la méthode DrawString () de spriteBatch. Cette méthode prend 3 paramètres :

  1. Police de caractères (SpriteFont)
  2. Position (en pixel à l’écran)
  3. Couleur du texte.

Le résultat en image :

xna_tuto3_3

  • Si je veux rajouter le ° à coté de l’angle.

Si vous tentez de faire le changement suivant :

string text2 = string.Format("Rotation du tank : {0} °", TankRotation);
spriteBatch.DrawString(this.textFont, text2, new Vector2(0,20), Color.DarkGreen);

Vous allez rencontre l’erreur : Argument Exception : The character ‘°’ (0x00b0) is not available in this SpriteFont. If applicable, adjust the font’s start and end CharacterRegions to include this character. Parameter name: character

Cela est dû au fait que la balise CharacterRegions ne contient pas ce  caractère dans sa liste de caractère disponible. (‘°’ a le code hexa 0xb0 => 176 en décimal). Il suffit de rajouter une range dans le fichier xml pour ajouter le caractère.

<CharacterRegions>
      <CharacterRegion>
        <Start>32</Start>
        <End>126</End>
      </CharacterRegion>
      <CharacterRegion>
        <Start>176</Start>
        <End>176</End>
      </CharacterRegion>
 </CharacterRegions>

Si vous recompilez et lancez l’application, le symbole apparait.

xna_tuto3_4

  • Conclusion

Vous pouvez maintenant écrire du texte à l’écran. Dans le prochain article, nous verrons l’utilisation du clavier/souris/manette XBOX 360 et Touch sur Windows Phone.

Code de ce tutorial

[XNA 4] Sortie de la version RTM

Filed under: Non classé — sebastiencourtois @ 09:43

Petit post pour annoncé que XNA Game Studio 4 n’est plus en BETA et vient de passer en RTM.

Vous pouvez développer vos applications PC et Windows Phone sans soucis :). Pour la XBOX 360, le déploiement d’application est possible mais l’outil XNA Game Studio Connect est encore en béta pour quelque temps.

Pour télécharger, il suffit d’aller sur la page de lancement XNA.

16/09/2010

[XNA 4] Tutoriel 2 : Gestion des images (2D)

Filed under: .NET, Débutant, Windows Phone, XBOX 360, XNA — Étiquettes : , , , , , , , , — sebastiencourtois @ 16:34

Remarque : Ce tutorial fait suite au Tutorial 1 et reprend le projet utilisé dans ce dernier.

Nous allons voir dans ce tutorial, comment charger, afficher et déplacer des images (aussi appelé sprites). Vous trouverez ici les images utilisées pour ce tutorial.

  • Ajout des images au projet

Lorsque vous souhaitez utiliser une image dans un projet XNA (que ce soit en 2D ou en 3D), il est nécessaire de l’ajouter au projet Content associé au projet XNA (Tutorial2DContent dans notre exemple).

xna_tuto2_1

Clic droit sur le projet Tutorial2DContent > Ajouter > Elément existant.

xna_tuto2_2

On sélectionne les fichiers que l’on souhaite ajouter et on clique sur le bouton “Ajouter”. Une fois que les fichiers sont ajoutés, on peut voir leurs propriétés.

xna_tuto2_3

La propriété Asset Name est le nom de l’image que nous utiliserons au sein de l’application. Vous pouvez noter qu’il n’y a pas d’extension. Vous pouvez la modifier si vous le souhaitez.

  • Chargement des images

Une fois que nous avons ajouté les images au projet, nous allons les charger au démarrage de l’application. Cela se fait généralement dans la méthode LoadContent() de la classe Game.

protected override void LoadContent()
{
    spriteBatch = new SpriteBatch(GraphicsDevice);
    //Chargement des deux textures
    this.tank = Content.Load<Texture2D>("tank_body");
    this.canon = Content.Load<Texture2D>("tank_canon");
}

Pour charger des images, il suffit d’appeler la méthode Load de la propriété Game.Content. Il est nécessaire de lui donner le type de données que l’on souhaite charger. Dans notre cas, on souhaite charger une image qui est représenté, dans XNA, par la classe Texture2D. On passe en paramètre  l’Asset Name de l’image à charger (et non pas le nom du fichier).

Le résultat du chargement est stocké dans une propriété de type Texture2D.

//Stockage de la texture du corps du tank
Texture2D tank;
//Stockage de la texture du canon
Texture2D canon;

  • Libération de la mémoire prise par les images (Optionnel)

Bien que .NET soit pourvu d’un Garbage collector nettoyant régulièrement les zones mémoires non utilisés, il est possible de faire le ménage soit même. Ainsi, si l’on souhaite libérer la mémoire prise par ces deux textures, il suffit d’aller dans la méthode UnloadContent de Game et d’appeler les Dispose() sur chacune des textures.

protected override void UnloadContent()
{
   this.tank.Dispose();
   this.canon.Dispose();
}

Rappel : La méthode UnloadContent() est appelée lorsque l’on sort de la boucle de jeu courante.

  • Affichage des images

Maintenant que nous avons chargé nos images, nous allons les afficher. Cela se passe dans la méthode Draw() de la classe Game.

//Position du tank
 Vector2 TankPosition = new Vector2(30, 40);

 protected override void Draw(GameTime gameTime)
 {
     GraphicsDevice.Clear(Color.CornflowerBlue);
     spriteBatch.Begin();
     spriteBatch.Draw(this.tank, TankPosition, Color.White);
     spriteBatch.End();
     base.Draw(gameTime);
 }

On utilise spriteBatch pour dessiner les textures à l’écran. On doit “préparer” spriteBatch avant son utilisation en appelant la méthode Begin() (nous parlerons de ses paramètres optionnels plus loin). On utilise ensuite la méthode Draw() afin d’afficher l’image. Dans sa version la plus simple, Draw comprend 3 paramètres :

  1. L’image à afficher
  2. La position de l’image par rapport au coin haut gauche de l’écran. La position est définie dans une structure Vector2. Afin de simplifier le code, on a créé une variable TankPosition pour stocker et manipuler la position de l’image.
  3. La teinte de l’image. Si l’on souhaite avoir l’image originale sans modification, on met la couleur blanche.

Une fois que l’on a dessiné l’ensemble des images souhaités, on appelle la méthode End() du spriteBatch afin que celui-ci envoie l’ensemble des informations à la carte graphique (ce comportement dépend du mode de spriteBatch choisi… voir plus loin dans cet article …).

  • Déplacement des images

Si l’on souhaite déplacer l’image, il suffit de modifier les données de la variable TankPosition entre chaque appel de la méthode Draw(). Si vous vous rappelez de la structure d’une boucle de jeu XNA, une méthode est appelé avant chaque Draw() ==> Update(). Cette méthode est destinée à faire toutes les tâches non graphiques (qui n’interagisse pas directement avec la carte graphique). Ainsi si l’on souhaite déplacer notre tank d’un pixel vers la droite et d’un pixel vers le bas, on utilisera le code suivant :

protected override void Update(GameTime gameTime)
{
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
      this.Exit();

   TankPosition.X += 1;
   TankPosition.Y += 1;
   base.Update(gameTime);
}
  • Système de coordonnées & Origines

Par défaut, l’ensemble des entités 2D de XNA (fenêtres, sprites …) sont définies dans un système de coordonnées cartésienne orthonormé dont l’unité est le pixel.

xna_tuto2_4

Le coin haut gauche de l’entité est l’origine (0,0) et les x sont positif lorsque l’on va vers la droite. Les y sont positif quand on va vers le bas.

Il est important de noter que le point d’origine est le même que ce soit pour la position de l’entité comme pour d’autres transformations comme la rotation. Nous allons voir, dans le paragraphe suivant, qu’il est possible de modifier ce point d’origine.

  • Rotation des images

Nous allons maintenant faire tourner le tank sur lui-même. Pour cela nous allons rajouter une propriété permettant de conserver la rotation

//Rotation en degré du tank
float TankRotation = 30.0f;

Attention : La rotation 0 est l’image tel qu’importée dans XNA. De plus la rotation se fait selon l’axe du point d’origine. Si l’on souhaite faire tourner une image sur elle-même, il est nécessaire de déplacer le point d’origine au centre de l’image (Width/2, Height/2).

Pour réaliser la rotation,nous utilisons une version plus avancée de la méthode Draw du spritebatch :

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);
    spriteBatch.Begin();
    spriteBatch.Draw(this.tank,                                  // Texture (Image)
                     TankPosition,                               // Position de l'image
                     null,                                       // Zone de l'image à afficher
                     Color.White,                                // Teinte
                     MathHelper.ToRadians(TankRotation),         // Rotation (en rad)
                     new Vector2(tank.Width/2,  tank.Height/2),  // Origine
                     1.0f,                                       // Echelle
                     SpriteEffects.None,                         // Effet
                     0);                                         // Profondeur
    spriteBatch.End();
    base.Draw(gameTime);
}

Les deux premiers et le quatrième paramètres ont déjà été décrit précédemment. Intéressons-nous aux autres paramètres :

    • Zone de l’image : Il est possible de ne copier qu’une partie de la texture à l’écran. Dans ce cas, on indique un rectangle indiquant la partie de la texture à afficher. Si l’on souhaite afficher la texture en entier, on passe la valeur null.
    • Rotation : Dans ce paramètre, on indique la rotation que l’on souhaite appliquer à la texture. Attention, l’angle de rotation doit être donné en RADIANS. Pour vous aider,  XNA fournir une classe MathHelper avec une méthode statique convertissant les degrés en radians.
    • Origine : Ce paramètre permet d’indiquer le point d’origine de la texture pour l’ensemble des transformations (déplacement, rotation…). Par défaut, il est situé en haut à gauche mais il est possible de le modifier comme indiqué dans notre exemple (le point d’origine de notre exemple se trouve au centre de la texture afin que celle-ci tourne sur elle-même.).
    • Echelle : Il est possible d’agrandir et de réduire la taille de votre texture. La valeur 1 permet de conserver la taille originale.
    • Effet : Ce paramètre permet l’application d’un effet de renversement (renversement vertical ou horizontal).Si vous ne souhaitez pas utiliser ces effets, mettre à SpriteEffects.None.
    • Profondeur : Le dernier paramètre permet d’indiquer la profondeur à laquelle vous souhaitez mettre votre texture. Cela permet de gérer les cas de chevauchement de texture (voir paragraphe suivant).

L’ensemble des méthodes Draw() de spriteBatch sont décrites en détail dans la MSDN.

  • Superposition et transparence

Si nous faisons la même chose avec le canon du tank, on obtient le code suivant :

//Position du tank
Vector2 TankPosition = new Vector2(30, 40);
//Rotation en degré du canon
float CanonRotation = 40.0f;

protected override void Draw(GameTime gameTime)
{
   GraphicsDevice.Clear(Color.CornflowerBlue);
   spriteBatch.Begin();
   spriteBatch.Draw(this.canon, TankPosition, null, Color.White, MathHelper.ToRadians(CanonRotation), new Vector2(canon.Width / 2 - 4.0f, canon.Height / 2), 1.0f, SpriteEffects.None, 0);
   spriteBatch.Draw(this.tank, TankPosition, null, Color.White, MathHelper.ToRadians(TankRotation), new Vector2(tank.Width / 2, tank.Height / 2), 1.0f, SpriteEffects.None, 0);
   spriteBatch.End();
   base.Draw(gameTime);
}

Remarque : Le point d’origine pour le canon contient un –4.0f pour X. Cela provient du décalage entre le centre de la texture du tank et celle du canon. Afin d’avoir un affichage un peu plus réaliste, j’ai décalé légèrement le point d’origine du canon.

Cela semble correct mais lorsque l’on affiche le résultat :

xna_tuto2_5a

xna_tuto2_5b

Canon et Tank correctement placé

Lors de la rotation du canon, une partie de celui ci disparait sous le tank lui même.

Ce problème est du à la façon dont spriteBatch affiche les sprites. Cela peut être défini dans la méthode Begin() grâce au premier paramètre qui est l’énumération SpriteSortMode

Valeur

Description

Deferred

Valeur par défaut et la plus utilisée. Envoi les opérations à la carte graphique lors de l’appel de End(). Cela permet d’utiliser plusieurs spritesBatchs sans risquer d’avoir de conflit au niveau de la carte graphique. Dépend du paramètre profondeur de la méthode Draw.Le premier plan étant 1 et le fond 0.

BackToFront

Affichage des sprites selon le paramètre de profondeur passé par  la méthode Draw. Le premier plan étant 1 et le fond 0.

FrontToBack

Affichage des sprites selon le paramètre de profondeur passé par  la méthode Draw. Le premier plan étant 0 et le fond 1.

Immediate

Affiche les images en écrasant les pixels déjà présent. Ce mode est le plus rapide mais celui qui gère le moins bien les transparences et supperposition ainsi que l’utilisation de multiples spritesBatch.

Texture

Affiche les images dans l’ordre des textures. Ainsi, si plusieurs appels à Draw se font avec la même texture, l’ensemble des appels sera fait en même temps pour gagner du temps en transfert de données vers la carte graphiques.

Pour la documentation originale sur SpriteSortMode : La page MSDN.

Begin peut prendre aussi un autre paramètre pour indiquer comment il doit gérer les cas d’écrasement de pixel (vouloir dessiner un pixel qui a déjà été dessiné par une autre texture). Ce paramètre est de type BlendState et comprend 4 valeurs possibles.

Valeur

Description

AlphaBlend

Valeur par défaut. Fusion du pixel source/destination en tenant compte du canal alpha (transparence).

Additive

Ajoute les canaux RGBA entre les deux pixels

NonPremultiplied

Ajoute les canaux RGB entre les deux pixels sans tenir compte de la transparence

Opaque

La couleur du nouveau pixel écrase celle du tampon.

Pour la documentation originale sur BlendState: La page MSDN.

Pour résoudre le problème que nous avons rencontré, il y a donc deux solutions :

    • Intervertir les deux méthodes Draw()
protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);
    spriteBatch.Begin();
    spriteBatch.Draw(this.tank, TankPosition, null, Color.White, MathHelper.ToRadians(TankRotation), new Vector2(tank.Width / 2, tank.Height / 2), 3.0f, SpriteEffects.None, 0);            
    spriteBatch.Draw(this.canon, TankPosition, null, Color.White, MathHelper.ToRadians(CanonRotation), new Vector2(canon.Width / 2 - 4.0f, canon.Height / 2), 3.0f, SpriteEffects.None, 0);
    spriteBatch.End();
    base.Draw(gameTime);
}

    • Changer la méthode Begin
protected override void Draw(GameTime gameTime)
{
     GraphicsDevice.Clear(Color.CornflowerBlue);
     spriteBatch.Begin(SpriteSortMode.FrontToBack,null);
     spriteBatch.Draw(this.canon, TankPosition, null, Color.White, MathHelper.ToRadians(CanonRotation), new Vector2(canon.Width / 2 - 4.0f, canon.Height / 2), 3.0f, SpriteEffects.None, 0);
     spriteBatch.Draw(this.tank, TankPosition, null, Color.White, MathHelper.ToRadians(TankRotation), new Vector2(tank.Width / 2, tank.Height / 2), 3.0f, SpriteEffects.None, 0);
     spriteBatch.End();
     base.Draw(gameTime);
}

On obtient un affichage “correct” :

xna_tuto2_6

Remarque : Les petites zones bleues proviennent de mon placement tank/canon qui n’est pas parfait sur pour ces images.

  • Conclusion

Vous savez maintenant charger ,afficher et déplacement un image dans un univers 2D XNA. Dans un prochain post, nous verrons comment déplacer ces images en fonction des entrées de l’utilisateurs (souris,clavier,Manette XBOX 360, Touch Windows Phone). Le prochain post est consacré à l’affichage de texte dans la fenêtre.

Téléchargement du résultat final

  • Exercices pour tester ses compétences
  1. Faire se déplacer le char dans l’écran en le faisant rebondir sur les coins de l’écran.
    • Indice 1 : Attention au point d’origine
    • Indice 2 : Les dimensions de l’écran sont disponibles au travers de graphics.GraphicsDevice.Viewport
    • Indice 3 : Attention au pixel transparent autour du tank
  2. Faire le même exercices en gérant le couple char+canon.

Solution possible

14/09/2010

[XNA 4] Tutoriel 1 : Création et structure d’un projet XNA

Filed under: .NET, C# 4, Débutant, Jeux vidéos, XBOX 360, XNA — Étiquettes : , , , , , , , — sebastiencourtois @ 13:57

En l’honneur de la sortie de XNA 4, j’ai décidé de réaliser une série de posts sur le sujet. J’essaierais d’être le plus général possible afin que l’ensemble des codes fonctionnent sur les plateformes cibles de XNA 4 : PC, XBOX 360, Zune et le nouveau Windows Phone. Lorsque cela ne sera pas le cas, cela sera indiqué dans le titre ou dans les premières lignes des posts.

Ces tutoriels seront à destination des débutants en programmation 3D.Toutefois, il est nécessaire d’avoir des bonnes notions de C#.

  • Commençons par le début : XNA ?

D’après les forums/FAQ, XNA voudrait dire : “XNA’s Not Acronymed”. Vu comme ça on n’est pas plus avancé (un peu comme le GNU is Not Unix chez les adorateurs de pingouins) :).

XNA est une plateforme (Framework) de développement de jeu vidéo 2D/3D à destination du PC, de la XBOX 360 et d’appareils portables (Zune/Windows Phone pour l’instant). Cette plateforme est en fait une surcouche d’une API de développement de jeu vidéo utilisés dans la plupart des jeux vidéo commerciaux actuels : DirectX.

XNA a été créé pour :

  • Fournir un environnement de développement simplifié pour réaliser des applications graphique 2D/3D rapidement sans trop se préoccuper des couches plus bases (notamment Win32/DirectX qui sont assez indigeste).

  • Fournir un environnement entièrement managé pour la création de jeu vidéo (DirectX étant uniquement axé C++). Un projet de DirectX managé a été lancé par Microsoft, il y a quelque années, mais il fut arrêté au profit de XNA. Un projet communautaire a été relancé sur le même principe : SlimDX. L’idée est de fournir un wrapper directement sur les méthodes DirectX afin de permettre l’utilisation de fonctionnalités non disponibles dans XNA.

  • Permettre de déployer l’application sur plusieurs plateformes différentes (PC,XBOX 360,mobiles) sans avoir à (trop) changer le code.

Pour atteindre ces objectifs, XNA a limité les fonctionnalités disponibles. En effet, XNA ne permet d’utiliser que DirectX 9 afin d’être compatible avec l’ensemble des plateformes cibles (Il faut savoir que DIrectX9 est sorti en Août 2005  et nous sommes actuellement à DirectX 11sorti en Octobre 2009). De plus, XNA étant une plateforme managée, le garbage collector et les mécanismes internes de .NET seront activés et pourront ralentir l’application (comparé à une application DX C++). Toutefois, le niveau des applications XNA restent correcte pour la création de jeu (voir les démos présentées sur le concours Imagine Cup par des étudiants : http://www.xna-connection.com/post/Imagine-Cup-2010-Les-finalistes-section-Game-Design).

  • Fini les discours commerciaux, passons à la pratique :

Afin de développer sur XNA 4, il est nécessaire d’installer :

Une fois installé, lancez Visual Studio, puis aller dans Fichier>Nouveau Projet. Dans l’écran “Nouveau projet”, allez sur Visual Studio > XNA Game Studio 4 puis choisissez “Windows Game (4.0)”. N’oubliez pas de choisir un nom de projet (“Tutorial2D” dans ce post) puis cliquez sur OK.

xnatuto1

 

 

 

 

 

 

 

 

 

 

 

Une fois créé, vous devriez avoir l’arborescence suivante :

xnatuto2

Le projet Tutorial2D est le projet principal contenant le point d’entrée ainsi que toute les références de votre application. Il est composé de 4 fichiers :

  • Game.ico : Icone du jeu (visible dans le coin en haut à gauche de la fenêtre)
  • Game1.cs : Code du jeu (voir suite du tutorial)
  • GameThumbnail.png : Vignette représentant le jeu (visible dans le menu XBOX 360)
  • Program.cs : Point d’entrée du programme.

Le projet Tutorial2DContent est un projet dit “de contenu”. Il accueillera uniquement les ressources (textures, modèles 3D, sons…) qui seront utilisées dans le jeu.

  • Analysons le point d’entrée du jeu : Program.cs
using System;

namespace Tutorial2D
{
#if WINDOWS || XBOX
    static class Program
    {
        static void Main(string[] args)
        {
            using (Game1 game = new Game1())
            {
                game.Run();
            }
        }
    }
#endif
}

Il s’agit d’un point d’entrée classique. Le .NET lance l’application en appelant Program.Main. On crée une instance de Game1 et on appelle “le point d’entrée” du jeu. Le #if Windows || XBOX sont des commandes de préprocesseurs pour indiquer que le code qui encadré est uniquement pour PC/XBOX. Pour une application mobile, le code est différent et fera l’objet d’un prochain post.

  • Déroulement d’un programme XNA

Une fois la méthode Game1.Run() appelé dans Program.cs, XNA va rentrer dans un processus défini comme suit :

xnatuto3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • Initialize() : Zone d’initialisation du jeu. C’est ici que se trouve le chargement et la configuration des modules non graphiques.
  • LoadContent() : Zone de chargement des données du jeu (textures, effets(shaders), modèles 3D…).
  • On rentre ensuite dans une boucle infini (la boucle de jeu). A intervalle régulier (60 fois par secondes au maximum par défaut), les deux méthodes suivantes vont être appelées dans cet ordre :
    • Update() : Mise à jour des données du jeu. Gestion du clavier/souris/gamepad, déplacement des joueurs, moteur physique …
    • Draw() : Affichage de l’ensemble des données graphiques. C’est ici que l’on indique à la carte graphique ce que l’on souhaite afficher à l’écran.
  • UnloadContent() : Lorsque l’on sort de cette boucle infini (En appelant la méthode Exit()), la méthode UnloadContent() est appelée afin de libérer la mémoire des objets dont on n’a plus besoin.

 

  • Game1.cs

L’ensemble des méthodes décrites ci-dessus se retrouvent dans la classe Microsoft.Xna.Framework.Game dont dérive Game1. Il est donc nécessaire de les surcharger afin de pouvoir créer son propre code.

public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;

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

    protected override void Initialize()
    {
            

        base.Initialize();
    }

    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);
    }

    protected override void UnloadContent()
    {

    }

    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();


        base.Update(gameTime);
    }

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


        base.Draw(gameTime);
    }
}

Il est à noter que des appels aux classes parents sont réalisés à la fin de Initialize(), Update() et Draw(). Ces appels sont nécessaires au bon fonctionnement de l’application et doivent toujours se trouver à la fin des méthodes concernés. Par exemple, si vous commentez base.Initialize(), XNA ne fera pas l’appel à LoadContent() mais lancera la boucle de jeu directement (Update <=> Draw).

Deux propriétés sont utilisées dans cette classe :

 GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
  • Gestionnaire de carte graphique. Cette propriété permet la configuration de la carte graphiques ainsi que l’accès à celle-ci.

     

  • Cette propriété permet l’affichage de texte et de texture à l’écran.
  • Au sein de la méthode Update(), on remarque l’utilisation du GamePad.

      if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

    Ainsi, si l’on clique sur le bouton “back” (petit bouton blanc à gauche du bouton central), on sort de la boucle de jeu. La gestion des entrées utilisateurs est une des tâches à effectuer dans la méthode Update().

    Nous étudierons l’utilisation de l’ensemble de ces outils en détail dans les prochains posts.

    • Lancement de son premier jeu XNA

    Lorsque l’on lance le projet, on obtient un écran avec un fond bleu.

    xnatuto4

    Cela provient du code de la méthode Draw(). Lors de la première ligne, on demande à la carte graphique (GraphicsDevice) de vider la fenêtre et de mettre une couleur de fond CornFlowerBlue. (si vous souhaitez savoir pourquoi cette couleur en particulier est présente par défaut lorsque l’on crée un projet XNA, je vous conseille la lecture de ce post).

    • C’est déja fini ? non, ce n’est que le début.

    Maintenant que vous avez les bases sur le fonctionnement de XNA (désolé pas de code :(), les prochains posts seront dédiés à la création d’un jeu 2d multiplateforme (PC/XBOX360,Windows Phone). J’ai dans l’idée de faire un petit Space Invader mais si vous avez d’autres idées, n’hésitez pas à poster en commentaires.

    La suite de la séries XNA : Tutoriel 2 : Gestion des images

    Pour ceux qui veulent déjà faire de la 3D, des tutoriaux arriveront très bientôt. En attendant vous pouvez toujours aller sur les liens XNA suivants :

    • Français :

    http://msmvps.com/blogs/valentin/

    http://www.xna-connection.com/

    http://xna-france.com/

    http://www.xnainfo.com/

    http://msdn.microsoft.com/fr-fr/directx/default.aspx

    • Anglais :

    http://creators.xna.com/fr-FR/

    http://blogs.msdn.com/b/shawnhar/

    http://xna-uk.net/

    14/06/2010

    Microsoft@E3 : Conférence Microsoft 2/2 (Kinect)

    Filed under: E3, Hors Catégorie, Kinect, Natal, XBOX 360 — Étiquettes : , , , , , , — sebastiencourtois @ 22:29

    La conférence de Microsoft à l’E3 a été l’occasion de voir enfin Kinect (Ex Natal) en action. (Les vidéos arriveront au fur et à mesure de leur publication par Microsoft).

    • Qu’est ce que Kinect ?

    Kinect  (ex Projet Natal) est un accessoire à la xbox 360 composé de de capteurs et de caméras noir et blanc ainsi que de rayon infrarouge permettant ainsi de reconnaitre les mouvements d’une personne dans l’espace avec une bonne précision. Vous pouvez ainsi jouer sans manette. Le but (plus ou moins avoué) de Microsoft est tout simplement de fournir un concurrent à la Wii voire de la dépasser et ainsi conquérir le cœur des joueurs casuals.

    Big Brother is watching you

    Remarque : Certains d’entre vous auront peut être remarqué que l’utilisation des rayons infrarouge est aussi utilisé par la table Surface.

    • Comment ça marche en interne ?

    Si vous vous demandez comment Microsoft a réussi à réaliser cet outil de reconnaissance de personnes, je vous conseille l’article suivant (en anglais). Pour faire simple, ils ont enregistré pendants des heures de nombreuses personnes se déplaçant devant l’accessoire en lui décrivant chaque mouvement (en gros, on lui montre 10 M photos de mains différentes en lui répétant que c’est une main… au bout d’un moment elle comprend et sait reconnaitre une main). Alliant ce gros flux de données corporelles et des algorithmes d’intelligence artificielle (Algorithmes génétiques+apprentissage à  mon avis), on obtient un système (appelé TheBrain).Celui-ci est capable de reconnaitre, à partir des flux vidéos, les parties du corps (30 parties du corps) et ainsi analyser les mouvements de chaque membre en temps réel (30 images/sec …). Il faut aussi rappeler que Kinect peut gérer plusieurs personnes en même temps ce qui montre que l’optimisation du système a du être poussée au maximum.

    • XBOX LIVE et Kinect

    La démo faite à l’E3 est assez révélatrice des possibilités de la bête. Une personne se met devant sa télé et fait un signe de la main puis est reconnu directement par le système.Son avatar se met alors à bouger comme lui. Ainsi il a juste à pointé de la main vers la télé pour faire apparaitre un curseur (comme avec une Wiimote). L’utilisateur peut aussi parler et donner des ordres comme “Xbox Play Movie” pour le film sélectionné. Une combinaison des mouvements et de la parole (car Kinect contient un micro) est donc possible grâce à Kinect.

    Il est aussi possible de chat par vidéo grâce à cette accessoire qui est monté sur un petit moteur. Celui-ci lui permet de suivre l’utilisateur lors de ses déplacement dans la pièce (pas plus loin … c’est une caméra pas un robot :)).

    • “The time of talk is over !!!” … Les jeux

    On attendait beaucoup des jeux présentés pour cet accessoire. 8 jeux ont été présentés et ils seront au nombre de 15 au lancement de la console.

      • Kinectimals

    C’est connu, les enfants adorent les animaux. Microsoft a donc créé un logiciel où l’on peut joueur avec des animaux grâce à Kinect. La démo a montré une gamine jouer avec un bébé tigre dans une forêt.Saut,roulade,faire le mort,sauter à la corde, la petite fille s’éclate avec son bébé tigre comme si c’était un vrai en lui donnant des ordres exécutés instantanément par le félin.

    Remarque : La vidéo est une version courte de ce qui a été présenté.

    Adorant les animaux (et surtout les bébés animaux), j’ai trouvé cette démo attendrissante d’autant que le “contact” animal/enfant semble bien rendu. Graphiquement, c’est assez beau (surtout la fourrure des animaux). Seul petit bémol tout de même avec la présence d’un flou continuel qui ne permet pas d’apprécier l’ensemble de l’environnement à sa juste valeur. Reste à savoir s’il y a un gameplay intéressant. On nous promet 40 animaux différents et 30 activités. J’ai un peu peur que cela ressemble trop au Eye Pet sur PS3 dont je trouvais le concept super (oui je suis un grand gamin :)) mais très limité au niveau activité.

    Cela me rappelle une démo qui m’avait marqué l’an dernier lors de l’annonce de Natal :

      • KinectSport

    Comme dit précédemment, Microsoft va pomper chez les concurrents et ne s’en cache pas. Ainsi KinectSport est une reproduction de WiiSport à quelque détails près. Tout d’abord le moteur graphique, même s’il reste un peu cartoon avec la possibilité de jouer avec ses avatars xbox, est graphiquement bien supérieur à ce qui se fait sur Wii aujourd’hui. De plus, il y a une plus grand variété d’épreuves : foot / bowling / athlétisme (course de haie, javelot,saut en longueur …) / ping pong  / boxe  / beach volley.

      • Kinect Joy Ride

    Clone de Mario Kart avec sensiblement le même gameplay et graphiquement supérieur tout en restant cartoon (avatar xbox 360). Au vu de la démo, je n’ai pas trouvé ça si intéressant que ça. Reste à savoir quand je l’aurais en main.

      • Kinect Adventure

    jeu d’aventure dans lequel vous devrez éviter des obstacles seul ou a plusieurs (sorte de tetris humain), vous devrez chevauchez des nuages ou encore descendre des chutes d’eau en canoé. Graphiquement joli (notamment le rendu de l’eau et des éclaboussures) mais toujours cartoon (avec les avatar). Semble assez fun en multi coopération.

      • YourShape (by UbiSoft)

    Premier éditeur indépendant à dégainer sur Kinect, UbiSoft (cocorico) qui sort un clone de WiiFit : YourShape.Divisé en 4 grandes sections ( art martiaux, yoga,fitness, mini jeux), il permet de s’entrainer et/ou de se maintenir en forme tout en s’amusant. Certains diront que WiiFit est déjà là et que ce n’est qu’un clone de plus sur le marché du fitness. Toutefois le plus de YourShape est que, avec Kinect, les mouvements sont vérifié visuellement et non plus selon la pression (aléatoire parfois) sur la Wii board. La démo effectué de chacune des sections est sympathique.

     

      • Dance Central (by Harmonix)

    Jeu de Dance qu’on pourrait qualifier de clone (encore !) de Just Dance sur Wii. Toutefois c’est encore la puissance de Kinect et la précision de la capture qui devrait permettre au jeu de s’imposer contre la précision aléatoire du jeu Wii (certains se reconnaitront :)). De plus les chorégraphies sont a base de routines bien expliquées et un mode entrainement pour les néophytes permet de rentrer facilement dans le jeu. Enfin le background est en 3D et reproduit vos mouvements :). Pour les musique, principalement de la pop,hip hop,r’n’b (Lady gaga, NoDoubt …).

     

      • Projet secret (by LucasArt)

    Un projet initié avec LucastArt sur l’univers Star Wars a été dévoilé permettant à tous les fan-boys d’incarner un jedi en vrai. Un court vidéo de gameplay a été montré et … grosse déception. Les mouvements sont lent et les déplacements du sabre aléatoire parfois ne touchant pas l’ennemi qui tombe. Bref déçu par le gameplay et un moteur graphique correct mais sans claque visuelle (pourtant on joue pas avec un avatar là je crois). Le projet n’est pas prévu avant 2011 donc on espère que ce sera plus au point la prochaine fois.

      • Forza Motorsport Kinect

    La référence des jeux de voiture sur XBOX reviendra en 2011 avec un jeu magnifique compatible Kinect. Quelques vidéos de gameplay qui ressemble au dernier opus et un essai d’une Ferrari très belle. ça promet d’envoyer du lourd et ça sera en 2011.

    • Remarques diverses :

    Lorsque l’on joue à certains jeux, Kinect prend des photos,vidéos de vous en train de jouer. Celles-ci sont récupérables et il est possible de les envoyer sur Facebook (histoire de pourrir les albums des potes c’est génial).

    Il y a une phase de reconnaissance plus longue lors de la première utilisation afin de scanner le corps et le reconnaitre rapidement les fois suivantes. Certains jeux arrivent à détecté lorsque l’utilisateur enlève ou met un vêtements (réalité augmenté dans les futurs jeux XBOX 360 … je vais chercher  ma cape harry potter :)).

    • Conclusion :

    Sortie annoncé  : 4 Novembre 2010 aux US (inconnu en France… surement une semaine à un mois après). 15 jeux promis à la sortie. Les rumeurs annoncent un prix plus bas pour ces jeux que les jeux 360 habituels.

    Prix : Inconnu pour le moment. Les rumeurs parlent de deux versions (une fixe et une avec caméra qui suit la personne). La version “suiveuse” serait la plus cher à 180 $.

    Kinect sur PC : ??? 😦

    Kinect via XNA : ??? 😦

    Avis : Mitigé. Je suis très emballé par la technologie et je pense que, comme Surface, les développeurs n’ont pas encore pensé leur “Kinect” à fond. Donc on aura un grand temps d’expérimentation de la part des éditeurs avant de  trouver des nouveaux paradigmes de gameplay et de jeux (comme cela a été le cas pour la Wii). Mes grandes craintes sont le prix et la fiabilité du système qui promet beaucoup mais qui, d’après les rumeurs, ne marche pas si bien que cela (pour l’instant le  grand public n’a pas pu y toucher… cela changera peut être dès demain au salon).

    Pour terminer, je dirais que Microsoft a frappé un grand coup ce soir mais risque d’avoir dégainé  trop tard face à une Wii déja trop implanté dans la culture vidéo ludique mondiale. Microsoft aurait sorti ce système il y a deux/trois ans,il aurait été les rois du pétrole. Aujourd’hui, il tente un coup de poker pour récupérer le public casual avec un très bon produit mais il néglige clairement les hardcore gamer qui l’ont fait vivre jusqu’à maintenant. En espérant que la sauce prenne …

    Bonus : La vidéo de promotion :

    Microsoft@E3 : Conférence Microsoft 1/2

    Filed under: Débutant, E3, Hors Catégorie, Kinect, Natal, XBOX 360 — Étiquettes : , , , , , , — sebastiencourtois @ 21:59

    La conférence Microsoft de E3 vient de se terminer.

    Revenons un peu sur ce qui a été montré et annoncé (je mettrais les liens vidéos au fur et à mesure de leur parution

    (Note : je parlerai de Kinect dans un post suivant) :

    • La XBOX a 10 ans
    • Metal Gear Rising (Sortie 2011)

    Hidéo Kojima est venu présenter son Metal Gear Rising avec un trailer et quelques vidéos de gameplay. Au vue de ces vidéos, je pense que le coté infiltration a été mise de coté pour fournir un beat-them-all au sabre avec un moteur graphique/physique qui assez impressionnant/précis.

    • Gears of Wars 3 (Fin 2010 / 2011)

    Démo de gameplay multi (à 4) en coopération. Environnement vaste et impressionant graphiquement. Le jeu reste bien bourrin avec un bestiaire.

    • Fable 3 (Sortie : Fin 2010)

    Trailer  ultra rapide du dernier né de Peter Molyneux. Deux ans après le dernier épisode, je n’ai pas trouvé que le moteur graphique s’était grandement améliorer. Maintenant reste à voir le gameplay libre qui tient très à cœur à Peter. J’aurais aimé une petit annonce de compatibilité avec Kinect (surtout vu la démo impressionnante de l’E3 2009) mais il n’a rien dit. 

    • Kindoms (de Crytek)

    Trailer énigmatique montrant un guerrier de l’antiquité en sang… ce qui étonne c’est que personne n’avait parlé d’un projet Crytek sur XBOX 360 (exclusif en plus) à part le très attendu Crysis 2… on reste quand même sur notre faim…  plus de réponses dans cet E3 ou en 2011.

    • Halo Reach

    Une démo du jeu solo. Pas de grande nouveautés depuis la béta publique multi-joueurs.

    • XBOX LIVE :

    Microsoft annonce qu’il y a 25 Millions d’utilisateurs inscrit sur la plateforme

    Il est mainteinant possible de regarder/écouter les vidéos/musique/photos à plusieurs tout en chattant (vidéo/audio/text).

    Un partenariat a été signé entre ESPN et Microsoft pour fournir du foot US (College football uniquement), Basket et Soccer directement sur le 360. Apparemment, ce service serait gratuit pour les membres XBOX LIVE GOLD (à vérifier).

    • XBOX 360 SLIM

    Une nouvelle 360 est disponible dès aujourd’hui au même prix que la précédent. Elle inclus le WIFI (Norme N) par défaut + HDD 250 Go. Je pense aussi que les composants internes sont plus fiables que les versions précédents. Elle a été aussi complètement redesigné et est beaucoup moins encombrantes.

    Conclusion et avis sur cette conférence :

    Si on excepte Kinect, la conférence a été pauve en annonce et Microsoft a voulu privilégié le casual gaming par rapport aux hardcore gamer (Peter Molineux est resté 30 secondes chrono sur scène :)). On sent clairement une intention de vouloir marcher sur les platebande de la Wii… malheureusement avec 2 ou 3 ans de retard.

    Bref pas grand chose à se mettre sous la dent à part du Kinect…

    09/06/2010

    [.NET 3.5] Attention lors de l’utilisation des lambdas/closures

    Filed under: .NET, C#, C# 4, Hors Catégorie, Intermediaire — Étiquettes : , , , , — sebastiencourtois @ 13:05

    Afin de commencer cette article, commençons par une petite devinette.

    Quel sont les résultats de ces deux morceaux de code :

    var actions = new List<Action>();
    
    for (int i = 0; i < 10; i++)
        actions.Add(() => Console.WriteLine(i));
    
    foreach (var action in actions)
        action();
    var actions = new List<Action>();
    
    for (int i = 0; i < 10; i++)
    {
        int j = i;
        actions.Add(() => Console.WriteLine(j));
    }
    
    foreach (var action in actions)
        action();

    La première réponse que vous avez du vous faire est que ces deux codes ont le même résultat : afficher les nombres de 0 à 9. En effet, on met des méthodes délégués dans une liste de délégués puis on parcourt cette liste afin d’exécuter le code de ces délégués. La seule différence entre les deux codes est l’utilisation d ‘une variable local j.

    Et pourtant la réponse est :

    • Exemple 1 : 10 10 10 10 10 10 10 10 10 10
    • Exemple 2 : 0 1 2 3 4 5 6 7 8 9

    Autre exemple dans le même genre :

    var actions = new List<Action>();
    string[] urls = 
    { 
       "http://www.url.com", 
       "http://www.someurl.com", 
       "http://www.someotherurl.com", 
       "http://www.yetanotherurl.com" 
    };
    
    for (int i = 0; i < urls.Length; i++)
    {
        actions.Add(() => Console.WriteLine(urls[i]));
    }
    var actions = new List<Action>();
    string[] urls = 
    { 
       "http://www.url.com", 
       "http://www.someurl.com", 
       "http://www.someotherurl.com", 
       "http://www.yetanotherurl.com" 
    };
    
    for (int i = 0; i < urls.Length; i++)
    {
        int j = i;
        actions.Add(() => Console.WriteLine(urls[j]));
    }
    Résultat : IndexOutOfRangeException (Index was outside the

    bounds of the array
    Résultat : les liens du tableaux sont affichés
    • Les lambdas peuvent être nuire gravement à la santé … mentale des développeurs 

    Etrange non ? Décompilons le tout première exemple afin devoir ce qui est effectivement exécuté.

    Note : Pour une raison inconnue, je n’ai pas réussi à obtenir ceci depuis Reflector. J’ai donc récupérer le code de l’article original.

    [CompilerGenerated]
    private sealed class <>c__DisplayClass2
    {
        // Fields
        public int i;
    
        // Methods
        public void <Main>b__0()
        {
            Console.WriteLine(this.i);
        }
    }
    [...]
    var actions = new List<Action>();
    
    <>c__DisplayClass2 localFrame = new <>c__DisplayClass2();
    for (localFrame.i = 0; localFrame.i < 10; localFrame.i++)
    {
    	actions.Add(localFrame.<Main>b__0);
    }
    
    foreach (var action in actions)
    {
    	action();
    }

    Comme on peut le voir, le fait de créer une lambda en utilisant une variable extérieure à celle ci à entrainer la génération d’une classe (<>c__DisplayClass2") qui est utilisé pour stocker la valeur de cette variable (dans la varaible i) et une méthode (<Main>b__0) qui contient le code de la lambda.  Si on regarde le code du premier for, on s’aperçoit que cette boucle incrémente la variable i de la classe DisplayClass2. Donc, à la fin de la première boucle, la classe DisplayClass2 contient une variable i = 10 et la liste actions contient une référence vers la méthode de cette classe. Ainsi lors de la boucle d’appel actions, on se retrouve a appeler le code Console.WriteLine(i) <=> Console.WriteLine(10) !!!!

    Si on résonne de la même façon sur l’exemple 2, on obtient un appel Console.WriteLine(urls[4]) ==> Exception.

    • Pourquoi l’utilisation d’une variable locale résout le problème

    Si on regarde le code ci dessous, on s’aperçoit que le problème vient du fait que la classe DisplayClass2 est créé en dehors de la boucle for et que celle ci utilise la variable i comme compteur. Le fait d’utiliser une variable temporaire à l’intérieur de la boucle for, oblige le compilateur à créer plusieurs instance de DisplayClass (une par itération). On obtient ainsi quelque chose comme le code suivant.

    for (int idx = 0; idx < 10; idx++)

    {

        <>c__DisplayClass2 localFrame = new <>c__DisplayClass2();

        localFrame.i = idx;

        actions.Add(localFrame.<Main>b__0);

    }

    Remarque : On pourrait penser que chaque instance est dispose lors de la sortie du scope. Cela n’’est pas vrai car la liste actiosn conserve une référence sur l’objet (en l’occurence sur une de ses méthodes) donc le garbage collector ne prend pas cet objet.

    • Conclusion

    Les lambdas sont aujourd’hui utilisé dans de nombreuses technologies .NET (LINQ,PFX…) et sont une bonne alternative aux délégués classiques. Toutefois, on peut voir que cela peut engendré une certaine confusion voire des bugs lorsqu’on les utilise.

    Je vous mets le lien vers l’article dont je me suis fortement inspiré pour cet article : Lambdas – Know your closures

    « Newer PostsOlder Posts »

    Créez un site Web ou un blog gratuitement sur WordPress.com.

    %d blogueurs aiment cette page :