Faire un tireur de vecteur au néon dans XNA effets de particules

Dans cette série de didacticiels, je vais vous montrer comment créer un jeu de tir au néon, tel que Geometry Wars, dans XNA. Le but de ces tutoriels n'est pas de vous laisser avec une réplique exacte de Geometry Wars, mais plutôt de passer en revue les éléments nécessaires qui vous permettront de créer votre propre variante de haute qualité..


Vue d'ensemble

Jusqu'à présent, dans la série, nous avons mis en place le gameplay et ajouté Bloom. Ensuite, nous allons ajouter des effets de particules.

Avertissement: fort!

Les effets de particules sont créés par la fabrication d’un grand nombre de petites particules. Ils sont très polyvalents et peuvent être utilisés pour ajouter du style à presque tous les jeux. Dans Shape Blaster, nous allons créer des explosions utilisant des effets de particules. Nous utiliserons également des effets de particules pour créer un tir d'échappement sur le vaisseau du joueur et pour ajouter une touche visuelle aux trous noirs. De plus, nous verrons comment faire en sorte que les particules interagissent avec la gravité provenant des trous noirs..

le ParticleManager Classe

Nous allons commencer par créer un ParticleManager classe qui va stocker, mettre à jour et dessiner toutes les particules. Nous allons rendre cette classe assez générale pour qu’elle puisse être facilement réutilisée dans d’autres projets. Garder le ParticleManager En règle générale, il ne sera pas responsable de l'apparence ou du mouvement des particules; nous allons gérer cela ailleurs.

Les particules ont tendance à être créées et détruites rapidement et en grand nombre. Nous allons utiliser un pool d'objets pour éviter de créer de grandes quantités de déchets. Cela signifie que nous allons allouer un grand nombre de particules dès le départ, puis continuer à les réutiliser. Nous ferons aussi ParticleManager avoir une capacité fixe. Cela simplifiera les choses et nous évitera de dépasser nos performances ou nos limites de mémoire en créant trop de particules. Lorsque le nombre maximal de particules est dépassé, nous allons commencer à remplacer les particules les plus anciennes par de nouvelles..

Nous ferons le ParticleManager une classe générique. Cela nous permettra de stocker des informations d’état personnalisées pour les particules sans les coder en dur dans la mémoire. ParticleManager lui-même. Nous allons également créer un imbriqué Particule classe.

 Classe publique ParticleManager public class Particle public Texture2D Texture; public Vector2 Position; float Orientation; public Vector2 Scale = Vector2.One; public Couleur Couleur; flottant public Durée; flottant public PercentLife = 1f; Etat T public; 

le Particule La classe possède toutes les informations nécessaires pour afficher une particule et gérer sa durée de vie. Le paramètre générique, État T, existe-t-il des données supplémentaires dont nous pourrions avoir besoin pour nos particules? Les données nécessaires varieront en fonction des effets de particules souhaités. il peut être utilisé pour stocker la vitesse, l'accélération, la vitesse de rotation ou toute autre chose dont vous pourriez avoir besoin.

Pour aider à gérer les particules, nous avons besoin d’une classe qui fonctionne comme un réseau circulaire, ce qui signifie que les index qui seraient normalement hors limites se replieront au début du tableau. Cela facilitera le remplacement des particules les plus anciennes en premier si nous manquons d’espace pour les nouvelles particules de notre réseau. Nous ajoutons ce qui suit comme classe imbriquée dans ParticleManager.

 classe privée CircularParticleArray private int start; public int Start get return start;  set start = valeur% list.Length;  public int Count get; ensemble;  public int Capacity get return list.Length;  liste privée de particules []; public CircularParticleArray (int capacité) list = new Particle [capacité];  public Particle this [int i] get return list [(début + i)% list.Length];  set list [(début + i)% list.Length] = valeur; 

