Space shooter : Aron & The Aliens

Chapitre 24 : Les collisions (4ème partie) !

 

Tutoriel présenté par : Robert Gillard (Gondulzak)
Date d'écriture : 27 octobre 2014

Dernière mise à jour : 22 novembre 2015

 

      Préliminaires

   Dans notre précédent chapitre, nous avons implémenté deux nouvelles classes, les classes «Fireballs» et «Missils», se rapportant aux collisions fireballs / ennemis et astronef / missils. Jusqu'ici nous n'avions pas encore généré de collisions entre les astéroïdes et l'astronef et nous allons entre autres le faire dans ce chapitre. smiley

   Les collisions astronef /astéroïdes vont se révéler mortelles pour Aron car un seul choc avec un astéroïde va immédiatement renvoyer l'astronef vers une nouvelle partie. Le joueur devra donc se montrer particulièrement prudent envers ces débris flottants et devra effectuer des slaloms pour tâcher de les éviter tout en essayant également d'éviter les tirs ennemis. surprise

   Cependant l'astronef ne sera pas démuni face à ce danger. En effet nous avons vu que la prise d'un power-up de santé (tonneau vert) allait par la même occasion donner des super fireballs à l'astronef, super fireballs qui pourront exploser un astéroïde en 8 coups tout en doublant la quantité de PO reçus ainsi que le score acquis. cool

   Et sans plus tarder, nous nous lançons dans la suite du code de nos collisions et en premier, par la gestion des collisions astronef / astéroïdes.

 

Le projet « AronAndTheAliens14 »

 

   Reprenez votre projet précédent ou créez-en un nouveau que vous nommerez «AronAndTheAliens14» (reportez-vous aux chapitres précédents en ce qui concerne les manipulations à réaliser).

 

 

     1 – Collisions astronef / astéroïdes : Le code

   Dans la région COLLISIONS UPDATE du fichier AronAndTheAliens14.cs, à la suite de la sous-région COLLISIONS ASTRONEF / MISSILS, entrez la sous-région suivante :

 
#region COLLISIONS ASTRONEF / ASTEROID
// Collision Astronef - Astéroide
for (int i = 0; i < asteroids.Count; i++)
{
 
asteroidBox = new Rectangle((int)asteroids[i].Position.X + 10,
(int)asteroids[i].Position.Y + 10,
asteroids[i].frameWidth - 20,
asteroids[i].frameHeight - 20);
 
// Determine si un astéroide est entré en collision avec l'astronef
if (astronefBox.Intersects(asteroidBox))
{
// Si collision
// Pas de pitié, une collision astéroide / astronef = astronef détruit !
hero.health = 0;
 
asteroids[i].Health = 0; // pour l'enlever de la liste par précaution!!
 
// Destruction du joueur
hero.killedByAsteroid = true;
hero.killedByAlien = false;
hero.noMoreEnergy = false;
hero.Active = false;
hero.onCombatWindow = false;
}
}
#endregion COLLISIONS ASTRONEF / ASTEROID

 

   Vous voyez que le code n'est pas long, nous allons le commenter quelque peu. wink

   Premièrement nous créons les hitboxes des astéroïdes présents à l'écran et un simple test que vous connaissez déjà détermine si l'astronef est entré en collision avec un astéroïde ou pas. Et comme je vous l'ai signalé plus haut, l'astronef est aussitôt détruit, nous mettons ses points de santé à 0. indecision

   Les points de santé de l'astéroïde incriminé sont également remis à 0 car nous savons qu'un test sur celui-ci dans le fichier Update() de la classe «Asteroids» mettra automatiquement la variable booléenne «Active» à false et enlèvera cet astéroïde de la liste.

   Ensuite, une nouvelle variable booléenne que nous déclarerons plus loin, «killedByAsteroid», indiquant si notre héros a été tué ou non par un astéroïde est mise à true. Les autres étant naturellement toutes portées à false.

   Et nous poursuivons maintenant avec le code des collisions super fireballs / astéroïdes

 

     2 – Collisions super fireballs / astéroïdes : le code

