XNA : Créer un mini jeu type Asteroid – partie 2

Dans l’article précédent nous avons vue comment mettre en place un projet XNA et comment afficher et déplacer des éléments à l’écran. Là je vous propose de commencer à étoffer un peu notre jeu de shoot en permettant au vaisseau de tirer un laser, nous l’empêcherons aussi de sortir de l’écran gérant au passage les collisions 😉 L’objectif est que quand le joueur appuis sur la barre espace un laser soit tiré vers le haut de l’écran, il ne faut pas que le vaisseau puisse tirer une rafale de laser (pas pour le moment) donc il faudra bien vérifier que celui-ci ne soit plus visible avant que le vaisseau puisse en tirer un autre.

Mise en place du projet

Là c’est très simple on va reprendre le dernier projet disponible dans l’article précédent et lui ajouter une nouvelle ressource graphique : le « giga laser » laser, faites donc un clic droit sur le lien précédent puis enregistrer sous… Enfin copier ce fichier image dans votre dossier Content si vous utilisez MonoGame ou faites le directement glisser sur le projet Content associé à votre projet de jeu si vous utilisez Windows, Visual Studio et XNA.

Mise en place du laser dans le code

Dans le code c’est très simple nous allons ajouter 4 nouveaux attributs privés :

  1. Un objet de type Texture2D qui représentera la texture du laser ;
  2. Une structure de type Vector2 qui sera utiliser pour diriger le laser ;
  3. Une variable de type int qui définira la vitesse du laser (elle est facultative mais il est préférable, dans un soucis de propreté de ne pas entrer trop de valeurs en dur dans le code 😉 ) ;
  4. Une variable de type bool qui nous indiquera si on peu tirer un nouveau laser ou pas.
public class SpaceGame : Game
{
  // Background et vaisseau
  // ...

  // Variables relatives au laser
  Texture2D laser;
  Vector2 laserPosition;
  int laserSpeed;
  bool shooted;

  // Constructeur ...
}

Dans la méthode Initialize() il va maintenant faloir instancier les 2 variables, la vitesse sera fixée à 4 et on pourra tirer un premier laser la première fois.

canShoot = false; // Le laser n'a pas été tiré
laserSpeed = 4; // Vitesse du laser

Au niveau du chargement et déchargement de la texture du laser cela se passe toujours dans les méthodes LoadContent() et UnloadContent()

// Dans LoadContent()
laser = Content.Load("laser");

// Dans UnloadContent()
laser.Dispose();

Passons à la suite, nous devons créer une méthode qui va tirer un laser à l’écran. Cette méthode sera chargée de détecter si on peut ou pas, tirer un nouveau laser à l’écran et si oui initialiser la position du laser à l’écran. Le laser est tiré par le nez du vaisseau, ce qui implique que pour determiner sa position de départ nous devrons prendre en compte :

  1. La position en X et Y du vaisseau
  2. La taille de l’image du vaisseau
  3. La taille de l’image du laser

La position en x du laser est déterminée grâce à la position du vaisseau en x, seulement on ne peux pas écrire simplement positionLaser.X = positionShip.X car le laser sera collé à la gauche du vaisseau (figure 1). Il faut en plus ajouter à sa position la moitié de la largeur du vaisseau (figure 2). Bien entendu pour être bien centré il faut soustraire la moitié de la largeur du laser à cette valeur (figure 3).

position du laser
Position du laser

Pour définir la position en y du laser on prendra la même que celle du vaisseau moins la hauteur de la texture du laser pour que celui ci soit à la bonne hauteur. Ainsi la position de départ du laser sera parfaitement centrée.

private void shoot()
{
	if (!shooted)
	{
		// La position du laser est définie par :
		// - la position en X du vaisseau + la moitier de sa taille
		// - moins la moitier de la taille du laser
		laserPosition = new Vector2(
			(shipPosition.X + (ship.Width / 2)) - laser.Width / 2,
			shipPosition.Y);
		shooted = true; // Le laser est tiré !
	}
}

