Tutoriel de développement XNA / Partie II : Mon premier jeu avec XNA 4.0 (Part 3)
Si vous souhaitez réaliser un jeu 2D, à un moment ou à un autre vous aurez besoin de créer un Background défilant. Ce tutoriel va vous apprendre à créer un Background défilant horizontalement en auto-scroll, un peu dans le genre shoot’em up, mais le même principe peut-être utilisé pour un jeu de plateforme par exemple.
Nous allons réutiliser la classe Sprite que nous avions créé dans la partie précédente en la modifiant un peu. Ouvrez la solution « MonPermierJeu ».
Ajouter les images
Maintenant ajoutons les images des backgrounds que nous allons utiliser. Vous pouvez télécharger les images du projet ici. Une fois les images téléchargées. Ajoutez-les au projet Content. Si vous ne vous souvenez plus comment faire, consultez la partie précédente pour rafraichir la mémoire : Mon premier jeu avec XNA 4.0 (Part 2).
Modifier la classe Sprite
Pour commencer nous allons faire quelques modifications sur la classe Sprite. Ces modifications vont rendre la classe un peu plus souple et nous permettre d’ajouter plus simplement les backgrounds.
Ajoutez les lignes suivantes en haut de la classe Sprite.
//Taille du Sprite public Rectangle Size; //Utilisé pour augmenter ou diminuer la taille du Sprite public float Scale = 1.0f;
L’objet Size nous permettra de connaitre la hauteur et la largeur de notre sprite et l’objet Scale permet d’augmenter ou diminuer proportionnellement la taille du sprite.
Ensuite nous allons modifier la méthode LoadContent pour calculer et initialiser la taille de notre sprite. Modifiez la méthode LoadContent comme suit.
//Charge la texture du sprite en utilsant le ContentPipeLine public void LoadContent(ContentManager monContentManager, string nomTexture) { _texture = monContentManager.Load(nomTexture); Size = new Rectangle(0, 0, (int)(_texture.Width * Scale), (int)(_texture.Height * Scale)); }
Supprimez la méthode Update de la classe Sprite car celle-ci n’est pas adaptée aux backgrounds. Nous coderons la logique de jeu dans la méthode Update de la classe Game1.
Ensuite nous voulons utiliser une des nombreuses méthodes d’override du SpriteBatch dans la méthode Draw. Appliquez les modifications.
//Affiche le sprite à l’écran public void Draw(SpriteBatch monSpriteBatch) { monSpriteBatch.Draw(_texture, Position, new Rectangle(0,0,_texture.Width,_texture.Height) , Color.White,0.0f,Vector2.Zero,Scale,SpriteEffects.None,0); }
Faite un rapide Debug pour voir si tout compile bien.
Maintenant que tout le travail sur la classe Sprite a été fait, nous allons pouvoir ajouter les backgrounds défilants au projet.
Commençons par ajouter de nouveaux sprites à la classe Game1.cs. Ajoutez les objets suivants en haut de la classe.
Sprite BackgroundOne; Sprite BackgroundTwo; Sprite BackgroundThree; Sprite BackgroundFour; Sprite BackgroundFive;
Ces objets Sprite seront utilisés pour faire défiler les images de background sur l’écran.
A présent, initialisez les nouveaux Sprite. La taille originale du logo étant un peu petite nous allons pouvoir l’augmenter à l’écran grâce à la propriété Scale.
Modifiez la méthode Initialize.
protected override void Initialize() { // TODO: Add your initialization logic here touchCollection = new TouchCollection(); logo = new Sprite(); logo.Scale = 2.0f; BackgroundOne = new Sprite(); BackgroundTwo = new Sprite(); BackgroundThree = new Sprite(); BackgroundFour = new Sprite(); BackgroundFive = new Sprite(); base.Initialize(); }
Maintenant que les sprites sont initialisés, nous devons nous occuper de la méthode LoadContent.
protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); logo.LoadContent(Content, "logo_HD"); logo.Position = new Vector2(240, 400); BackgroundOne.LoadContent(this.Content, "BackgroundOne"); BackgroundOne.Position = new Vector2(0, 0); BackgroundTwo.LoadContent(this.Content, "BackgroundTwo"); BackgroundTwo.Position = new Vector2(BackgroundOne.Position.X + BackgroundOne.Size.Width, 0); BackgroundThree.LoadContent(this.Content, "BackgroundThree"); BackgroundThree.Position = new Vector2(BackgroundTwo.Position.X + BackgroundTwo.Size.Width, 0); BackgroundFour.LoadContent(this.Content, "BackgroundFour"); BackgroundFour.Position = new Vector2(BackgroundThree.Position.X + BackgroundThree.Size.Width, 0); BackgroundFive.LoadContent(this.Content, "BackgroundFive"); BackgroundFive.Position = new Vector2(BackgroundFour.Position.X + BackgroundFour.Size.Width, 0); }
Vous pouvez voir que le contenu de chaque Sprite est chargé avec les images des Backgrounds. Chaque Sprite est positionné en fin de chaine. La première image est positionnée en (0,0), ce qui correspond au coin supérieur gauche de l’écran et les autres suivent sur la droite.
Faire défiler un Background correspond à déplacer les images de background à travers l’écran à la façon d’un train. Quand une image passe outre l’écran sur la gauche, elle est déplacée en fin de queue.
Pour créer cet effet nous allons modifier la méthode Update pour qu’elle vérifie la position de chaque background et la déplace en fin de queue si nécessaire et également effectuer un déplacement régulier vers la gauche des images pour donner cet effet de défilement.
protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); touchCollection = TouchPanel.GetState(); bool buttonTouched = false; //interprete le touch sur l'écran foreach (TouchLocation location in touchCollection) { switch (location.State) { case TouchLocationState.Pressed: buttonTouched = true; break; case TouchLocationState.Moved: buttonTouched = true; break; case TouchLocationState.Released: break; } } if (buttonTouched) { if (touchCollection[0].Position.X < logo.Position.X && logo.Position.X > 0) { logo.Position.X -= 10; } if (touchCollection[0].Position.X > logo.Position.X && logo.Position.X < BackBufferWidth - logo.Size.Width) { logo.Position.X += 10; } if (touchCollection[0].Position.Y < logo.Position.Y && logo.Position.Y > 0) { logo.Position.Y -= 10; } if (touchCollection[0].Position.Y > logo.Position.Y && logo.Position.Y < BackBufferHeight - logo.Size.Height) { logo.Position.Y += 10; } } if (BackgroundOne.Position.X < -BackgroundOne.Size.Width) { BackgroundOne.Position.X = BackgroundFive.Position.X + BackgroundFive.Size.Width; } if (BackgroundTwo.Position.X < -BackgroundTwo.Size.Width) { BackgroundTwo.Position.X = BackgroundOne.Position.X + BackgroundOne.Size.Width; } if (BackgroundThree.Position.X < -BackgroundThree.Size.Width) { BackgroundThree.Position.X = BackgroundTwo.Position.X + BackgroundTwo.Size.Width; } if (BackgroundFour.Position.X < -BackgroundFour.Size.Width) { BackgroundFour.Position.X = BackgroundThree.Position.X + BackgroundThree.Size.Width; } if (BackgroundFive.Position.X < -BackgroundFive.Size.Width) { BackgroundFive.Position.X = BackgroundFour.Position.X + BackgroundFour.Size.Width; } Vector2 aDirection = new Vector2(-1, 0); Vector2 aSpeed = new Vector2(160, 0); BackgroundOne.Position += aDirection * aSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; BackgroundTwo.Position += aDirection * aSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; BackgroundThree.Position += aDirection * aSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; BackgroundFour.Position += aDirection * aSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; BackgroundFive.Position += aDirection * aSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; base.Update(gameTime); }
Le code se décompose en trois sections. Commençons par la première section qui concerne le déplacement du joueur (notre logo).
touchCollection = TouchPanel.GetState(); bool buttonTouched = false; //interprete le touch sur l'écran foreach (TouchLocation location in touchCollection) { switch (location.State) { case TouchLocationState.Pressed: buttonTouched = true; break; case TouchLocationState.Moved: buttonTouched = true; break; case TouchLocationState.Released: break; } } if (buttonTouched) { if (touchCollection[0].Position.X < logo.Position.X && logo.Position.X > 0) { logo.Position.X -= 10; } if (touchCollection[0].Position.X > logo.Position.X && logo.Position.X < BackBufferWidth - logo.Size.Width) { logo.Position.X += 10; } if (touchCollection[0].Position.Y < logo.Position.Y && logo.Position.Y > 0) { logo.Position.Y -= 10; } if (touchCollection[0].Position.Y > logo.Position.Y && logo.Position.Y < BackBufferHeight - logo.Size.Height) { logo.Position.Y += 10; } }
Il s’agit en fait du même code que celui de méthode Update de la classe Sprite du chapitre précédent. Il permet de gérer le déplacement du logo.
Analysons la deuxième section maintenant. Celle-ci permet de garder la « chaine » d’image continue.
if (BackgroundOne.Position.X < -BackgroundOne.Size.Width) { BackgroundOne.Position.X = BackgroundFive.Position.X + BackgroundFive.Size.Width; } if (BackgroundTwo.Position.X < -BackgroundTwo.Size.Width) { BackgroundTwo.Position.X = BackgroundOne.Position.X + BackgroundOne.Size.Width; } if (BackgroundThree.Position.X < -BackgroundThree.Size.Width) { BackgroundThree.Position.X = BackgroundTwo.Position.X + BackgroundTwo.Size.Width; } if (BackgroundFour.Position.X < -BackgroundFour.Size.Width) { BackgroundFour.Position.X = BackgroundThree.Position.X + BackgroundThree.Size.Width; } if (BackgroundFive.Position.X < -BackgroundFive.Size.Width) { BackgroundFive.Position.X = BackgroundFour.Position.X + BackgroundFour.Size.Width; }
On pourrait pût vouloir déplacer en premier lieu les backgrounds avant de vérifier leur position. Mais comme souvent dans le jeu vidéo, l’ordre d’exécution est très important. Si l’on avait développé sous cette logique, un décalage entre les images de background se serait formé.
Voyons maintenant la dernière partie de code de cette méthode.
Vector2 aDirection = new Vector2(-1, 0); Vector2 aSpeed = new Vector2(160, 0); BackgroundOne.Position += aDirection * aSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; BackgroundTwo.Position += aDirection * aSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; BackgroundThree.Position += aDirection * aSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; BackgroundFour.Position += aDirection * aSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; BackgroundFive.Position += aDirection * aSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; base.Update(gameTime);
Le vecteur aDirection détermine dans quel sens le défilement va s’effectuer en X et en Y avec -1 pour la gauche et 1 pour la droite. Ici (-1,0) signifie un déplacement uniquement horizontal vers la gauche.
Le vecteur aSpeed indique la vitesse de défilement du background en X et en Y. Ici on a une vitesse horizontale de 160 pixel/s et de 0 pixel/s verticalement.
Pour obtenir la nouvelle position il suffit donc de multiplier la direction par la vitesse et d’ajouter le tout à l’ancienne position.
Fin des explications pour cette méthode. Il ne reste plus qu’à nous occuper de la méthode Draw. Modifiez la méthode en fonction des lignes suivantes.
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend); BackgroundOne.Draw(this.spriteBatch); BackgroundTwo.Draw(this.spriteBatch); BackgroundThree.Draw(this.spriteBatch); BackgroundFour.Draw(this.spriteBatch); BackgroundFive.Draw(this.spriteBatch); logo.Draw(spriteBatch); spriteBatch.End(); base.Draw(gameTime); }
Lancez le Debug, l’émulateur charge le projet et vous devez maintenant voir apparaître le logo Tequila Rapido dans les nuages, volant tel superman à travers le ciel.
Vous pouvez voir ce que donne le résultat dans cette vidéo
Les sources du projet sont disponibles en téléchargement ici.
































Salut ,la fonctionne load content me produit une erreur et je ne peux téléchargé les images. (superbe tuto !)