Et à la suite de la sous-région COLLISIONS ASTRONEF / ASTEROID, que vous venez d'écrire, entrez la sous-région suivante : 

 
#region COLLISIONS SUPER FIREBALLS / ASTEROID
// Collisions Super Fireballs - Astéroides
if (hero.gotHealth == true)
{
for (int i = 0; i < fireballs.Count; i++)
{
for (int j = 0; j < asteroids.Count; j++)
{
// Creation des rectangle pour déterminer une collision
//Superfireball / Asteroid
fireballBox = new Rectangle((int)fireballs[i].Position.X -
fireballs[i].frameWidth / 2, (int)fireballs[i].Position.Y -
fireballs[i].frameHeight / 2, fireballs[i].frameWidth,
fireballs[i].frameHeight);
 
asteroidBox = new Rectangle((int)asteroids[j].Position.X -
asteroids[j].frameWidth / 2,
(int)asteroids[j].Position.Y - asteroids[j].frameHeight / 2,
asteroids[j].frameWidth, asteroids[j].frameHeight);
 
// Determine si un asteroide est entré en collision avec un
//Superfireball
if (asteroidBox.Intersects(fireballBox))
{
 
asteroids[j].Health -= fireballs[i].superDamage;
asteroids[j].Position.X -= 5; // Un recul dû au choc
 
// Si un astéroide est désintégré
if (asteroids[j].Health <= 0 && asteroids[j].Active)
{
//Aron s'empare s'empare de 50 gold
hero.gold += asteroids[j].goldAmount;
//Et le score est crédité de 100 unités
hero.score += asteroids[j].scoreAmount;
// Ajout d'une animation explosion
AddExplosion(asteroids[j].Position, gameTime);
 
asteroids[j].Active = false;
}
 
fireballs[i].Active = false;
}
}
}
}
#endregion COLLISIONS SUPER FIREBALLS / ASTEROID
 

   Dans cette gestion de collisions, nous devons premièrement savoir si oui ou non nous venons d'acquérir un power-up de santé (tonneau vert) puisque c'est grâce à celui-ci que nous obtenons nos super fireballs. wink

   Nous faisons donc un premier test pour voir si la variable booléenne gotHealth est bien égale à true. Si ce n'est pas le cas on sort immédiatement de cette région. Si la variable gotHealth est true, nous entrons dans une double boucle.

   Après avoir déterminé nos hitboxes de collision, nous voyons que pour chaque super fireball tirée, on parcourt toute la liste des astéroïdes pour voir si un astéroïde est entré en collision avec cette super fireball et on passe a la super fireball suivante. Un choc super fireball / astéroïde va donc causer 80 pts de dégats (variable superDamage) à un astéroïde et comme le total des points de santé d'un astéroïde est de 800 (voir classe Asteroids), il faudra donc que l'astronef tire 8 super fireballs pour désintégrer un astéroïde. Nous voyons également qu'un choc super fireball / astéroïde provoque un recul de 5 pixels de cet astéroïde. 

   Dès qu'il y a désintégration d'un astéroïde, les PO du héros sont crédités de 60 pièces tandis que le score augmente de 140 unités. La variable Active de la classe Asteroids est mise à false et l'astéroïde est aussitôt retiré de la liste des astéroïdes. Et il en va de même pour chaque super fireball ayant heurté un astéroïde.

   C'est tout pour ces deux types de collisions, nous allons maintenant procéder à un ajout à réaliser dans la classe Héro où nous devons déclarer la variable booléenne «killedByAsteroid».

   Et dans la classe Hero, dans la région Déclaration des variables de la classe Hero, en dessous de la ligne: 

 

En-dessous de :
public bool killedByAlien;
Ajoutez :
// Si Astronef pulvérisé par un astéroide
public bool killedByAsteroid;  

 

   Ensuite, dans la fonction Initialize() de cette même classe Hero, en dessous de la ligne :

 
En-dessous de :
killedByAlien = false;
Ajoutez :
killedByAsteroid = false;
 

   Notre astronef étant bien entendu intact au départ. cheeky


   Dans le chapitre précédent j'avais signalé que je procéderais à une petite modification sur la fréquence de tir des fireballs de l'astronef selon que ceux-ci sont des super fireballs ou de simples fireballs et de même en ce qui concerne les missiles ou super missiles de l'ennemi.

   Je pense qu'il n'est pas utile de compliquer ce projet au niveau de la fréquence des tirs, mais à la place, j'aimerais que la prise d'un ou l'autre power-up ne puisse se faire que si les touches de tir sont relâchées. wink

   Voici donc les modifications à effectuer dans le code pour arriver à ce résultat :

   Dans la classe «Hero», à l'intérieur de la région Déclaration des variables de la classe Hero, en dessous de la ligne : 

 
