In deze serie tutorials laat ik je zien hoe je een neon-tweepijps-schietspel maakt, zoals Geometry Wars, in XNA. Het doel van deze tutorials is om je niet te laten met een exacte replica van Geometry Wars, maar om de nodige elementen door te nemen die je in staat stellen om je eigen hoogwaardige variant te maken.
In de serie tot nu toe hebben we de gameplay ingesteld en bloei toegevoegd. Vervolgens voegen we deeltjeseffecten toe.
Waarschuwing: Loud!Effecten van deeltjes worden gecreëerd door een groot aantal kleine deeltjes te maken. Ze zijn zeer veelzijdig en kunnen worden gebruikt om flair toe te voegen aan bijna elk spel. In Shape Blaster zullen we explosies maken met deeltjeseffecten. We zullen ook deeltjeseffecten gebruiken om uitlaatgassen te creëren voor het schip van de speler en om visuele flair toe te voegen aan de zwarte gaten. Bovendien zullen we kijken hoe deeltjes kunnen interageren met de zwaartekracht van de zwarte gaten.
ParticleManager
KlasseWe beginnen met het maken van een ParticleManager
klasse die alle deeltjes opslaat, bijwerkt en tekent. We zullen deze klasse algemeen genoeg maken zodat deze gemakkelijk in andere projecten kan worden hergebruikt. Om de ParticleManager
in het algemeen zal het niet verantwoordelijk zijn voor hoe de deeltjes er uitzien of bewegen; we zullen dat elders aan.
Deeltjes worden meestal snel en in grote aantallen gemaakt en vernietigd. We zullen een gebruiken object pool om te voorkomen dat grote hoeveelheden afval ontstaan. Dit betekent dat we een groot aantal deeltjes vooraan zullen toewijzen en deze dezelfde deeltjes opnieuw zullen gebruiken. We zullen ook maken ParticleManager
hebben een vaste capaciteit. Dit zal het vereenvoudigen en ervoor zorgen dat onze prestaties of geheugenbeperkingen niet worden overschreden door te veel deeltjes te maken. Wanneer het maximale aantal deeltjes wordt overschreden, zullen we beginnen met het vervangen van de oudste deeltjes door nieuwe.
We zullen het maken ParticleManager
een generieke klasse. Hierdoor kunnen we aangepaste statusinformatie opslaan voor de deeltjes zonder deze hard te coderen in de ParticleManager
zelf. We zullen ook een geneste maken Deeltje
klasse.
openbare klasse ParticleManagerpublic class Particle public Texture2D Texture; openbare Vector2-positie; openbare zwevende oriëntatie; public Vector2 Scale = Vector2.One; openbare kleurkleur; openbare float Duur; openbare float PercentLife = 1f; openbare staat;
De Deeltje
klasse heeft alle informatie die nodig is om een deeltje weer te geven en de levensduur te beheren. De generieke parameter, T staat
, is er om aanvullende gegevens te bewaren die we nodig hebben voor onze deeltjes. Welke gegevens nodig zijn, hangt af van de gewenste deeltjeseffecten; het kan worden gebruikt om snelheid, versnelling, rotatiesnelheid of iets anders op te slaan.
Om de deeltjes te helpen beheren, hebben we een klasse nodig die functioneert als een circulaire array, wat betekent dat indexen die normaal gesproken geen grenzen overschrijden, zich in plaats daarvan omdraaien naar het begin van de array. Dit maakt het gemakkelijk om de oudste deeltjes eerst te vervangen als er onvoldoende ruimte is voor nieuwe deeltjes in onze array. We voegen het volgende als een geneste klasse toe in ParticleManager
.
private class CircularParticleArray private int start; public int Start krijg return start; set start = value% list.Length; openbare int Graaf krijgen; vast te stellen; public int Capacity krijg return list.Length; private Particle [] lijst; public CircularParticleArray (int capacity) list = new Particle [capacity]; public Particle this [int i] krijg return list [(start + i)% list.Length]; set list [(start + i)% list.Length] = waarde;
We kunnen de Begin
eigenschap om aan te passen waar index nul in onze CircularParticleArray
komt overeen met in de onderliggende array, en tellen
wordt gebruikt om bij te houden hoeveel actieve deeltjes in de lijst voorkomen. We zullen ervoor zorgen dat het deeltje op index nul altijd het oudste deeltje is. Als we het oudste deeltje vervangen door een nieuw deeltje, gaan we gewoon verder Begin
, die in wezen de ronde matrix roteert.
Nu we onze helperlessen hebben, kunnen we beginnen met het invullen van de ParticleManager
klasse. We hebben enkele lidvariabelen en een constructor nodig.
// Deze afgevaardigde zal voor elk deeltje worden opgeroepen. privéactieupdateParticle; private CircularParticleArray particleList; openbare ParticleManager (int capaciteit, actie updateParticle) this.updateParticle = updateParticle; particleList = new CircularParticleArray (capacity); // Vul de lijst met lege deeltje-objecten, voor hergebruik. voor (int i = 0; i < capacity; i++) particleList[i] = new Particle();
De eerste variabele gedeclareerd, updateParticle
, wordt een aangepaste methode die de deeltjes op de juiste manier bijwerkt voor het gewenste effect. Een spel kan meerdere hebben ParticleManagers
die anders werken indien nodig. We maken ook een CircularParticleList
en vul het met lege deeltjes. De constructeur is de enige plaats waar de ParticleManager
wijst geheugen toe.
Vervolgens voegen we de CreateParticle ()
methode, die een nieuw deeltje maakt met het volgende ongebruikte deeltje in de pool, of het oudste deeltje als er geen ongebruikte deeltjes zijn.
openbare void CreateParticle (Texture2D texture, Vector2 position, Color tint, float duration, Vector2 scale, T state, float theta = 0) deeltje deeltje; if (particleList.Count == particleList.Capacity) // als de lijst vol is, overschrijf dan het oudste deeltje en roteer de circulaire lijst particle = particleList [0]; particleList.Start ++; else particle = particleList [particleList.Count]; particleList.Count ++; // Maak het deeltjesdeeltje. Textuur = textuur; particle.Position = positie; particle.Tint = tint; particle.Duration = duration; particle.PercentLife = 1f; particle.Scale = schaal; particle.Orientation = theta; particle.State = state;
Deeltjes kunnen op elk moment worden vernietigd. We moeten deze deeltjes verwijderen en ervoor zorgen dat de andere deeltjes in dezelfde volgorde blijven. We kunnen dit doen door de lijst met deeltjes te herhalen, terwijl we bijhouden hoeveel er zijn vernietigd. Terwijl we gaan, verplaatsen we elk actief deeltje voor alle vernietigde deeltjes door het te verwisselen met het eerste vernietigde deeltje. Zodra alle vernietigde deeltjes aan het einde van de lijst staan, deactiveren we ze door de lijst in te stellen tellen
veranderlijk naar het aantal actieve deeltjes. Vernietigde deeltjes zullen in de onderliggende array blijven, maar zullen niet worden bijgewerkt of getekend.
ParticleManager.Update ()
verwerkt elk deeltje en verwijdert vernietigde deeltjes uit de lijst.
public void Update () int removalCount = 0; voor (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;
Het laatste ding om in te implementeren ParticleManager
trekt de deeltjes.
public void Draw (SpriteBatch spriteBatch) for (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
structHet volgende dat u moet doen, is het maken van een aangepaste klasse of struct om aan te passen hoe de deeltjes eruit zullen zien in Shape Blaster. Er zijn verschillende soorten deeltjes in Shape Blaster die zich enigszins anders gedragen, dus we beginnen met het maken van een enum
voor het deeltjestype. We hebben ook variabelen nodig voor de snelheid van het deeltje en de beginlengte.
public enum ParticleType none, Enemy, Bullet, IgnoreGravity public struct ParticleState public Vector2 Velocity; publiek ParticleType Type; public float LengthMultiplier;
Nu zijn we klaar om de update-methode van het deeltje te schrijven. Het is een goed idee om deze methode snel te maken, omdat deze mogelijk voor een groot aantal deeltjes moet worden gebruikt.
We beginnen eenvoudig. Voeg de volgende methode toe aan ParticleState
.
public static void UpdateParticle (ParticleManager.Deeltjes deeltje) var vel = particle.State.Velocity; particle.Position + = vel; particle.Orientation = vel.ToAngle (); // gedenormaliseerde drijvers veroorzaken significante prestatieproblemen als (Math.Abs (vel.X) + Math.Abs (vel.Y) < 0.00000000001f) vel = Vector2.Zero; vel *= 0.97f; // particles gradually slow down x.State.Velocity = vel;
We komen terug en verbeteren deze methode in een oogwenk. Laten we eerst een aantal partikeleffecten maken, zodat we onze wijzigingen daadwerkelijk kunnen uittesten. In GameRoot
, een nieuwe verklaren ParticleManager
en noem het Bijwerken()
en Trek()
methoden.
// in GameRoot openbare statische ParticleManager ParticleManager get; privé set; // in GameRoot.Initialize () ParticleManager = nieuwe ParticleManager (1024 * 20, ParticleState.UpdateParticle); // in GameRoot.Update () ParticleManager.Update (); // in GameRoot.Draw () spriteBatch.Begin (SpriteSortMode.Deferred, BlendState.Additive); ParticleManager.Draw (); spriteBatch.End ();
Geef ook een nieuwe aan Texture2D
riep LineParticle
voor de textuur van het deeltje in de Kunst
klasse, en laad de textuur zoals we deden voor de andere sprites.
Laten we nu vijanden laten ontploffen. Wijzig de Enemy.WasShot ()
methode als volgt.
openbare void WasShot () IsExpired = true; voor (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);
Dit creëert 120 deeltjes die naar buiten schieten met verschillende snelheden in alle richtingen. De willekeurige snelheid wordt zodanig gewogen dat deeltjes eerder in de buurt van de maximumsnelheid bewegen. Dit zorgt ervoor dat er meer deeltjes aan de rand van de explosie komen als deze uitzet. De deeltjes duren 190 frames, of iets meer dan drie seconden.
Je kunt nu het spel uitvoeren en vijanden zien exploderen. Er zijn echter nog steeds enkele verbeteringen aan te brengen voor de deeltjeseffecten.
Het eerste probleem is dat de deeltjes abrupt verdwijnen zodra de duur ervan op is. Het zou leuker zijn als ze soepel zouden verdwijnen. Maar laten we een beetje verder gaan en de deeltjes helderder maken als ze snel bewegen. Ook ziet het er goed uit als we snel bewegende deeltjes verlengen en langzaam bewegende deeltjes verkorten.
Wijzig de ParticleState.UpdateParticle ()
methode als volgt (wijzigingen worden gemarkeerd).
public static void UpdateParticle (ParticleManager.Deeltjes deeltje) var vel = particle.State.Velocity; particle.Position + = vel; particle.Orientation = vel.ToAngle (); drijfsnelheid = vel. Lengte (); float alpha = Math.Min (1, Math.Min (particle.PercentLife * 2, speed * 1f)); alpha * = alpha; particle.Color.A = (byte) (255 * alpha); particle.Scale.X = particle.State.LengthMultiplier * Math.Min (Math.Min (1f, 0.2f * speed + 0.1f), alpha); if (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;
De explosies zien er nu veel beter uit, maar ze hebben allemaal dezelfde kleur. We kunnen ze meer afwisseling bieden door willekeurige kleuren te kiezen. Een methode om willekeurige kleuren te produceren, is door willekeurig de rode, blauwe en groene componenten te kiezen, maar dit levert veel saaie kleuren op en we willen dat onze deeltjes een neonlicht-uiterlijk hebben. We kunnen meer controle krijgen over onze kleuren door ze op te geven in de HSV-kleurenruimte. HSV staat voor tint, verzadiging en waarde. We willen kleuren kiezen met een willekeurige tint, maar een vaste verzadiging en waarde. We hebben een hulpfunctie nodig die een kleur uit HSV-waarden kan produceren.
static class ColorUtil public static Color HSVToColor (float h, float s, float v) if (h == 0 && s == 0) return new Color (v, v, v); zweven c = s * v; float x = c * (1 - Math.Abs (h% 2 - 1)); zweven m = v - c; als (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);
Nu kunnen we wijzigen Enemy.WasShot ()
om willekeurige kleuren te gebruiken. Om de kleur van de explosie minder eentonig te maken, kiezen we voor elke explosie twee nabijgelegen sleutelkleuren en lineair interpoleren we ertussen met een willekeurige hoeveelheid voor elk deeltje.
openbare void WasShot () IsExpired = true; floaten hue1 = rand.NextFloat (0, 6); floate hue2 = (hue1 + rand.NextFloat (0, 2))% 6f; Kleur color1 = ColorUtil.HSVToColor (hue1, 0.5f, 1); Kleur color2 = ColorUtil.HSVToColor (hue2, 0.5f, 1); voor (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);
De explosies zouden op de onderstaande animatie moeten lijken.
Je kunt spelen met de kleurgeneratie om aan je voorkeuren te voldoen. Een alternatieve techniek die goed werkt, is door met de hand een aantal kleurpatronen voor explosies uit te kiezen en willekeurig te kiezen uit uw vooraf gekozen kleurenschema's.
We kunnen ook de kogels laten ontploffen wanneer ze de rand van het scherm bereiken. We zullen in wezen hetzelfde doen als vijandelijke explosies.
Voeg een statische toe Willekeurig
lid van de Kogel
klasse.
private static Random rand = nieuw Willekeurig ();
Pas dan aan Bullet.Update ()
als volgt.
// verwijder kogels die van het scherm verdwijnen als (! GameRoot.Viewport.Bounds.Contains (Position.ToPoint ())) IsExpired = true; voor (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 );
Je zult misschien opmerken dat het een verspilling is om de deeltjes een willekeurige richting te geven, omdat minstens de helft van de deeltjes direct van het scherm af zal gaan (meer als de kogel in een hoek explodeert). We zouden wat extra werk kunnen doen om ervoor te zorgen dat deeltjes alleen snelheden krijgen tegenover de muur waarmee ze worden geconfronteerd. In plaats daarvan nemen we echter een signaal uit Geometry Wars en laten alle deeltjes van de muren afketsen. Alle deeltjes die buiten het scherm worden geplaatst, worden teruggestuurd.
Voeg de volgende regels toe aan ParticleState.UpdateParticle ()
ergens tussen de eerste en de laatste regel.
var pos = x.Position; int width = (int) GameRoot.ScreenSize.X; int height = (int) GameRoot.ScreenSize.Y; // botst tegen de randen van het scherm als (pos.X. < 0) vel.X = Math.Abs(vel.X); else if (pos.X > breedte) vel.X = -Math.Abs (vel.X); als (pos.Y. < 0) vel.Y = Math.Abs(vel.Y); else if (pos.Y > hoogte) vel.Y = -Math.Abs (vel.Y);
We maken een hele grote explosie wanneer de speler wordt gedood. Wijzigen PlayerShip.Kill ()
zoals zo:
openbare leegte Kill () framesUntilRespawn = 60; Kleur geel = nieuwe kleur (0.8f, 0.8f, 0.4f); voor (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);
Dit is vergelijkbaar met de vijandelijke explosies, maar we gebruiken meer deeltjes en gebruiken altijd hetzelfde kleurenschema. Het deeltjestype is ook ingesteld op ParticleType.None
.
In de demo vertragen deeltjes van vijandige explosies sneller dan deeltjes van het schip van de speler exploderen. Dit zorgt ervoor dat de explosie van de speler iets langer duurt en een beetje epischer lijkt.
Nu we partikelingseffecten hebben, laten we de zwarte gaten opnieuw bekijken en ze laten interageren met deeltjes.
Zwarte gaten moeten behalve andere entiteiten ook invloed hebben op deeltjes, dus we moeten dit aanpassen ParticleState.UpdateParticle ()
. Voeg de volgende regels toe.
if (x.State.Type! = ParticleType.IgnoreGravity) foreach (var blackHole in EntityManager.BlackHoles) var dPos = blackHole.Position - pos; vlotterafstand = dPos.Length (); var n = dPos / afstand; vel + = 10000 * n / (afstand * afstand + 10000); // voeg tangentiële versnelling toe voor nabijgelegen deeltjes als (afstand < 400) vel += 45 * new Vector2(n.Y, -n.X) / (distance + 100);
Hier, n
is de eenheidsvector die naar het zwarte gat wijst. De aantrekkingskracht is een aangepaste versie van de inverse vierkante functie. De eerste wijziging is dat de noemer is \ (afstand ^ 2 + 10.000 \). Dit zorgt ervoor dat de aantrekkende kracht een maximale waarde nadert in plaats van naar oneindig te neigen naarmate de afstand erg klein wordt. Wanneer de afstand veel groter is dan 100 pixels, wordt \ (afstand ^ 2 \) veel groter dan 10.000. Daarom heeft het toevoegen van 10.000 tot \ (afstand ^ 2 \) een zeer klein effect en benadert de functie een normale inverse vierkante functie. Wanneer de afstand echter veel kleiner is dan 100 pixels, heeft de afstand een klein effect op de waarde van de noemer en wordt de vergelijking ongeveer gelijk aan:
vel + = n;
De tweede wijziging is het toevoegen van een zijwaartse component aan de snelheid wanneer de deeltjes dicht genoeg bij het zwarte gat komen. Dit dient twee doelen. Ten eerste laat het de deeltjes spiraalvormig in de richting van het zwarte gat klokken. Ten tweede, wanneer de deeltjes dicht genoeg bij elkaar komen, zullen ze een evenwicht bereiken en een gloeiende cirkel rond het zwarte gat vormen.
Tip: Om een vector te roteren, V, 90 ° met de klok mee, nemen(V.Y, -V.X)
. Evenzo, nemen om 90 ° tegen de klok in te roteren (-V.Y, V.X)
. Zwarte gaten produceren twee soorten deeltjes. Ten eerste zullen ze periodiek deeltjes uitwerpen die rond hen draaien. Ten tweede, wanneer een zwart gat wordt geschoten, worden speciale deeltjes uitgespuwd die niet worden beïnvloed door de zwaartekracht.
Voeg de volgende code toe aan de BlackHole.WasShot ()
methode.
float hue = (float) ((3 * GameRoot.GameTime.TotalGameTime.TotalSeconds)% 6); Kleurkleur = ColorUtil.HSVToColor (tint, 0.25f, 1); const int numParticles = 150; float startOffset = rand.NextFloat (0, MathHelper.TwoPi / numParticles); voor (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);
Dit werkt meestal op dezelfde manier als de andere deeltjesexplosies. Een verschil is dat we de tint van de kleur kiezen op basis van de totale verstreken speeltijd. Als je het zwarte gat meerdere keren snel na elkaar schiet, zie je de tint van de explosies geleidelijk roteren. Dit ziet er minder rommelig uit dan het gebruik van willekeurige kleuren, terwijl er nog steeds variatie mogelijk is.
Voor de ronddraaiende deeltjesnevel moeten we een variabele toevoegen aan de BlackHole
klasse om de richting te volgen waarin we deeltjes spuiten.
privé float sprayAngle = 0;
Voeg nu het volgende toe aan de BlackHole.Update ()
methode.
// De zwarte gaten sproeien enkele ronddraaiende deeltjes. De spray schakelt elke kwart seconde aan en uit. if ((GameRoot.GameTime.TotalGameTime.Milliseconds / 250)% 2 == 0) Vector2 sprayVel = MathUtil.FromPolar (sprayAngle, rand.NextFloat (12, 15)); Kleurkleur = ColorUtil.HSVToColor (5, 0.5f, 0.8f); // lichtpaars Vector2 pos = Positie + 2f * nieuw 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, color, 190, 1.5f, state); // roteer de spuitrichting sprayAngle - = MathHelper.TwoPi / 50f;
Dit zorgt ervoor dat de zwarte gaten spatten van paarse deeltjes sproeien die een koele gloeiende ring vormen die rond het zwarte gat draait, zoals:
Zoals voorgeschreven door de wetten van de geometrisch-neon fysica, stuwt het schip van de speler zichzelf door een stroom vurige deeltjes uit zijn uitlaatpijp te spuiten. Met onze deeltjesmotor op zijn plaats, is dit effect eenvoudig te maken en voegt het visuele flair toe aan de bewegingen van het schip.
Terwijl het schip beweegt, creëren we drie stromen deeltjes: een centrale stroom die recht uit de achterkant van het schip vuurt, en twee zijstromen waarvan de hoeken heen en weer zwenken ten opzichte van het schip. De twee zijstromen draaien in tegengestelde richtingen om een kriskras patroon te maken. De zijstromen hebben een rodere kleur, terwijl de middenstroom een heter, geel-witte kleur heeft. De onderstaande animatie toont het effect.
Om het vuur helderder te laten gloeien dan alleen uit de bloei, laten we het schip extra deeltjes uitstoten die er als volgt uitzien:
Deze deeltjes worden getint en gemengd met de reguliere deeltjes. De code voor het volledige effect wordt hieronder getoond.
private void MakeExhaustFire () if (Velocity.LengthSquared ()> 0.1f) // stel enkele variabelen in Orientation = Velocity.ToAngle (); Quaternion rot = Quaternion.CreateFromYawPitchRoll (0f, 0f, Orientation); dubbele t = GameRoot.GameTime.TotalGameTime.TotalSeconds; // De primaire snelheid van de deeltjes is 3 pixels / frame in de richting tegenovergesteld aan waar het schip naartoe rijdt. Vector2 baseVel = Velocity.ScaleTo (-3); // Bereken de zijwaartse snelheid voor de twee zijstromen. De richting staat loodrecht op de snelheid van het schip en de // magnitude varieert sinusvormig. Vector2 perpVel = new Vector2 (baseVel.Y, -baseVel.X) * (0.6f * (float) Math.Sin (t * 10)); Kleur sideColor = nieuwe kleur (200, 38, 9); // diep rood Kleur midColor = nieuw Kleur (255, 187, 30); // oranje-geel Vector2 pos = Positie + Vector2.Transform (nieuwe Vector2 (-25, 0), rot); // positie van de uitlaatpijp van het schip. const float alpha = 0.7f; // middendeeltjesstroom Vector2 velMid = baseVel + rand.NextVector2 (0, 1); GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, Color.White * alpha, 60f, new Vector2 (0.5f, 1), new ParticleState (velMid, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.Glow, pos, midColor * alpha, 60f, new Vector2 (0.5f, 1), new ParticleState (velMid, ParticleType.Enemy)); // side particle streams Vector2 vel1 = baseVel + perpVel + rand.NextVector2 (0, 0.3f); Vector2 vel2 = baseVel - perpVel + rand.NextVector2 (0, 0.3f); GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, Color.White * alpha, 60f, new Vector2 (0.5f, 1), new ParticleState (vel1, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, Color.White * alpha, 60f, new Vector2 (0.5f, 1), new ParticleState (vel2, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.Glow, pos, sideColor * alpha, 60f, new Vector2 (0.5f, 1), new ParticleState (vel1, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.Glow, pos, sideColor * alpha, 60f, new Vector2 (0.5f, 1), new ParticleState (vel2, ParticleType.Enemy));
Er is niets sneaky aan de hand in deze code. We gebruiken een sinusfunctie om het zwenkeffect in de zijstromen te produceren door hun zijwaartse snelheid in de loop van de tijd te variëren. Voor elke stream maken we twee overlappende deeltjes per frame: een semitransparante wit LineParticle
en een gekleurd glimdeeltje erachter. telefoontje MakeExhaustFire ()
aan het einde van PlayerShip.Update ()
, onmiddellijk voordat de snelheid van het schip op nul wordt gezet.
Met al deze deeltjeseffecten begint Shape Blaster er behoorlijk gaaf uit te zien. In het laatste deel van deze serie voegen we nog een geweldig effect toe: het kromme achtergrondraster.