Faceți un shooter vector neon în XNA Efecte particulare

În această serie de tutoriale, vă voi arăta cum să faceți un împușcătură cu stick-uri neon, cum ar fi Geometry Wars, în XNA. Scopul acestor tutoriale nu este să vă lase o replică exactă a războaielor Geometry, ci mai degrabă să treceți peste elementele necesare care vă vor permite să creați propria varianta de înaltă calitate.


Prezentare generală

În seria de până acum, am creat gameplay-ul și am adăugat bloom. În continuare, vom adăuga efecte particulare.

Avertizare: Tare!

Efectele particulelor sunt create prin realizarea unui număr mare de particule mici. Ele sunt foarte versatile și pot fi folosite pentru a adăuga fler la aproape orice joc. În Shape Blaster vom face explozii folosind efectele particulelor. De asemenea, vom folosi efecte de particule pentru a crea focul de evacuare pentru nava jucătorului și pentru a adăuga un fler vizual la găurile negre. În plus, vom examina modul în care particulele interacționează cu gravitatea din găurile negre.

ParticleManager Clasă

Vom începe prin crearea unui ParticleManager clasa care va stoca, actualiza și trage toate particulele. Vom face ca această clasă să fie destul de generală încât să poată fi reutilizată cu ușurință în alte proiecte. Pentru a păstra ParticleManager general, nu va fi responsabil pentru modul în care particulele arata sau se misca; vom rezolva asta în altă parte.

Particulele tind să fie create și distruse rapid și în număr mare. Vom folosi un piscină de obiecte pentru a evita crearea de cantități mari de gunoi. Aceasta înseamnă că vom aloca un număr mare de particule în față și apoi vom reuși să reutilizăm aceleași particule. Vom face și noi ParticleManager au o capacitate fixă. Acest lucru îl va simplifica și ne va ajuta să nu depășim performanțele sau limitele noastre de memorie, creând prea multe particule. Când numărul maxim de particule este depășit, vom începe să înlocuim cele mai vechi particule cu altele noi.

Vom face ParticleManager o clasă generică. Acest lucru ne va permite să stocăm informații personalizate de stare pentru particule, fără a fi greu să le codificăm ParticleManager în sine. Vom crea, de asemenea, un imbricat particulă clasă.

 clasa publica ParticleManager public class Particle textura publică Texture2D; poziția publică Vector2; public float Orientare; public Vector2 Scale = Vector2.One; public Culoare de culoare; durata flotării publice; plată publică PercentLife = 1f; statul T public; 

particulă clasa are toate informațiile necesare pentru a afișa o particulă și a gestiona durata sa de viață. Parametrul generic, Statul T, este acolo să deținem orice date suplimentare de care avem nevoie pentru particulele noastre. Ce date sunt necesare vor varia în funcție de efectele de particule dorite; ar putea fi folosit pentru a stoca viteza, accelerația, viteza de rotație sau orice altceva de care ai nevoie.

Pentru a ajuta la gestionarea particulelor, vom avea nevoie de o clasă care să funcționeze ca o arbore circular, ceea ce înseamnă că indici care ar fi în mod normal în afara limitelor se vor înfășura în jurul valorii de la începutul matricei. Acest lucru va face mai ușor să înlocuiți mai întâi cele mai vechi particule, dacă nu avem spațiu pentru noi particule din matricea noastră. Adăugăm următoarele ca o clasă imbricată în ParticleManager.

 clasa privată CircularParticleArray private int start; public int Start get return start;  set start = value% list.Length;  public int Count get; a stabilit;  public int Capacitate get return list.Length;  privată Particule [] lista; public CircularParticleArray (capacitatea int) list = noua Particule [capacitate];  public Particle acest [int i] get lista de întoarcere [(start + i)% list.Length];  set list [(start + i)% list.Length] = valoare; 