Nous pouvons régler le Début propriété à ajuster où l'indice zéro dans notre CircularParticleArray correspond à dans le tableau sous-jacent, et Compter sera utilisé pour suivre combien de particules actives sont dans la liste. Nous veillerons à ce que la particule d'indice zéro soit toujours la plus ancienne. Si nous remplaçons la particule la plus ancienne par une nouvelle, nous allons simplement incrémenter Début, qui tourne essentiellement le réseau circulaire.

Maintenant que nous avons nos classes d’aide, nous pouvons commencer à remplir le ParticleManager classe. Nous aurons besoin de variables membres et d'un constructeur.

 // Ce délégué sera appelé pour chaque particule. Action privée updateParticle; private CircularParticleArray particleList; public ParticleManager (int capacité, Action updateParticle) this.updateParticle = updateParticle; particleList = new CircularParticleArray (capacité); // Remplit la liste avec des objets particules vides, pour les réutiliser. pour (int i = 0; i < capacity; i++) particleList[i] = new Particle(); 

La première variable déclarée, updateParticle, sera une méthode personnalisée qui met à jour les particules de manière appropriée pour l'effet souhaité. Un jeu peut avoir plusieurs Gestionnaires de particules cette mise à jour différemment si nécessaire. Nous créons également un CircularParticleList et le remplir avec des particules vides. Le constructeur est le seul endroit où ParticleManager alloue de la mémoire.

Ensuite, nous ajoutons le CreateParticle () méthode, qui crée une nouvelle particule en utilisant la particule suivante non utilisée du pool ou la particule la plus ancienne s'il n'y a pas de particules inutilisées.

 vide public CreateParticle (Texture2D texture, position Vector2, teinte de la couleur, durée de flottement, échelle de Vector2, état T, float thêta = 0) Particule particule; if (particleList.Count == particleList.Capacity) // si la liste est pleine, écrasez la particule la plus ancienne et faites pivoter la liste circulaire particle = particleList [0]; particleList.Start ++;  else particule = particuleList [particuleList.Count]; particleList.Count ++;  // Crée la particule de la particule. Texture = texture; particule.Position = position; particule.Tinte = teinte; particule.Duration = durée; particule.PercentLife = 1f; particule.échelle = échelle; particule.Orientation = thêta; particule.état = état; 

Les particules peuvent être détruites à tout moment. Nous devons éliminer ces particules tout en veillant à ce que les autres particules restent dans le même ordre. Nous pouvons le faire en parcourant la liste des particules tout en gardant une trace du nombre de celles qui ont été détruites. Au fur et à mesure, nous déplaçons chaque particule active devant toutes les particules détruites en les échangeant avec la première particule détruite. Une fois que toutes les particules détruites sont à la fin de la liste, nous les désactivons en définissant la liste. Compter variable en fonction du nombre de particules actives. Les particules détruites resteront dans le tableau sous-jacent, mais ne seront ni mises à jour ni dessinées..

ParticleManager.Update () gère la mise à jour de chaque particule et le retrait de la liste des particules détruites.

 void public Update () int removalCount = 0; pour (int i = 0; i < particleList.Count; i++)  var particle = particleList[i]; updateParticle(particle); particle.PercentLife -= 1f / particle.Duration; // sift deleted particles to the end of the list Swap(particleList, i - removalCount, i); // if the particle has expired, delete this particle if (particle.PercentLife < 0) removalCount++;  particleList.Count -= removalCount;  private static void Swap(CircularParticleArray list, int index1, int index2)  var temp = list[index1]; list[index1] = list[index2]; list[index2] = temp; 

La dernière chose à mettre en œuvre dans ParticleManager dessine les particules.

 public void Draw (SpriteBatch spriteBatch) pour (int i = 0; i < particleList.Count; i++)  var particle = particleList[i]; Vector2 origin = new Vector2(particle.Texture.Width / 2, particle.Texture.Height / 2); spriteBatch.Draw(particle.Texture, particle.Position, null, particle.Color, particle.Orientation, origin, particle.Scale, 0, 0);  