Cette méthode est très simple, on test dans un premier temps que le joueur peut tirer un laser, si c’est le cas alors la position du laser est positionné au nez du vaisseau. On passe ensuite la variable shooted à true pour indiquer que le joueur a tiré.

Dans la méthode update() nous avons 3 tests à faire :

  1. Si le laser est tiré il faut incrémenter sa position en x pour qu’il monte vers le haut de l’écran
  2. Si le laser est tiré ET qu’il n’est plus visible il faut arrêter de le mettre à jour et permettre au joueur de tirer à nouveau
  3. Si le joueur appuis sur la touche espace, on tire un laser (si c’est possible)

Ce qui nous donne le code suivant :

protected override void Update(GameTime gameTime)
{
	// Si le laser n'est plus visible et qu'il a été tiré
	// On ne le met plus à jour
	if (canShoot && laserPosition.Y + laser.Height <= 0)
	  canShoot = false; // On peut maintenant retirer un autre laser


	// Si le laser a été tiré on le fait monter
	if (canShoot)
	  laserPosition.Y -= laserSpeed;

	// Tire le laser
	if (Keyboard.GetState().IsKeyDown(Keys.Space))
	  shoot();
				
	// Reste du code
	base.Update(gameTime);
}

Enfin il faut afficher le laser si il a été tiré, dans le cas contraire ça ne sert à rien de l'afficher. La mise à jour de la méthode Draw() est donc tout ce qu'il y a de plus simple. On fait un test sur la variable shooted et si elle vaut true alors on affiche le laser.

protected override void Draw(GameTime gameTime)
{
	// Efface l'écran
	graphics.GraphicsDevice.Clear(Color.AliceBlue);

	// Début du mode dessin
	spriteBatch.Begin();

	// Code d'affichage du background et du vaisseau
	// Si le laser est tiré on le dessine
	if (shooted)
		spriteBatch.Draw(laser, laserPosition, Color.White);

	// Fin du mode dessin
	spriteBatch.End();

	base.Draw(gameTime);
}

Le code est très simple à comprendre et vous pourrez constater si vous compilez votre projet maintenant que maintenant on peut tirer un laser à l'écran 😀

Le vaisseau tire un nouveau laser
Notre vaisseau tire un laser

Les collisions

Nous avons déjà géré une sorte de collision puisque quand le laser sort de l'écran on ne l'affiche plus, on va faire le même type de test lors du déplacement du vaisseau pour éviter qu'il sorte de l'écran. Pour cela comme toujours nous utiliserons conjointement les propriétés de shipPosition (Vector2) et ship (Texture2D).

// Déplacement du vaisseau et gestion des collisions avec les bords
if (Keyboard.GetState().IsKeyDown(Keys.Up) && shipPosition.Y > 0)
  shipPosition.Y -= moveSpeed;

if (Keyboard.GetState().IsKeyDown(Keys.Down) && shipPosition.Y + ship.Height < graphics.PreferredBackBufferHeight)
    shipPosition.Y += moveSpeed; 

if (Keyboard.GetState().IsKeyDown(Keys.Left) && shipPosition.X > 0)
  shipPosition.X -= moveSpeed;

if (Keyboard.GetState().IsKeyDown(Keys.Right) && shipPosition.X + ship.Width < graphics.PreferredBackBufferWidth)
  shipPosition.X += moveSpeed;

La détection des bords est très simple, pour ce qui est du haut et de la gauche on test si la position du vaisseau est supérieur à 0 (les coordonnées 0,0 sont en haut à gauche de la fenêtre), dans les autres cas on ajoute au coordonnées courante du vaisseau sa largeur ou sa hauteur pour vérifier qu'on ne sort pas de l'écran 😉

Les collisions avec les bords de l'écran sont maintenant gérées et si vous changez la résolution de l'écran vous n'aurez pas de problèmes car on utilise les propriétés PreferredBackBuffer{Width|Height} de l'objet graphics.

Voilà qui termine ce tutoriel, la prochaine fois nous ferons un peu de refactoring en créent une classe Sprite qui nous facilitera bien la vie croyez moi, on créera aussi un composant graphique pour afficher le nombre d'image par seconde.

Télécharger le projet MonoGame (Windows)