Putem seta start proprietate pentru a regla unde indexul zero în nostru CircularParticleArray corespunde cu matricea de bază, și Numara va fi folosit pentru a urmări numărul de particule active din listă. Vom asigura că particula la indicele zero este întotdeauna cea mai veche particulă. Dacă vom înlocui cea mai veche particulă cu una nouă, vom crește pur și simplu start, care în esență rotește matricea circulară.

Acum că avem cursurile ajutoarelor noastre, putem începe să completați ParticleManager clasă. Vom avea unele variabile membre și un constructor.

 // Acest delegat va fi chemat pentru fiecare particulă. acțiune privată updateParticle; privat CircularParticleArray particleList; public ParticleManager (capacitate int, Acțiune updateParticle) this.updateParticle = updateParticle; particleList = circuitul CircularParticleArray nou (capacitate); // Populați lista cu obiecte particulare goale, pentru reutilizare. pentru (int i = 0; i < capacity; i++) particleList[i] = new Particle(); 

Prima variabilă declarată, updateParticle, va fi o metodă personalizată care actualizează particulele corespunzător pentru efectul dorit. Un joc poate avea mai multe ParticleManagers actualizarea diferită dacă este necesar. De asemenea, creăm un CircularParticleList și umpleți-o cu particule goale. Constructorul este singurul loc ParticleManager alocă memoria.

Apoi adăugăm CreateParticle () care creează o nouă particulă utilizând următoarea particulă neutilizată din piscină sau cea mai veche particulă dacă nu există particule neutilizate.

 public void CreateParticle (textura Texture2D, pozitia Vector2, nuanta de culoare, durata flotorului, scara Vector2, starea T, float theta = 0) Particula particulei; dacă (particleList.Count == particleList.Capacity) // dacă lista este plină, suprascrie cea mai veche particulă și rotiți particula listă circulară = particleList [0]; particleList.Start ++;  altceva particle = particleList [particleList.Count]; particleList.Count ++;  // Crearea particulei particule.Textură = textură; particle.Position = poziție; particle.Tint = tint; particle.Durație = durată; particle.PercentLife = 1f; particle.Scale = scară; particle.Orientation = theta; particle.State = stare; 

Particulele pot fi distruse oricând. Trebuie să îndepărtăm aceste particule, asigurându-ne că celelalte particule rămân în aceeași ordine. Putem face acest lucru prin iterarea prin lista de particule, urmărind în același timp câte au fost distruse. Pe măsură ce mergem, mutăm fiecare particulă activă în fața tuturor particulelor distruse, schimbând-o cu prima particulă distrusă. Odată ce toate particulele distruse sunt la sfârșitul listei, le dezactivează prin setarea listei Numara variabilă la numărul de particule active. Particulele distruse vor rămâne în matricea de bază, dar nu vor fi actualizate sau desenate.