le ParticleState Struct

La prochaine chose à faire est de créer une classe ou une structure personnalisée pour personnaliser l'apparence des particules dans Shape Blaster. Shape Blaster comportera différents types de particules se comportant légèrement différemment, nous allons donc commencer par créer un enum pour le type de particule. Nous aurons également besoin de variables pour la vitesse de la particule et sa longueur initiale.

 public enum ParticleType Aucun, Enemy, Bullet, IgnoreGravity public struct ParticleState public Vector2 Velocity; type de particule public; flotteur public LengthMultiplier; 

Nous sommes maintenant prêts à écrire la méthode de mise à jour de la particule. C'est une bonne idée de faire cette méthode rapide car il pourrait être appelé pour un grand nombre de particules.

Nous allons commencer simple. Ajoutez la méthode suivante à ParticleState.

 public static void UpdateParticle (ParticleManager.Particule de particule) var vel = particle.State.Velocity; particule.Position + = vel; particule.Orientation = vel.ToAngle (); // Les flottants dénormalisés posent d'importants problèmes de performances si (Math.Abs ​​(vel.X) + Math.Abs ​​(vel.Y) < 0.00000000001f) vel = Vector2.Zero; vel *= 0.97f; // particles gradually slow down x.State.Velocity = vel; 

Explosions ennemies

Nous reviendrons et améliorerons cette méthode dans un instant. Tout d’abord, créons des effets de particules afin de pouvoir tester nos modifications. Dans GameRoot, déclarer un nouveau ParticleManager et appeler son Mettre à jour() et Dessiner() les méthodes.

 // dans GameRoot public static ParticleManager ParticleManager get; ensemble privé;  // dans GameRoot.Initialize () ParticleManager = new ParticleManager (1024 * 20, ParticleState.UpdateParticle); // dans GameRoot.Update () ParticleManager.Update (); // dans GameRoot.Draw () spriteBatch.Begin (SpriteSortMode.Deferred, BlendState.Additive); ParticleManager.Draw (); spriteBatch.End ();

En outre, déclarer un nouveau Texture2D appelé LineParticle pour la texture de la particule dans le Art classe, et charge la texture comme nous l'avons fait pour les autres sprites.

Maintenant, faisons exploser les ennemis. Modifier le Enemy.WasShot () méthode comme suit.

 public vide WasShot () IsExpired = true; pour (int i = 0; i < 120; i++)  float speed = 18f * (1f - 1 / rand.NextFloat(1f, 10f)); var state = new ParticleState()  Velocity = rand.NextVector2(speed, speed), Type = ParticleType.Enemy, LengthMultiplier = 1f ; GameRoot.ParticleManager.CreateParticle(Art.LineParticle, Position, Color.LightGreen, 190, 1.5f, state);  

Cela crée 120 particules qui vont sortir vers l'extérieur avec des vitesses différentes dans toutes les directions. La vitesse aléatoire est pondérée de telle sorte que les particules sont plus susceptibles de se déplacer près de la vitesse maximale. Cela entraînera plus de particules au bord de l'explosion lors de son expansion. Les particules durent 190 images, soit un peu plus de trois secondes.

Vous pouvez maintenant lancer le jeu et regarder les ennemis exploser. Cependant, il reste quelques améliorations à apporter aux effets de particules.

Le premier problème est que les particules disparaissent brusquement une fois leur durée écoulée. Ce serait mieux s'ils pouvaient disparaître en douceur. Mais allons un peu plus loin et rendons les particules plus brillantes lorsqu'elles se déplacent rapidement. En outre, il est intéressant d’allonger les particules rapides et les particules lentes.