En-dessous de :
public bool killedByAsteroid;
Ajoutez :
// Si le joueur presse la touche "space" ou le bouton "X"
public bool isShooting;

 

  Ensuite, dans la fonction Initialize() de cette même classe Hero, en-dessous de la ligne :

 
En-dessous de :
killedByAsteroid = false;
Ajoutez :
// Pas de tir au départ
isShooting = false;
 

  Nous allons maintenant ouvrir notre fichier AronAndTheAliens14.cs car nous avons plusieurs ajouts à y faire.


     3 – Fichier AronAndTheAliens14.cs

Dans ce fichier, à l'intérieur de la région HERO UPDATE FUNCTION, remplacez le code relatif à l'appui de la touche «Space» ou du bouton «X» par celui-ci : 

 
// La touche SPACE du clavier ou le bouton X du gamepad
//servent au joueur pour tirer sur l'ennemi
if (currentKeyboardState.IsKeyDown(Keys.Space) ||
(currentGamePadState.Buttons.X == ButtonState.Pressed))
{
 
//mode tir si touche pressée
hero.isShooting = true;
 
// Ne tire que dans l'intervalle de temps fixé
if (gameTime.TotalGameTime - previousFireballTime > fireballTime)
{
// Réinitialisation du temps
previousFireballTime = gameTime.TotalGameTime;
 
// On ajoute le projectile avec une origine réaliste par rapport
// à l'astronef
AddFireball(hero.Position - new Vector2(hero.frameWidth / 2, 0));
 
}
}
else
//on n'est plus en mode tir dès que la touche est relachée
hero.isShooting = false;
 

   Code dans lequel nous mettons notre variable isShooting à true si effectivement nous appuyons sur les touches de tir et dans le cas où nous ne sommes plus en mode tir, nous passons cette variable à false.

   Il nous reste à nous rendre dans la région COLLISION ASTRONEF / POWER_UPS pour indiquer que nous ne prendrons pas le power-up sur lequel se trouve l'astronef tant que nous serons en mode tir.
   Donc à la suite du test qui détermine si l'astronef est entré en collision avec un power-up, nous ajoutons simplement un test sur la variable isShooting (je ne vous montre ici que les premières lignes wink

 