ParticleManager.Update () gestionează actualizarea fiecărei particule și eliminarea particulelor distruse din listă.

 public void Actualizare () int removalCount = 0; pentru (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; 

Ultimul lucru pe care trebuie să-l implementăm ParticleManager desenează particulele.

 public void Draw (SpriteBatch spriteBatch) pentru (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);  

ParticleState struct

Următorul lucru pe care trebuie să-l faceți este să creați o clasă sau structură personalizată pentru a personaliza modul în care particulele vor arăta în Shape Blaster. Vor exista mai multe tipuri diferite de particule în Shape Blaster care se comportă ușor diferit, așa că vom începe prin crearea unui enum pentru tipul de particule. Vom avea nevoie și de variabile pentru viteza particulei și lungimea inițială.

 public enum ParticleType Nici unul, Enemy, Bullet, IgnoreGravity public struct ParticleState public Vector2 Velocity; tip public ParticleType; plutarul public plutitorMultiplier; 

Acum suntem gata să scriem metoda de actualizare a particulelor. Este o idee bună să faceți această metodă rapidă, deoarece ar putea fi necesară numirea unui număr mare de particule.

Vom începe simplu. Adăugați următoarea metodă la ParticleState.

 public static void UpdateParticle (particule ParticleManager.Particle) var vel = particle.State.Velocity; particle.Position + = vel; particle.Orientation = vel.ToAngle (); // flotoarele denormalizate produc probleme semnificative de performanță dacă (Math.Abs ​​(vel.X) + Math.Abs ​​(vel.Y) < 0.00000000001f) vel = Vector2.Zero; vel *= 0.97f; // particles gradually slow down x.State.Velocity = vel; 

Exploziile de inamici

Vom reveni și vom îmbunătăți această metodă într-un moment. În primul rând, să creăm anumite efecte de particule, astfel încât să putem testa efectiv schimbările noastre. În GameRoot, declara un nou ParticleManager și numiți-o Actualizați() și A desena() metode.

 // în GameRoot public static ParticleManager ParticleManager get; set privat;  // în GameRoot.Initialize () ParticleManager = ParticleManager nou (1024 * 20, ParticleState.UpdateParticle); // în GameRoot.Update () ParticleManager.Update (); // în GameRoot.Draw () spriteBatch.Begin (SpriteSortMode.Deferred, BlendState.Additive); ParticleManager.Draw (); spriteBatch.End ();

De asemenea, declarați un nou Texture2D denumit LineParticle pentru textura particulei în Artă clasa și încărcați textura așa cum am făcut-o pentru ceilalți sprites.

Acum hai să-i facem pe dusmani să explodeze. Modificați Enemy.WasShot () după cum urmează.

 void public WasShot () IsExpired = true; pentru (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);  

Acest lucru creează 120 de particule care vor trage spre exterior cu viteze diferite în toate direcțiile. Viteza aleatorie este ponderată astfel încât particulele sunt mai susceptibile de a se deplasa în apropierea vitezei maxime. Acest lucru va determina mai multe particule să fie la marginea exploziei pe măsură ce se extinde. Particulele au 190 de cadre, sau doar peste trei secunde.

Acum puteți rula jocul și vă puteți uita că inamicii explodează. Cu toate acestea, există încă unele îmbunătățiri care trebuie făcute pentru efectele particulelor.

Prima problemă este că particulele dispar brusc odată ce durata acestora se scurge. Ar fi mai bine dacă ar putea să se estompeze fără probleme. Dar să mergem puțin mai departe și să facem ca particulele să strălucească mai repede când se mișcă repede. De asemenea, ar fi frumos să prelungim particulele care se mișcă rapid și să scurteze cele lentătoare.

Modificați ParticleState.UpdateParticle () după cum urmează (schimbările sunt evidențiate).

 public static void UpdateParticle (particule ParticleManager.Particle) var vel = particle.State.Velocity; particle.Position + = vel; particle.Orientation = vel.ToAngle (); viteza flotorului = vel.Length (); float alfa = Math.Min (1, Math.Min (particle.PercentLife * 2, viteza * 1f)); alfa * = alfa; particula.Color.A = (octet) (255 * alfa); particle.Scale.X = particul.Stat.LengthMultiplier * Math.Min (Math.Min (1f, 0.2f * viteza + 0.1f), alfa); dacă (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; 

Exploziile arată mult mai bine acum, dar toate au aceeași culoare. Le putem oferi mai multă varietate alegând culori aleatorii. O metodă de a produce culori aleatorii este alegerea aleatorie a componentelor roșii, albastre și verzi, dar acest lucru va produce o mulțime de culori plictisitoare și dorim ca particulele noastre să aibă un aspect de lumină neon. Putem avea mai mult control asupra culorilor noastre specificându-le în spațiul de culoare HSV. HSV înseamnă nuanță, saturație și valoare. Am dori să alegem culori cu o nuanță aleatorie, dar cu o saturație și o valoare fixă. Avem nevoie de o funcție de ajutor care poate produce o culoare din valorile HSV.

 clasa statică ColorUtil public static Culoare HSVToColor (float h, float s, float v) if (h == 0 && s == 0) returnează o nouă culoare (v, v, v); float c = s * v; float x = c * (1 - Math.Abs ​​(h% 2-1)); float m = v - c; dacă (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);  

Acum putem modifica Enemy.WasShot () pentru a utiliza culori aleatorii. Pentru a face culoarea exploziei mai puțin monotonă, vom alege două culori cheie învecinate pentru fiecare explozie și vom interpola liniar între ele printr-o sumă aleatorie pentru fiecare particulă.

 void public WasShot () IsExpired = true; float hue1 = rand.NextFloat (0, 6); flotare hue2 = (hue1 + rand.NextFloat (0, 2))% 6f; Culoare culoare1 = ColorUtil.HSVToColor (hue1, 0.5f, 1); Culoare culoare2 = ColorUtil.HSVToColor (hue2, 0.5f, 1); pentru (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);  

Exploziile ar trebui să arate ca animația de mai jos.

Puteți juca cu generația de culori pentru a se potrivi preferințelor dvs. O tehnică alternativă care funcționează bine este să alegeți un număr de modele de culori pentru explozii și să alegeți aleatoriu printre schemele de culori alese în prealabil.

Exploziile cu bullet

De asemenea, putem face gloanțele să explodeze când ajung la marginea ecranului. Vom face în mod esențial același lucru pe care l-am făcut pentru explozii inamice.

Adăugați o caracteristică statică Întâmplător membru al echipei Glonţ clasă.

 static privat Random rand = new Random ();

Apoi modificați Bullet.Update () după cum urmează.

 // ștergeți gloanțele care se dezactivează dacă (! GameRoot.Viewport.Bounds.Contains (Position.ToPoint ())) IsExpired = true; pentru (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 ); 

S-ar putea observa că dând particulelor o direcție aleatorie este risipă, pentru că cel puțin jumătate din particule se vor deplasa imediat în afara ecranului (mai mult dacă gloanțele explodează într-un colț). Am putea face ceva efort pentru a ne asigura că particulele sunt date doar cu viteze opuse față de peretele cu care se confruntă. În schimb, vom lua un indiciu de la Geometry Wars și vom face ca toate particulele să scape de ziduri. Orice particule care se află în afara ecranului vor fi returnate.

Adăugați următoarele linii la ParticleState.UpdateParticle () oriunde între prima și ultima linie.

 var pos = x.Poziție; int lățime = (int) GameRoot.ScreenSize.X; int înălțime = (int) GameRoot.ScreenSize.Y; // se ciocnesc cu marginea ecranului dacă (pos.X < 0) vel.X = Math.Abs(vel.X); else if (pos.X > lățime) vel.X = -Math.Abs ​​(vel.X); dacă (pos.Y < 0) vel.Y = Math.Abs(vel.Y); else if (pos.Y > înălțime) vel.Y = -Math.Abs ​​(vel.Y);

Explosiunea navei jucătorului

Vom face o explozie foarte mare când jucătorul va fi ucis. Modifica PlayerShip.Kill () ca astfel:

 public void Kill () framesUntilRespawn = 60; Culoare galbenă = culoare nouă (0.8f, 0.8f, 0.4f); pentru (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);  

Acest lucru este similar cu exploziile inamice, dar folosim mai multe particule și folosim întotdeauna aceeași schemă de culoare. Tipul de particule este, de asemenea, setat la ParticleType.None.

În demo, particulele din explozii inamice încetinesc mai repede decât particulele de pe nava jucătorului care explodează. Acest lucru face ca explozia jucătorului să dureze un pic mai mult și să pară un pic mai epic.


Găurile negre revizuite

Acum, că avem efecte particulare, să revizuim gaurile negre și să le facem să interacționeze cu particulele.

Efectul asupra particulelor

Găurile negre ar trebui să afecteze particule în plus față de alte entități, deci trebuie să le modificăm ParticleState.UpdateParticle (). Adăugați următoarele linii.

 dacă (x.State.Type! = ParticleType.IgnoreGravity) foreach (var blackHole în EntityManager.BlackHoles) var dPos = blackHole.Position - pos; distanța flotant = dPos.Length (); var n = dPos / distanță; vel + = 10000 * n / (distanța * distanța + 10000); // adăugați accelerația tangențială pentru particulele din apropiere dacă (distanța < 400) vel += 45 * new Vector2(n.Y, -n.X) / (distance + 100);  

Aici, n este vectorul unității care indică spre gaura neagră. Forța atractivă este o versiune modificată a funcției pătrat invers. Prima modificare este că numitorul este \ (distanța ^ 2 + 10.000 \). Acest lucru determină forța atractivă să se apropie de o valoare maximă, în loc să se tindă spre infinit, deoarece distanța devine foarte mică. Atunci când distanța este mult mai mare de 100 pixeli, \ (distanța ^ 2 \) devine mult mai mare de 10.000. Prin urmare, adăugarea a 10.000 la \ (distanța ^ 2 \) are un efect foarte mic și funcția aproximează o funcție pătrată inversă normală. Cu toate acestea, atunci când distanța este mult mai mică de 100 de pixeli, distanța are un efect mic asupra valorii numitorului, iar ecuația devine aproximativ egală cu:

 vel + = n;

A doua modificare este adăugarea unei componente laterale la viteza atunci când particulele se apropie suficient de gaura neagră. Aceasta are două scopuri. Mai întâi, particulele se spirală în sens orar spre gaura neagră. În al doilea rând, atunci când particulele se apropie destul de mult, ele vor ajunge la echilibru și vor forma un cerc stralucitor în jurul gaurii negre.

Bacsis: Pentru a roti un vector, V, 90 ° în sensul acelor de ceasornic, luați (V.Y, -V.X). În mod similar, pentru a roti 90 ° în sens invers acelor de ceasornic, luați (-V.Y, V.X).

Producerea de particule

Găurile negre vor produce două tipuri de particule. În primul rând, vor pulveriza periodic particule care vor orbita în jurul lor. În al doilea rând, când o gaură neagră este împușcată, acesta va pulveriza particule speciale care nu sunt afectate de gravitatea sa.

Adăugați următorul cod la BlackHole.WasShot () metodă.

 float nuanță = (float) ((3 * GameRoot.GameTime.TotalGameTime.TotalSeconds)% 6); Culoare culoare = ColorUtil.HSVToColor (nuanță, 0,25f, 1); const int numParticles = 150; flotare startOffset = rand.NextFloat (0, MathHelper.TwoPi / numParticles); pentru (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); 

Aceasta funcționează în cea mai mare parte în același mod ca și celelalte explozii de particule. O diferență este că alegem nuanța culorii pe baza duratei totale de joc scurs. Dacă trageți gaura neagră de mai multe ori în succesiune rapidă, veți vedea că nuanța exploziilor se rotește treptat. Acest lucru pare mai puțin murdar decât utilizarea culorilor aleatorii, permițând în același timp o variație.

Pentru pulverizarea orbită a particulelor, trebuie să adăugăm o variabilă la Gaură neagră pentru a urmări direcția în care în prezent pulverizăm particule.

 spray privat cu flotorAngle = 0;

Acum adăugați următoarele la BlackHole.Update () metodă.

 Găurile negre pulverizează particule care orbitează. Pulverizatorul pornește și se oprește la fiecare trimestru al doilea. dacă ((GameRoot.GameTime.TotalGameTime.Milliseconds / 250)% 2 == 0) Vector2 sprayVel = MathUtil.FromPolar (sprayAngle, rand.NextFloat (12, 15)); Culoare culoare = ColorUtil.HSVToColor (5, 0.5f, 0.8f); // lumină violet Vector2 pos = poziția + 2f * vector2 nou (sprayVel.Y, -sprayVel.X) + rand.NextVector2 (4, 8); var state = ParticleState noi () Velocity = sprayVel, LengthMultiplier = 1, Type = ParticleType.Enemy; GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, culoare, 190, 1.5f, de stat);  // rotiți direcția de pulverizare sprayAngle - = MathHelper.TwoPi / 50f;

Acest lucru va determina gaurile negre să pulverizeze spurturi de particule purpurii care vor forma un inel răcoros care orbitează în jurul gaurii negre, după cum urmează:



Foc de evacuare a navei

Așa cum este impus de legile fizicii geometrice-neon, nava jucătorului se propulsează prin jettarea unui flux de particule de foc în conducta de eșapament. Cu motorul nostru cu particule în loc, acest efect este ușor de realizat și adaugă fler vizual la mișcarea navei.

Pe măsură ce se deplasează nava, creează trei fluxuri de particule: un curs central care se declanșează direct pe spatele navei și două fluxuri laterale ale căror unghiuri se rotesc înapoi și în raport cu nava. Cele două fluxuri laterale se rotesc în direcții opuse pentru a realiza un model de traversare. Fluxurile laterale au o culoare mai roșie, în timp ce fluxul central are o culoare mai caldă, galben-albă. Animația de mai jos arată efectul.

Pentru a face ca focul sa straluceasca mai mult decat ar fi de la inflorire, vom avea nava care emite particule suplimentare care arata astfel:

Aceste particule vor fi colorate și amestecate cu particulele obișnuite. Codul pentru întregul efect este prezentat mai jos.

 void privat MakeExhaustFire () if (Velocity.LengthSquared ()> 0.1f) // configurați unele variabile Orientation = Velocity.ToAngle (); Quaternion rot = Quaternion.CreateFromYawPitchRoll (0f, 0f, Orientare); dublu t = GameRoot.GameTime.TotalGameTime.TotalSeconds; Viteza primară a particulelor este de 3 pixeli / cadru în direcția opusă căreia nava călătorește. Vector2 baseVel = Velocitate.ScaleTo (-3); // Calculați viteza laterală pentru cele două fluxuri laterale. Direcția este perpendiculară pe viteza navei, iar magnitudinea variază sinusoidal. Vector2 perpVel = vectorul Vector2 (baseVel.Y, -baseVel.X) * (0.6f * (float) Math.Sin (t * 10)); Culoare sideColor = culoare nou (200, 38, 9); // roșu intens Culoare midColor = culoare nouă (255, 187, 30); // portocaliu-galben Vector2 pos = Poziție + Vector2.Transform (nou Vector2 (-25, 0), rot); poziția țevii de eșapament a navei. const float alfa = 0.7f; // fluxul de particule medii Vector2 velMid = baseVel + rand.NextVector2 (0, 1); GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, Color.White * alfa, 60f, nou Vector2 (0.5f, 1), new ParticleState (velMid, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.Glow, pos, midColor * alfa, 60f, nou Vector2 (0.5f, 1), noul ParticleState (velMid, ParticleType.Enemy)); // fluxuri de particule laterale Vector2 vel1 = baseVel + perpVel + rand.NextVector2 (0, 0.3f); Vector2 vel2 = bazăVel - perpVel + rand.NextVector2 (0, 0.3f); GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, Color.White * alfa, 60f, nou Vector2 (0.5f, 1), nou ParticleState (vel1, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, Color.White * alfa, 60f, nou Vector2 (0.5f, 1), nou ParticleState (vel2, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (art.Glow, pos, sideColor * alfa, 60f, nou Vector2 (0.5f, 1), nou ParticleState (vel1, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.Glow, pos, sideColor * alfa, 60f, nou Vector2 (0.5f, 1), nou ParticleState (vel2, ParticleType.Enemy)); 

Nu se întâmplă nimic în acest cod. Utilizăm o funcție sinusoidală pentru a produce efectul pivotant în fluxurile laterale prin modificarea vitezei lor lateral în timp. Pentru fiecare flux, se creează două particule suprapuse pe cadru: un alb semi-transparent LineParticle și o particulă strălucitoare colorată în spatele ei. Apel MakeExhaustFire () la sfârșitul PlayerShip.Update (), imediat înainte de a seta viteza navei la zero.


Concluzie

Cu toate aceste efecte de particule, Shape Blaster începe să arate destul de rece. În partea finală a acestei serii, vom adăuga încă un efect extraordinar: grila de fond de bază.