Modifier le ParticleState.UpdateParticle () méthode comme suit (les modifications sont mises en évidence).

 public static void UpdateParticle (ParticleManager.Particule de particule) var vel = particle.State.Velocity; particule.Position + = vel; particule.Orientation = vel.ToAngle (); vitesse de flottement = vel.Length (); float alpha = Math.Min (1, Math.Min (particule.PercentLife * 2, vitesse * 1f)); alpha * = alpha; particule.Couleur.A = (octet) (255 * alpha); particule.échelle.X = particule.État.LongueurMultiplicateur * Math.Min (Math.Min (1f, 0,2f * vitesse + 0,1f), alpha); si (Math.Abs ​​(vel.X) + Math.Abs ​​(vel.Y) < 0.00000000001f) // denormalized floats cause significant performance issues vel = Vector2.Zero; vel *= 0.97f; // particles gradually slow down x.State.Velocity = vel; 

Les explosions semblent beaucoup mieux maintenant, mais elles ont toutes la même couleur. Nous pouvons leur donner plus de variété en choisissant des couleurs aléatoires. Une méthode pour produire des couleurs aléatoires consiste à choisir les composants rouge, bleu et vert de manière aléatoire, mais cela produira beaucoup de couleurs ternes et nous aimerions que nos particules aient une apparence de lumière néon. Nous pouvons avoir plus de contrôle sur nos couleurs en les spécifiant dans l'espace colorimétrique HSV. HSV signifie teinte, saturation et valeur. Nous aimerions choisir des couleurs avec une teinte aléatoire, mais une saturation et une valeur fixes. Nous avons besoin d'une fonction d'assistance capable de produire une couleur à partir des valeurs HSV.

 Classe statique ColorUtil Couleur statique publique HSVToColor (float h, float s, float v) if (h == 0 && s == 0) renvoie une nouvelle couleur (v, v, v); float c = s * v; float x = c * (1 - Math.Abs ​​(h% 2 - 1)); float m = v - c; si (h < 1) return new Color(c + m, x + m, m); else if (h < 2) return new Color(x + m, c + m, m); else if (h < 3) return new Color(m, c + m, x + m); else if (h < 4) return new Color(m, x + m, c + m); else if (h < 5) return new Color(x + m, m, c + m); else return new Color(c + m, m, x + m);  

Maintenant on peut modifier Enemy.WasShot () utiliser des couleurs aléatoires. Pour rendre la couleur de l'explosion moins monotone, nous allons sélectionner deux couleurs de clé proches pour chaque explosion et les interpoler linéairement entre elles d'une quantité aléatoire pour chaque particule..

 public vide WasShot () IsExpired = true; float hue1 = rand.NextFloat (0, 6); float hue2 = (hue1 + rand.NextFloat (0, 2))% 6f; Couleur color1 = ColorUtil.HSVToColor (hue1, 0.5f, 1); Couleur couleur2 = CouleurUtil.HSVToColor (teinte2, 0.5f, 1); pour (int i = 0; i < 120; i++)  float speed = 18f * (1f - 1 / rand.NextFloat(1f, 10f)); var state = new ParticleState()  Velocity = rand.NextVector2(speed, speed), Type = ParticleType.Enemy, LengthMultiplier = 1 ; Color color = Color.Lerp(color1, color2, rand.NextFloat(0, 1)); GameRoot.ParticleManager.CreateParticle(Art.LineParticle, Position, color, 190, 1.5f, state);  

Les explosions devraient ressembler à l'animation ci-dessous.

Vous pouvez jouer avec la génération de couleurs en fonction de vos préférences. Une technique alternative qui fonctionne bien consiste à sélectionner à la main un certain nombre de motifs de couleur pour les explosions et à choisir de manière aléatoire parmi vos jeux de couleurs présélectionnés..

Bullet Explosions

Nous pouvons également faire exploser les balles lorsqu'elles atteignent le bord de l'écran. Nous ferons essentiellement la même chose que nous avons faite pour les explosions ennemies.

Ajouter un statique au hasard membre du Balle classe.

 Random statique privé Rand = new Random ();