// Determine si l'astronef est entré en collision avec un power-up
if (astronefBox.Intersects(barrelBox))
{
if (hero.isShooting == false)
{
if (hero.gold >= 200)
{
..........
 

 

   Voilà, nous pouvons refaire des tests de tirs et nous voyons maintenant que nous ne pouvons plus nous emparer d'un power-up si nous ne nous arrêtons pas de tirer le temps de cette acquisition. wink


   Bien, nous allons faire un autre ajout dans notre fichier AronAndTheAliens.cs. Jusqu'à présent, après chaque destruction de l'astronef, si vous tardez quelque peu à recommencer une partie, vous devriez vous apercevoir que le jeu continue à se dérouler car de temps à autre, un nouveau type de mort de notre héros s'affiche ! surprise

   Comment éviter ceci ? frown Eh bien c'est assez simple, nous allons bloquer le déroulement du programme après la mort de notre héros. wink

   Dans la région FUNCTION UPDATE, après le test qui démarre le jeu en pressant la touche «Enter» ou le bouton «Start», ajoutez le test suivant : 

 
if (hero.onCombatWindow == true)
{
//Mise à jour du background et des images défilantes
..........
 
}
 
}
#endregion FUNCTION UPDATE
 

   Ok, pendant que nous sommes dans nos petites modifications il y a une chose que que voudrais encore ajouter. Afin d'éviter d'éventuelles erreurs qui pourraient se produire dans toutes nos collisions, je voudrais retranscrire dans sa classe une variable booléenne que j'ai déclarée dans le fichier AronAndTheAliens14.cs.

   Donc à l'intérieur de ce fichier, supprimez la variable bool isEnergyBarrel, que vous trouverez dans la région POWER_UP DECLARATIONS DATA'S, car nous allons la reporter dans la classe «Barrels»
   Et dans la classe Barrels, dans la région Déclaration des variables de la classe Barrels, en dessous de la ligne :

 
En-dessous de :
float barrelMoveSpeed;
Ajoutez :
//Power-up type
public bool isEnergyBarrel;
 

   Et dans la fonction Initialize() de la même classe, en dessous de :

 
En-dessous de :
oneUnit = 1;
Ajoutez :
// Au départ, pas de power-up acquis
isEnergyBarrel = false;

 

   Pour utiliser cette variable dans nos collision, nous allons créer une instance de la classe «Barrels».

   Dans la région CLASSES REFERENCES, en dessous de la ligne :

 
En-dessous de :
Missils missiles;
Ajoutez :
Barrels powerUp;
 

   Et dans la fonction Initialize(), juste en dessous de :

 
En-dessous de :
barrels = new List<Barrels>();
Ajoutez :
//Instanciation de la classe barrels
powerUp = new Barrels();

 

   Maintenant, dans la région COLLISION ASTRONEF / POWER_UPS, remplacez la ligne :

 
Remplacez la ligne :
if (isEnergyBarrel == true)
Par :
if (powerUp.isEnergyBarrel == true)
 

   Et la ligne :

 
Remplacez la ligne :
else if (isEnergyBarrel == false)
Par :
else if (powerUp.isEnergyBarrel == false)
 

   Et voilà en ce qui concerne nos premiers ajouts à effectuer dans notre fichier AronAndTheAliens14.cs. Mais nous allons devoir également afficher un nouveau type de mort de notre héros. Eh oui, souvenez-vous qu'il peut se faire exploser par un astéroïde, maintenant... indecision

   Nous allons donc nous rendre dans notre classe «Scripts» et dans la fonction TypeOfDeath() en particulier. Et dans cette fonction, à la suite du test: 

 
En-dessous de :
if (hero.killedByAlien)
{
..........
 
}
Ajoutez :
if (hero.killedByAsteroid)
{
spriteBatch.DrawString(font2,
"The astronef has scratched on an asteroid !",
new Vector2(this.Window.ClientBounds.Width / 2 -
font2.MeasureString(
"The astronef has scratched on an asteroid !").X / 2,
220), Color.White);
}
 

   Et voilà, quand l'astronef s'écrasera sur un astéroïde, notre fenêtre indiquant ce type de mort de notre héros nous le fera savoir. angel

 

    4 – Des Satellites dans l'Espace

   Si vous vous souvenez du chapitre 21, vous devez vous rappeler qu'un satellite traversait la fenêtre de combat dans le sens droite-gauche. Mais dans ce chapitre, notre satellite en question n'avait aucune interaction avec d'autres éléments du jeu (quel petit feignant... laugh) or, quitte à le laisser se balader dans notre fenêtre, autant qu'il serve à quelque chose, ne croyez-vous pas ? cheeky

   Nous avions également un affichage en temps réel de la position des planètes renseigné sur le côté gauche de notre fenêtre. Cet affichage me semblait important, surtout en ce qui concerne la position de la Terre par rapport à la fenêtre et ce, afin de se rendre compte de l'éloignement de l'astre et de la distance restante jusqu'au moment où l'astronef pourrait se recharger en énergie et en boucliers.

   C'est ici que nous allons mettre notre satellite au travail car sa présence sur la scène nous renseignera les positions énumérées ci-dessus et si une vilaine sorcière parvient à l'exploser, il n'y aura bien entendu plus rien à afficher jusqu'à l'apparition d'un... nouveau satellite car ici aussi nous allons utiliser une liste pour la gestion des satellites. wink Et maintenant, oubliez la classe «Satellite» écrite au chapitre 21 car nous allons en écrire une autre. Et oui, pour le travail que nous allons demander à nos satellites il nous faut remplacer cette classe par cette autre cool :


     Une nouvelle classe «Satellite»

Notre image satellite sera désormais une petite feuille de deux frames car nous allons animer nos nouveaux satellites suivant l'image ci-après :

   N'oubliez pas de remplacer l'ancienne image satellite.png par celle-ci dans votre dossier de textures si ce n'est déjà fait.

   Maintenant, supprimez tout l'intérieur de l'ancienne classe «Satellite» et remplacez-le par le code suivant :

 
#region Description
//MERUVIA XNA TUTORIALS
//AronAndTheAliens14
//Game : Aron and the aliens
//Les collisions (partie IV)
//Collisions Astronef / Astéroïdes, Super fireballs / Astéroïdes et satellites / missils
//Fichier : Satellite.cs
//Last update : 25/10/2014
#endregion
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
 
namespace AronAndTheAliens14
{
class Satellite
{
#region Déclaration des variables de la classe Satellite
 
// Un effet sur le sprite Satellite
public SpriteEffects SpriteEffet;
 
// L'animation qui représente le Satellite
public Animation SatelliteAnimation;
 
// Points de santé du satellite
public int Health;
 
// La position du satellite relative au coin supérieur droit de l'écran
public Vector2 Position;
 
// Etat du satellite
public bool Active;
 
// Vitesse de déplacement du satellite
float satelliteMoveSpeed;
#endregion Déclaration des variables de la classe SpaceForce
 
 
#region Propriétés
// Renvoie la largeur du satellite
public int frameWidth
{
get { return SatelliteAnimation.FrameWidth; }
}
 
// Renvoie la hauteur du satellite
public int frameHeight
{
get { return SatelliteAnimation.FrameHeight; }
}
#endregion Propriétés
 
 
#region Fonction d'initialisation
public void Initialize(Animation animation, Vector2 position)
{
 
// Charge la texture du satellite
SatelliteAnimation = animation;
 
// Initialise la position du satellite
this.Position = position;
 
//Le satellite se dirige dans le sens des frames de
//la feuille de sprites
SpriteEffet = SpriteEffects.None;
 
//Initialisation des points de santé
Health = 5;
 
// Le "drapeau" Active est mis à true pour permettre au satellite
// de de se présenter en début du jeu
Active = true;
 
// Vitesse de la Space Force
satelliteMoveSpeed = 0.75f;
 
}
#endregion Fonction d'initialisation
 
 
#region Fonction de mise à jour
public void Update(GameTime gameTime)
{
// Un satellite se dirige toujours de la droite vers la gauche
Position.X -= satelliteMoveSpeed;
 
// Mise à jour de la position de l'animation
SatelliteAnimation.Position = Position;
SatelliteAnimation.spriteEffet = SpriteEffet;
 
// Mise à jour de l'animation
SatelliteAnimation.Update(gameTime);
 
// Si le satellite sort de l'écran par la gauche on la désactive
if ((Position.X < -1024) || (Health <= 0))
{
// En mettant le "drapeau" Active à false, l'objet Space Force
//est enlevé de la liste
Active = false;
}
}
#endregion Fonction de mise à jour
 
 
#region Fonction de dessin
public void Draw(SpriteBatch spriteBatch)
{
// Dessine l'animation
SatelliteAnimation.Draw(spriteBatch);
}
#endregion Fonction de dessin
}
}
 

    Cette classe n'a rien de différent des précédentes classes d'animations vues jusqu'ici, je ne ferai donc qu'une ou deux remarques. wink

   J'ai initialisé les points de santé du satellite avec une valeur de 5, ce qui veut dire qu'un simple missile détruira un satellite. indecision J'ai d'autre part quelque peu modifié les conditions de désactivation d'un satellite dans la liste.

   En effet, si l'un d'entre eux est aussitôt enlevé de la liste dès que ses points de santé sont à 0, vous voyez que je fais voyager un satellite sur une distance de 1024 pixels vers la gauche de l'écran avant de le désactiver si jamais il avait traversé l'écran sans se faire détruire par un alien. Ceci est fait dans le but de continuer à afficher un peu plus longtemps la position des planètes sur l'écran. Il est bien entendu que ces affichages cessent immédiatement dès l'explosion d''un satellite ou dès que celui-ci aura atteint une distance de -1024 pixels par rapport au début de l'écran. Il va de soit que la création d'un nouveau satellite réaffichera de nouveau les positions des planètes à l'écran. wink

   Avant de nous rendre dans notre fichier «AronAndTheAliens14.cs», il nous faut premièrement faire une petite modification dans la classe «Planets». Et oui, ce chapitre 24 sera celui du «grand chambardement» mais croyez-moi, notre gameplay n'en sera que renforcé. angel

   Ouvrez le fichier «Planets.cs» et à la fin de la classe, supprimez toutes les lignes qui affichent en temps réel la position des planètes dans leur champ de déplacement (vous pouvez faire un copier/coller et le sauver quelque part, nous allons réafficher ces lignes à un autre endroit un peu plus loin). Et dans la classe «Planets» nous avions déjà écrit les propriétés permettant d'accéder aux positions de la Terre et de Jupiter mais comme nous aurons bientôt besoin de connaître les positions de tous les astres, nous allons compléter notre série d'accesseurs par l'ajout de ceux-ci : 

 
/* Et pour pouvoir retrouver les positions de toutes nos planètes,
nous ajoutons les accesseurs suivants : */
public Vector2 GalaxyPosition
{
get { return galaxyPosition; }
}
 
public Vector2 BigAsteroidPosition
{
get { return bigAsteroidPosition; }
}
 
public Vector2 VenusPosition
{
get { return venusPosition; }
}
 
public Vector2 MoonPosition
{
get { return moonPosition; }
}
 

   Nous retournons maintenant dans le fichier «AronAndTheAliens14.cs» afin d'y apporter les modifications qui s'imposent.


     Retour dans «AronAndTheAliens14.cs»

   Dans la région CLASSES REFERENCES, supprimez la ligne :

 
Satellite satellite;
 

   Et en dessous de la ligne :

 
En-dessous de :
Barrels powerUp;
Ajoutez :
Satellite satellit;

 

   Ensuite, en dessous de la région POWER_UP DECLARATIONS DATA'S, entrez la région suivante :

 
#region SATELLITE DECLARATIONS DATA'S
 
Texture2D satelliteTexture; //Texture Satellite
Random rand3 = new Random(); //Variable d'élément aléatoire
List<Satellite>satellite; //Les satellites seront gérés dans une liste
public bool isDestroyedByAlien;
 
// La fréquence à laquelle apparait un Satellite
TimeSpan satelliteSpawnTime;
TimeSpan previousSatelliteSpawnTime;
 
#endregion SATELLITE DECLARATIONS DATA'S

 

   Maintenant, dans la région HITBOX COLLISIONS DECLARATIONS, ajoutez la ligne suivante comme dernière ligne de la région :

 

Rectangle satelliteBox; //hitbox pour un satellite
 

   Nous nous rendons maintenant dans la fonction Initialize(). En dessous de la ligne :

 
En-dessous de :
missiles = new Missils();
Ajoutez :
//Création d'une liste de satellites
satellite = new List<Satellite>();
 
//Création d'une instance de Satellite
satellit = new Satellite();
 
//Au départ, le satellite est Ok.
isDestroyedByAlien = false;

 

     Et dans l'initialisation des différents Timers à 0, ajoutez la ligne suivante :

 
previousSatelliteSpawnTime = TimeSpan.Zero;
 

   Toujours dans la fonction Initialize(), en desoous de la ligne :

 
En-dessous de :
missilsFireTime = TimeSpan.FromSeconds(0.55f);
Ajoutez :
// Un satellite est généré toutes les 60 secondes
satelliteSpawnTime = TimeSpan.FromSeconds(60.0f);
 

   Nous passons maintenant à la fonction LoadContent(). Dans la région CLASSES INSTANCES, supprimez la ligne :

 
satellite = new Satellite(Content);
 

   Et dans la région TEXTURES, en dessous de la ligne :

 
En-dessous de :
superMissilTexture = Content.Load<Texture2D>("Textures/EnemySuperMissile");
Ajoutez :
satelliteTexture = Content.Load<Texture2D>("Textures/Satellite");

 

   Nous passons maintenant à la région RESET VARIABLES TO START AGAIN. En dessous de la ligne :

 
En-dessous de :
missiles.isSuperMissil = false;
Ajoutez :
isDestroyedByAlien = false;

 

   Et en dessous de la région UPDATE MISSIL FUNCTION, entrez les deux régions suivantes :

 
#region ADD SATELLITE TO LIST
private void AddSatellite()
{
// Création d'un satellite animé
Animation satelliteAnimation = new Animation();
 
// On initialise l'animation par rapport aux données de la feuille de sprites
// et le temps que nous désirons appliquer entre chaque frame
satelliteAnimation.Initialize(satelliteTexture,
Vector2.Zero,
128,
116,
2,
500,
Color.White,
0.75f,
true);
 
Vector2 position = new Vector2(GraphicsDevice.Viewport.Width, rand3.Next(50,
GraphicsDevice.Viewport.Height - 116));
 
//Crétation d'un satellite
Satellite satellit = new Satellite();
 
// Initialisation d'un ennemi par rapport à sa position
satellit.Initialize(satelliteAnimation, position);
 
// Ajout du satellite à sa liste active
satellite.Add(satellit);
 
}
#endregion ADD SPACE FORCE TO LIST
 
 
 
#region SATELLITE UPDATE LIST FUNCTION
private void UpdateSatellite(GameTime gameTime)
{
// Génération d'un nouveau Satellite
if (gameTime.TotalGameTime - previousSatelliteSpawnTime >
satelliteSpawnTime)
{
previousSatelliteSpawnTime = gameTime.TotalGameTime;
 
// Ajoute un satellite
AddSatellite();
isDestroyedByAlien = false;
}
 
// Mise à jour des satellites
for (int i = satellite.Count - 1; i >= 0; i--)
{
satellite[i].Update(gameTime);
 
// Si l'état est inactif
if (satellite[i].Active == false)
{
isDestroyedByAlien = true;
satellite.RemoveAt(i);
}
}
}
#endregion SATELLITE UPDATE LIST FUNCTION
 

   Il s'agit simplement des fonctions d'ajout et de mise à jour de satellites, comme nous l'avons toujours fait jusqu'à présent pour nos animations précédentes.

   Nous allons maintenant nous rendre dans la fonction UpdateCollisions(GameTime gameTime) afin d'y écrire l'implémentation des collisions missils / satellite. Donc dans la région COLLISIONS UPDATE, en dessous de la sous-région COLLISIONS SUPER FIREBALLS / ASTEROID, entrez la sous-région suivante :

 
#region COLLISIONS SATELLITE / MISSILS
// Collisions satellite - missiles
for (int j = 0; j < enemies.Count; j++)
{
for (int i = 0; i < missils.Count; i++)
{
 
missilBox = new Rectangle((int)missils[i].Position.X,
(int)missils[i].Position.Y,
missils[i].frameWidth,
missils[i].frameHeight);
 
for (int k = 0; k < satellite.Count; k++)
{
satelliteBox = new Rectangle((int)satellite[k].Position.X,
(int)satellite[k].Position.Y,
satellite[k].frameWidth,
satellite[k].frameHeight);
 
// Determine si l'astronef est entré en collision avec
// un missile
if ((satellite[k].Position.X < 672) &&
(satellite[k].Position.X > 128) &&
(satelliteBox.Intersects(missilBox)))
{
//Un missile ordinaire suffit à détruire un satellite
satellite[k].Health -= missils[i].Damage;
 
//Si le missile est entré en collision avec le satellite,
// le missile est détruit
missils[i].Active = false;
 
/ / Ajout d'une animation explosion
AddExplosion(satellite[k].Position, gameTime);
 
// Si les PV du satellite sont <= 0, celui-ci est détruit
if (satellite[k].Health < 0)
{
satellite[k].Health = 0;
satellite[k].Active = false;
 
// La perte d'un satellite vous coûtera 500 PO
hero.gold -= 500;
 
if (hero.gold < 0)
hero.gold = 0;
}
}
}
}
}
#endregion COLLISIONS SATELLITE / MISSILS
 

   Dans cette région nous créons les hitboxes des missiles de nos ennemis et celles des satellites. wink

   Nous testons ensuite s'il y a collision entre un missile et un satellite, mais comme vous le voyez, cette collision ne peut se faire qu'à l'intérieur de la fenêtre dans les limites que j'ai fixées. La valeur 672 est bien entendu la position de l'extrême droite de la fenêtre (800), de laquelle je soustrais la largeur d'un satellite (128). D'autre part, comme les points de santé d'un satellite ne sont que de 5, un seul tir de missile suffit à le détruire et provoque une explosion. Ceci aura pour conséquence le retrait d'une valeur de 500 PO des PO du héros, pour autant qu'il possède cette somme bien entendu indecision, sinon celle-ci sera remise à 0. Vous voyez que vous aurez intérêt à bien défendre vos satellites dès que ceux-ci se présenteront dans la fenêtre de combat ! surprise

   Mais il nous faut maintenant retourner dans la région ENEMIES UPDATE LIST FUNCTION car dans la fonction UpdateEnemies(GameTime gameTime) nous devons dire à nos sorcières «Attention ! Un satellite se présente devant vous, tirez-lui un missile..." Donc dans la fonction UpdateEnemies(), à la suite de la ligne :

 
AddMissil(enemies[i].Position + new Vector2(enemies[i].frameWidth - 50, enemies[i].frameHeight / 3));

 

Il nous faut ouvrir une nouvelle boucle qui va parcourir l'ensemble des satellites. Donc, à cet endroit, ajoutez le code suivant :

 
for (int j = satellite.Count - 1; j >= 0; j--)
 
//Ajout d'un missile, mais qui ne sera tiré qu'en face du satellite
if (((int)enemies[i].Position.Y + enemies[i].frameHeight / 2 >
(int)satellite[j].Position.Y) &&
((int)enemies[i].Position.Y + enemies[i].frameHeight / 2 <
(int)satellite[j].Position.Y + satellite[j].frameHeight - 32))
{
AddMissil(enemies[i].Position +
new Vector2(enemies[i].frameWidth - 50,
enemies[i].frameHeight / 3));
}
 

   Ce qui aura pour effet d'ajouter un missile chaque fois qu'un alien se présentera devant un satellite de la même manière que nous l'avons fait dès que l'astronef se présentait en face d'un ennemi.      

   Maintenant, dans la fonction Update(), en dessous de la ligne :

 
En-dessous de :
UpdateMissils(gameTime);
Ajoutez :
// Mise à jour des satellites
UpdateSatellite(gameTime);
 

   Il nous reste notre dernière fonction à compléter, la fonction Draw(). Car si vous vous souvenez que je vous ai fait supprimer toutes les lignes qui affichent en temps réel la position des planètes dans leur champ de déplacement à l'intérieur de la classe «Planets» (c'est déjà loin tout ça... cheeky), il nous faut maintenant les réécrire. C'est dans la fonction Draw() que nous allons le faire, et ce, grâce à tous les accesseurs «get» que nous avons ajoutés à l'intérieur de cette classe.

   Et en dessous du code qui dessine les barrels, entrez le code suivant :

 
// Dessine les satellites
for (int i = 0; i < satellite.Count; i++)
{
satellite[i].Draw(spriteBatch);
}
 
// Affiche la position en temps réel des planètes dans leur
// champ de déplacement
if (isDestroyedByAlien == false)
{
spriteBatch.DrawString(font3, "Galaxy : " +
(int)planets.GalaxyPosition.X,
new Vector2(5, 280), Color.Green);
 
spriteBatch.DrawString(font3, "Asteroid : " +
(int)planets.BigAsteroidPosition.X,
new Vector2(5, 300), Color.Green);
 
spriteBatch.DrawString(font3, "Venus : " + (int)planets.VenusPosition.X,
new Vector2(5, 320), Color.Green);
 
spriteBatch.DrawString(font3, "Earth : " + (int)planets.EarthPosition.X,
new Vector2(5, 340), Color.Green);
 
spriteBatch.DrawString(font3, "Moon : " + (int)planets.MoonPosition.X,
new Vector2(5, 360), Color.Green);
 
spriteBatch.DrawString(font3, "Jupiter : " +
(int)planets.JupiterPosition.X,
new Vector2(5, 380), Color.Green);
}
else if (isDestroyedByAlien == true)
{
// Affiche la position en temps réel des planètes dans leur
// champ de déplacement
spriteBatch.DrawString(font3, "Galaxy : " + "????",
new Vector2(5, 280), Color.Green);
 
spriteBatch.DrawString(font3, "Asteroid : " + "????",
new Vector2(5, 300), Color.Green);
 
spriteBatch.DrawString(font3, "Venus : " + "????",
new Vector2(5, 320), Color.Green);
 
spriteBatch.DrawString(font3, "Earth : " + "????",
new Vector2(5, 340), Color.Green);
 
spriteBatch.DrawString(font3, "Moon : " + "????",
new Vector2(5, 360), Color.Green);
 
spriteBatch.DrawString(font3, "Jupiter : " + "????",
new Vector2(5, 380), Color.Green);
 

   Dans un premier temps, nous dessinons tous nos satellites, et ensuite, en fonction de l'état du booléen isDestroyedByAlien, nous écrivons soit les positions en temps réel des différentes planètes dans notre fenêtre, soit des points d'interrogation, indiquant qu'un satellite vient de se faire toucher par un missile. wink

   Voilà, j'espère ne rien avoir oublié. cheeky En tout cas je vous conseille de télécharger le projet relatif à ce chapitre et de vous amuser à faire voyager l'astronef dans ce monde hostile. Nous en aurons bientôt fini avec notre jeu et dans le chapitre 25, nous implémenterons les combats d'arrière-plan entre la MSF et les ovnis (faudra bientôt quatre zyeux pour naviguer dans tout ça... laugh).

   Une dernière chose, j'ai porté la quantité de gold acquis par le héros à 30 et le score à 70 dans la classe «Enemy» ainsi que la quantité de gold à 60 et le score à 140 dans la classe «Asteroid». Ces modifications se retrouvent dans le projet qui accompagne ce chapitre. wink

   Et pour terminer je vous montre les conséquences dues aux états d'un satellite avec ces deux screenshots.

 

Un satellite en vol...

 

Un satellite détruit...

 

   A bientôt pour le chapitre 25 (Les Collisions partie V – Ovnis VS MSF). wink

         Gondulzak.

 

 

 

Connexion

CoalaWeb Traffic

Today66
Yesterday274
This week1137
This month4811
Total1744018

27/04/24