Puis modifier Bullet.Update () comme suit.

 // supprime les puces qui sortent de l'écran si (! GameRoot.Viewport.Bounds.Contains (Position.ToPoint ())) IsExpired = true; pour (int i = 0; i < 30; i++) GameRoot.ParticleManager.CreateParticle(Art.LineParticle, Position, Color.LightBlue, 50, 1, new ParticleState()  Velocity = rand.NextVector2(0, 9), Type = ParticleType.Bullet, LengthMultiplier = 1 ); 

Vous remarquerez peut-être que donner une direction aléatoire aux particules est une perte de temps, car au moins la moitié des particules quitteront immédiatement l'écran (davantage si la balle explose dans un coin). Nous pourrions faire un travail supplémentaire pour nous assurer que les particules ne reçoivent que des vitesses opposées au mur auquel elles font face. Au lieu de cela, nous allons nous inspirer de Geometry Wars et faire en sorte que toutes les particules rebondissent sur les murs. Toutes les particules hors écran seront renvoyées.

Ajoutez les lignes suivantes à ParticleState.UpdateParticle () n'importe où entre la première et la dernière ligne.

 var pos = x.Position; int width = (int) GameRoot.ScreenSize.X; int height = (int) GameRoot.ScreenSize.Y; // entrer en collision avec les bords de l'écran if (pos.X < 0) vel.X = Math.Abs(vel.X); else if (pos.X > largeur) vel.X = -Math.Abs ​​(vel.X); si (pos.Y < 0) vel.Y = Math.Abs(vel.Y); else if (pos.Y > hauteur) vel.Y = -Math.Abs ​​(vel.Y);

Explosion du navire du joueur

Nous allons faire une très grosse explosion quand le joueur est tué. Modifier PlayerShip.Kill () ainsi:

 vide public Kill () framesUntilRespawn = 60; Couleur jaune = nouvelle couleur (0.8f, 0.8f, 0.4f); pour (int i = 0; i < 1200; i++)  float speed = 18f * (1f - 1 / rand.NextFloat(1f, 10f)); Color color = Color.Lerp(Color.White, yellow, rand.NextFloat(0, 1)); var state = new ParticleState()  Velocity = rand.NextVector2(speed, speed), Type = ParticleType.None, LengthMultiplier = 1 ; GameRoot.ParticleManager.CreateParticle(Art.LineParticle, Position, color, 190, 1.5f, state);  

Cela ressemble aux explosions ennemies, mais nous utilisons plus de particules et utilisons toujours le même jeu de couleurs. Le type de particule est également défini sur ParticleType.None.

Dans la démo, les particules provenant d'explosions ennemies ralentissent plus rapidement que celles du vaisseau du joueur en train d'exploser. Cela rend l'explosion du joueur un peu plus longue et un peu plus épique.


Trous noirs revisités

Maintenant que nous avons des effets de particules, revisitons les trous noirs et faisons-les interagir avec les particules.

Effet sur les particules

Les trous noirs devraient affecter les particules en plus des autres entités, nous devons donc modifier ParticleState.UpdateParticle (). Ajouter les lignes suivantes.

 if (x.State.Type! = ParticleType.IgnoreGravity) foreach (var blackHole dans EntityManager.BlackHoles) var dPos = blackHole.Position - pos; distance de flottement = dPos.Length (); var n = dPos / distance; vel + = 10000 * n / (distance * distance + 10000); // ajoute une accélération tangentielle pour les particules proches si (distance < 400) vel += 45 * new Vector2(n.Y, -n.X) / (distance + 100);  

Ici, n est le vecteur unitaire pointant vers le trou noir. La force d'attraction est une version modifiée de la fonction carrée inverse. La première modification est que le dénominateur est \ (distance ^ 2 + 10 000 \). Cela force la force d'attraction à s'approcher d'une valeur maximale au lieu de tendre vers l'infini à mesure que la distance devient très petite. Lorsque la distance est beaucoup plus grande que 100 pixels, \ (distance ^ 2 \) devient beaucoup plus grand que 10 000. Par conséquent, l’ajout de 10 000 à \ (distance ^ 2 \) a un très petit effet et la fonction se rapproche d’une fonction carrée inverse normale. Toutefois, lorsque la distance est beaucoup plus petite que 100 pixels, la distance a un faible effet sur la valeur du dénominateur et l'équation devient approximativement égale à:

 vel + = n;

La deuxième modification consiste à ajouter une composante latérale à la vitesse lorsque les particules se rapprochent suffisamment du trou noir. Cela sert à deux fins. Premièrement, les particules tournent en spirale dans le sens des aiguilles d’une montre vers le trou noir. Deuxièmement, lorsque les particules se rapprochent suffisamment, elles atteignent l’équilibre et forment un cercle rougeoyant autour du trou noir..

Pointe: Pour faire pivoter un vecteur, V, 90 ° dans le sens des aiguilles d'une montre, prenez (V.Y, -V.X). De même, pour faire pivoter de 90 ° dans le sens anti-horaire, prenez (-V.Y, V.X).

Produire des particules

Les trous noirs vont produire deux types de particules. Tout d'abord, ils pulvériseront périodiquement des particules qui graviteront autour d'eux. Deuxièmement, quand un trou noir est tiré, il pulvérisera des particules spéciales qui ne sont pas affectées par sa gravité.

Ajoutez le code suivant au BlackHole.WasShot () méthode.

 float hue = (float) ((3 * GameRoot.GameTime.TotalGameTime.TotalSeconds)% 6); Couleur couleur = CouleurTrouvée.HSVToColor (teinte, 0.25f, 1); const int numParticles = 150; float startOffset = rand.NextFloat (0, MathHelper.TwoPi / numParticles); pour (int i = 0; i < numParticles; i++)  Vector2 sprayVel = MathUtil.FromPolar(MathHelper.TwoPi * i / numParticles + startOffset, rand.NextFloat(8, 16)); Vector2 pos = Position + 2f * sprayVel; var state = new ParticleState()  Velocity = sprayVel, LengthMultiplier = 1, Type = ParticleType.IgnoreGravity ; GameRoot.ParticleManager.CreateParticle(Art.LineParticle, pos, color, 90, 1.5f, state); 

Cela fonctionne généralement de la même manière que les autres explosions de particules. Une différence est que nous sélectionnons la teinte de la couleur en fonction du temps total écoulé. Si vous tirez plusieurs fois de suite dans le trou noir, vous constaterez une rotation progressive de la teinte des explosions. Cela semble moins compliqué que d'utiliser des couleurs aléatoires tout en permettant la variation.

Pour le spray de particules en orbite, nous devons ajouter une variable à la Trou noir classe pour suivre la direction dans laquelle nous pulvérisons actuellement des particules.

 float privé sprayAngle = 0;

Maintenant, ajoutez ce qui suit au BlackHole.Update () méthode.

 // Les trous noirs vaporisent des particules en orbite. La pulvérisation bascule et se désactive tous les quarts de seconde. if ((GameRoot.GameTime.TotalGameTime.Milliseconds / 250)% 2 == 0) Vector2 sprayVel = MathUtil.FromPolar (sprayAngle, rand.NextFloat (12, 15)); Couleur Couleur = CouleurTrouvée.HSVToColor (5, 0.5f, 0.8f); // violet clair Vector2 pos = Position + 2f * nouveau Vector2 (sprayVel.Y, -sprayVel.X) + rand.NextVector2 (4, 8); var state = new ParticleState () Velocity = sprayVel, LengthMultiplier = 1, Type = ParticleType.Enemy; GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, couleur, 190, 1.5f, état);  // tourne le sens de pulvérisation sprayAngle - = MathHelper.TwoPi / 50f;

Les trous noirs pulvériseront des particules violettes qui formeront un anneau rougeoyant qui gravitera autour du trou noir, comme suit:



Incendie des navires

Comme le dictent les lois de la physique géométrique et du néon, le vaisseau du joueur se propulse en projetant un flot de particules de feu sur son tuyau d'échappement. Avec notre moteur de particules en place, cet effet est facile à créer et ajoute une touche visuelle au mouvement du navire..

Au fur et à mesure que le navire se déplace, nous créons trois flux de particules: un flux central qui se propage directement à l'arrière du navire et deux flux latéraux dont les angles pivotent d'avant en arrière par rapport au navire. Les deux courants latéraux pivotent dans des directions opposées pour former un motif entrecroisé. Les flux latéraux ont une couleur plus rouge tandis que le flux central a une couleur plus chaude, jaune-blanc. L'animation ci-dessous montre l'effet.

Pour que le feu brille plus fort que lors de la seule floraison, le navire émettra des particules supplémentaires ressemblant à ceci:

Ces particules seront teintées et mélangées aux particules régulières. Le code de l'ensemble de l'effet est présenté ci-dessous.

 void privé MakeExhaustFire () if (Velocity.LengthSquared ()> 0.1f) // configurer certaines variables Orientation = Velocity.ToAngle (); Quaternion rot = Quaternion.CreateFromYawPitchRoll (0f, 0f, Orientation); double t = GameRoot.GameTime.TotalGameTime.TotalSeconds; // La vitesse primaire des particules est de 3 pixels / image dans la direction opposée à celle du navire. Vector2 baseVel = Velocity.ScaleTo (-3); // Calcule la vitesse latérale pour les deux flux latéraux. La direction est perpendiculaire à la vitesse du navire et la magnitude // varie de manière sinusoïdale. Vector2 perpVel = nouveau Vector2 (baseVel.Y, -baseVel.X) * (0.6f * (float) Math.Sin (t * 10)); Color sideColor = nouvelle couleur (200, 38, 9); // rouge profond Couleur midColor = new Color (255, 187, 30); // Vector2 orange-jaune pos = Position + Vector2.Transform (nouveau Vector2 (-25, 0), rot); // position du tuyau d'échappement du navire. alpha de const float = 0.7f; // flux de particules moyen Vector2 velMid = baseVel + rand.NextVector2 (0, 1); GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, Color.White * alpha, 60f, nouveau Vector2 (0.5f, 1), nouveau ParticleState (velMid, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.Glow, pos, midColor * alpha, 60f, nouveau Vector2 (0.5f, 1), nouveau ParticleState (velMid, ParticleType.Enemy)); // flux de particules latérales Vector2 vel1 = baseVel + perpVel + rand.NextVector2 (0, 0.3f); Vecteur2 vel2 = baseVel - perpVel + rand.NextVector2 (0, 0,3f); GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, Color.White * alpha, 60f, nouveau Vector2 (0.5f, 1), nouveau ParticleState (vel1, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, Color.White * alpha, 60f, nouveau Vector2 (0.5f, 1), nouveau ParticleState (vel2, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.Glow, pos, sideColor * alpha, 60f, nouveau Vector2 (0.5f, 1), nouveau ParticleState (vel1, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.Glow, pos, sideColor * alpha, 60f, nouveau Vector2 (0.5f, 1), nouveau ParticleState (vel2, ParticleType.Enemy)); 

Il n'y a rien de sournois dans ce code. Nous utilisons une fonction sinus pour produire l’effet de pivotement dans les courants latéraux en faisant varier leur vitesse latérale dans le temps. Pour chaque flux, nous créons deux particules superposées par image: une blanche semi-transparente LineParticle et une particule de lueur colorée derrière elle. Appel MakeExhaustFire () au bout du PlayerShip.Update (), immédiatement avant de régler la vitesse du navire à zéro.


Conclusion

Avec tous ces effets de particules, Shape Blaster commence à avoir l'air cool. Dans la dernière partie de cette série, nous ajouterons un autre effet impressionnant: la grille de fond de déformation.