Starling Partikel Effekter för Stage3D Shooter Games

Jag är säker på att Stage3D inte är främmande för de flesta läsare; Det är det nya API som ger AS3-programmerare tillgång till GPU. Men kodning mot opcoder i Stage3D kanske inte är allas föredragna val, så det är lyckligt att det finns en genväg: Starling, ett bibliotek utvecklat för att inkapsla denna lågnivå programmering för att göra det mycket lättare. Och tillsammans med Starling kommer dess partikel effekter förlängning. I denna handledning ska vi kolla in partikelsystemen i denna ram och se dess applikationer som appliceras på ett shoot-up-spel.


Steg 1: Grundläggande inställning

Hoppa över det här steget om du har arbetat med FlashDevelop under en tid. För nybörjare, här är hur du installerar ett bibliotekspaket - i detta fall Starling och dess partikelförlängning. Observera att dessa två objekt inte kommer i ett paket, så vi måste ladda ner dem separat.

Först ladda ner Starling Framework och dess partikelförlängning från deras repositories. Unzip efter framgångsrik nedladdning. Skanna den första ompublicerade katalogen för src mapp och klistra in Starling Framework-biblioteket, markerat i bilden nedan, i din projektkälla mapp.

Skanna den andra mappen för partikelförlängningen och kombinera dem ihop. Du kan dra den markerade mappen nedan till Stare mapp. Bilden nedan är det slutresultat du ska komma fram till.

För mer information om FlashDevelop och använd externa bibliotek, se dessa handledning:

  • Nybörjarens guide till FlashDevelop
  • Så här använder du ett externt bibliotek i dina Flash-projekt

Steg 2: Huvudklasser

Om du inte har introducerats till Starling och dess partikelförlängning redan, uppmuntrar jag starkt ett besök på Lee Brimelows videohandledning om Starling- och partikeleffekter, och Matthew Chungs handledning om hantering av animationsstater med Starling.

Vi kommer bara att brisa genom grunderna i två steg här. Om du redan känner till Starling och dess partikelförlängning, kan du hoppa till steg 4.

Du kan se från den andra bilden av föregående steg (den nedre delen) att två klasser skapas: Main.as och Testing.as. Den första fungerar som en startare för den senare. Så, de flesta av vår Starling-kod bor i Testing.as. Jag har markerat den viktiga koden i Main.as här:

 privat funktion init (e: Event = null): void removeEventListener (Event.ADDED_TO_STAGE, init); // Ingångspunkt var myStarling: Starling = Ny Starling (Testning, scen); myStarling.simulateMultitouch = true; myStarling.start (); // initiera Starling på scen // tillåta mus / touch händelser att hända i Starling // vridnyckel och starta motorn! 

... och Testing.as ska se så här ut:

 public class Testing utökar Sprite public function Testing () addEventListener (Event.ADDED_TO_STAGE, init);  privatfunktion init (e: händelse): void removeEventListener (Event.ADDED_TO_STAGE, init); // koden går här. stage.color = 0; // vänd scenfärg till svart // Rita lite quad på scenen, // för att se till att allt är på plats // Notera att övre vänstra hörnet av sprite är inriktat till mitten av scen var q: Quad = ny Quad (30, 30); addChild (q); q.color = 0xEEEEEE; q.x = stage.stageWidth >> 1; q.y = stadium.stageHeight >> 1; 

Och om allt är korrekt inställt, borde du komma fram till resultatet som visas nedan. Ingenting mycket, bara en sprite på scenen.

Notera: Testing.as förlänger Sprite från Starling.display.Sprite, inte flash.display.Sprite. Klasserna har samma namn, men är inte samma.


Steg 3: Partikelförlängning i Starling

Partikelförlängningen av Starling innehåller tre viktiga klasser. Deras funktionaliteter är tabulerade nedan.

Namn på klass Funktionalitet
Particle.as En enda partikel med unika egenskaper.
ParticleSystem.as Kontrollerar flödet av partiklar: generation, animering och återvinning.
ParticleDesignerPS.as En förlängning av ParticleSystem.as för att möjliggöra enkel manipulation av partikelsystemet.

Vi ska skapa en instans av ParticleDesignerPS.as. Detta kräver inmatning av följande argument i klasskonstruktorn:

  • en XML-fil som innehåller allt initieringsvärde för partikelegenskaper (ganska många av dem)
  • en bild för prov för alla partiklar

Inga bekymmer, onebyonedesign.com hjälper dig med detta. Besök deras partikeldesignersida och tweak alla dessa initieringsvärden till ditt hjärtans innehåll. Exportera sedan all data till ZIP-format. Denna ZIP-fil kommer att innehålla XML-filen och bilden för partikel-effekten du just har utformat via deras webbsida!

Unzip och dra alla dessa i din källkatalog i FlashDevelop. Se de markerade objekten i bilden nedan.

Generera importdeklarationer för att få dessa två objekt till din Testning klass. Du måste också skriva om i det metod i Testning. De är alla nedan.

 [Embed (source = "particle.pex", mimeType = "application / octet-stream")] privat var InitValues: Klass [Inbädda (source = "texture.png")] privat var Prov: Klass
 privat funktion init (e: Event): void removeEventListener (Event.ADDED_TO_STAGE, init); // koden går här. stage.color = 0; // ändra scenfärg till svart var flöde1: ParticleDesignerPS = ny ParticleDesignerPS (XML (new InitValues ​​()), Texture.fromBitmap (new sample ())); addChild (flöde1); flow1.emitterX = stage.stageWidth >> 1; flow1.emitterY = stage.stageHeight >> 1; flow1.start (); Starling.juggler.add (flöde1); 

Här är resultatet du borde komma fram till. Snarare enkelt, rätt?


Steg 4: Förstå partikelsystemet

Vårt nästa steg är att möjliggöra interaktion med partikelsystemet (den lilla elden) vid körning. Vi använder musen för att kontrollera brandens egenskaper. Men innan vi gör, skulle jag vilja sidetracka lite för att korta dig om begreppet partikelsystem.

Partiklar är bara sprites som släpps ut från en koordinat. Efter födseln kommer de att animera i ett visst mönster. Detta mönster kan vara unikt för varje partikel eller vanligt för alla partiklar. Men vara säker, deras fysiska egenskaper kommer att förändras över tiden. Till exempel:

  • Avstånd från födelsekoordinat
  • Riktning och hastighet
  • Färg och alfa nivå
  • Storlek

Dessutom bestäms deras liv på scenen vid födseln. Om varje partikel får leva för alltid på scenen får vi överbefolkning och applikationsprestandan kommer att drabbas på grund av att det finns många grafiska tillgångar att hantera. Vid något tillfälle dör partikeln men det blir inte skövlade av scenen. Det återvinns istället, genom att flyttas till en födelsekoordinat och antar rollen som en ny partikel. Det är en ny partikel eftersom en ny uppsättning egenskaper kommer att definieras för det då innan dess animering sparkar in och en annan cykel fortsätter.

(Detta är objekt poolning, och du kan se hur man applicerar den till egna Flash-projekt utan stjärna här.)

Okej, hur hänför sig detta till vår eld? Tja, vi kan använda easing funktioner för att animera egenskaper av denna eld över tiden. Inom ramen för Starling, ParticleDesignerPS är placerad i slutet av denna hierarki:

ParticleDesignerPS > ParticleSystem > Display > EventDispatcher > Objekt

För att få en balans kommer vi bara spåra ärvda egenskaper från ParticleSystem. Låt oss ta en titt på dessa nästa steg ...


Steg 5: Egenskaper hos ParticleSystem och ParticleDesignerPS

Nedan är egenskaperna hos ParticleSystem.

Fast egendom Beskrivning
kapacitet Maximalpartiklar som systemet kan bära när som helst. Ökar i steg om 500 när antalet partiklar överstiger dess nuvarande kapacitet. Skrivskyddad.
numParticles Antal partiklar i systemet vid en given tidpunkt. Skrivskyddad.
emissionRate Antal partiklar som genereras från födelsekoordinat varje sekund.
emitterX, emitterY Kontrollpunkt för behållaren där alla partiklar lever.

blendFactorSource, blendFactorDestination

Context3DBlendFactor definition för källa och destination. Destination refererar till pixelfärg från senaste render och källa avser ny pixelfärg för att dra till destination.
textur Nuvarande bild samplad som partikelstruktur. Skrivskyddad.

De av ParticleDesignerPS Tabelleras i nästa tabell. De flesta av dessa egenskaper kan finjusteras av start- och slutstatus. Till exempel kommer alla genererade partiklar att initiera med en storlek av 1,0 och avsluta vid 0,1. Däremot kommer partikelflödet vara tråkigt om alla partiklar initieras och avslutas vid sådana liknande tillstånd, så ParticleDesignerPS avsättningar för varians av initialvärdet, och ibland varians på terminvärde också.

Med hänvisning till deras exempel, om vi ger en varians av 0,2 på partikelns ursprungliga storlek, kommer konsekutiva partiklar som fötts eller återvinns i systemet att initiera sin storlek någonstans mellan 0,8 och 1,2 och slutar vid 0,1.

Fast egendom Beskrivning
emitterXVariance, emitterYVariance Variation av födelsekoordinaten.
startSize, startSizeVariance Initial storlek och varians
endSize, endSizeVariance Avslutningsstorlek och varians
emitAngle, emitAngleVariance Partikelns initiala riktning och varians
fart, speedVariance Partikelns initialhastighet och varians
gravityX Acceleration längs x-axeln på alla partikels initialhastighet
gravityY Acceleration längs y-axeln på alla partiklarnas initialhastighet
tangentialAcceleration, tangentialAccelerationVariation Rotationshastighet för partikelhastighet och varians

Det finns två typer av partikelflöde för in ParticleDesignerPS: gravitation och radiell. Visad i tabellen ovan är egenskaper du kan tweak om du använder gravitation flöde. För tyngdkraften är partiklarnas födelsekoordinat belägen vid emitterX och emitterY. För radiell är födelsekoordinaten för partiklar belägen någon punkt bort från emitterX och emitterY, och de rör sig mot Det. Tabellen nedan visar egenskaperna hos radiella flöden.

Fast egendom Beskrivning
maxRadius, maxRadiusVariance Maximal radie från centrum och varians
minRadius Minsta radie från centrum
rotationPerSecond, rotationPerSecondVariance Rotationshastighet på partikelhastighet
Startcolor, startColorVariance Initial färg och varians
EndColor, endColorVariance Avslutande färg och varians

Steg 6: Lägga till interaktivitet

Tja, tack för att du tagit emot den lilla omvägen. Nu för några ActionScript. I i det metod, vi lägger till en lyssnare på scenen för touch-händelser.

 stage.addEventListener (TouchEvent.TOUCH, spår);

Och lyssnaren som nedan.

 privat funktionsspår (e: TouchEvent): void var touch: Touch = e.getTouch (scen); // mapping på scenens koordinatnät // när användaren trycker på musen och rör sig om (touch.phase == TouchPhase.MOVED) // beräkna vinkeln till partikelflödet till var distX: Number = touch.globalX - flow1.emitterX; var distY: Number = touch.globalY - flow1.emitterY; varvinkel: Number = Math.atan2 (distY, distX); t = ny Tween (flöde1, 1,5, övergångar.EASE_OUT_BACK); t.animate ("emitAngle", vinkel); Starling.juggler.add (t); 

För att utföra animeringen, a Tween instans definieras. Dess manipulation liknar den som är populär Tween motorer på många sätt. Sedan lägger vi till det för jonglern av nuvarande förekomst av Starling. Detta jugglerobjekt hjälper till att gradvis uppdatera Tween instans över tiden.

Resultatet är nedan. Klicka och dra musen runt scenen.


Steg 7: Ställa in skepp

Låt oss sätta upp vårt skepp nu och lägga ett spår på den. Tillgångarna är från opengameart.org och jag har inkluderat dem i nedladdningspaketet. Kolla in den här webbplatsen för andra gratis spelkonst.

Vi börjar på nytt med en annan klass, TestingShip.as. Först importera rymdskeppsbilden "boss1.png".

 [Bädda in (source = "boss1.png")] privat var Skepp: Klass

... följt av en liten inställning för att initialisera den i i det metod:

 // konfigurera det grafiska utseendet var shipBMP: Bitmap = nytt Ship () som Bitmap; // importera tillgång till en bmp var shipTEX: Texture = Texture.fromBitmap (shipBMP); // prov bmp som textur till bild var shipIMG: Image = new Image (shipTEX); // bild skapad med textur // inställningsskips orientering och positionsskickaIMG.rotation - = Math.PI * 0.5; // omorientera bildskärmenIMG.x - = shipIMG.width >> 1; // eftersom bildens ursprung ligger längst upp till vänster, shipIMG.y + = shipIMG.height >> 1; // vi flyttar bilden theShip = new Sprite (); // och sätta den i en sprite. Nu är registreringspunkten centrerad. theShip.addChild (shipIMG); // sprite placeras på scenen addChildAt (theShip, 0); // navigationsegenskaperna för skeppet loc = ny vektor2D (stage.stagewidth >> 1, stage.stageHeight >> 1); lof = ny vektor2D (0, 10); updateShip ();

Uppdatera dess position och orientering enligt loc (plats) och lof (synfält).

 privat funktion updateShip (): void theShip.x = loc.x; theShip.y = loc.y; theShip.rotation = lof.getAngle (); 

Återigen, klicka och dra i scenen för att se effekten:


Steg 8: Ställa in avgasröret

Okej, fartygets avgaser ligger ovanpå själva fartyget, och rymdskeppet svarar inte på musevenemanget. Vi fixar det nu. Bara kompensera emitterX och emitterY av partikelflödet något avstånd från rymdskeppet och uppdatera rymdskeppets rotation med användning av lof.

(Anteckna det lof uppdateras på mushändelser. Du kommer att se scriptet nästa steg.)

 privat funktion updateShip (): void theShip.x = loc.x; theShip.y = loc.y; theShip.rotation = lof.getAngle (); // uppdatera partikelspår offset = ny Vector2D (60, 0); offset.setAngle (lof.getAngle ()); flow1.emitterX = loc.x - offset.x; flow1.emitterY = loc.y - offset.y; 

Steg 9: Navigera Ship

Låt oss försöka programmera fartygsnavigering nu vid avsändningen av en mushändelse. Jag har bara kommenterat de viktiga raderna:

 privat funktionsspår (e: TouchEvent): void var touch: Touch = e.getTouch (scen); om (touch.phase == TouchPhase.MOVED) var distX: Number = touch.globalX - flow1.emitterX; var distY: Number = touch.globalY - flow1.emitterY; vinkel = Math.atan2 (distY, distX); t = ny Tween (flöde1, 1,5, övergångar.EASE_OUT_BACK); t.animate ("emitAngle", vinkel + Math.PI); t2 = ny Tween (theShip, 1.5, Transitions.EASE_OUT); t2.moveTo (touch.globalX, touch.globalY); // flytta skeppet t2.onUpdate = refresh // ring på den här funktionen när tween engine kör Starling.juggler.add (t); Starling.juggler.add (t2); // lägg till juggler privatfunktion uppdatera (): void loc.x = theShip.x; // uppdatera platsen loc.y = theShip.y; lof.setAngle (vinkel); // Uppdatera orienteringsuppdateringShip (); // uppdatering 

Och här är en show av slutresultatet. Dra musen runt scenen och rymdskeppet kommer dit.


Steg 10: Animera Avgas

Låt oss finjustera våra avgaser. När fartyget rör sig kommer avgaserna definitivt att blåsa hårdare, eller hur? Vi kan stiga upp emissionRate och fart när du flyttar skeppet och gå ner emissionRate när den är stoppad Här är händelsen, markerad:

 t2 = ny Tween (theShip, 1.5, Transitions.EASE_OUT); t2.moveTo (touch.globalX, touch.globalY); // flytta skeppet t2.onUpdate = refresh // ringa på den här funktionen när tween engine körs t2.onStart = startState // när fartyget börjar flytta t2.onComplete = endState // när fartygsanimering slutar

Och här kallas funktionen på dessa händelser.

 privat funktion startState (): void flow1.emissionRate = 250 flow1.speed = 100;  privat funktion endState (): void flow1.emissionRate = 50 flow1.speed = 10; 

Klicka och dra igen och var uppmärksam på längden på avgasen.


Steg 11: Parallax Effekt

Partiklar kan också uttrycka hur snabbt fartyget rör sig i förhållande till omgivningen. Kolla in resultatet nedan. Klicka och dra musen runt. Observera den hastighet vid vilken de omgivande partiklarna rör sig. De ökar när du interagerar med skeppet och saktar när du slutar interaktionen. De orienterar också deras rotation i enlighet därmed.

Konfigurering av denna effekt är relativt lätt på appen som visas av onebyonedesign.com. Vi måste dock koda lite ActionScript för att ändra det vid körning, och det här kommer att ta de närmaste stegen.


Steg 12: En emitter framför

Initieringen av partiklarna följer ett liknande format som i föregående exempel. Du kan välja att tweak din effekt med appen från onebyonedesign.com och importera till ditt skede. Jag kodar bara rakt i ActionScript för enkelhet.

 envr = ny ParticleDesignerPS (XML (new InitValues ​​()), Texture.fromBitmap (new Sample ())); addChildAt (EnvR, 0); envr.blendFactorSource = Context3DBlendFactor.ONE envr.blendFactorDestination = Context3DBlendFactor.ONE envr.speed = 10; envr.speedVariance = 20; envr.startSize = 15; envr.startSizeVariance = 0; envr.endSize = 20; envr.endSizeVariance = 20 envr.lifespan = 5.0; envr.lifespanVariance = 4.0; envr.emissionRate = 10 envr.start (); Starling.juggler.add (EnvR);

Vi måste också sätta partikelemittaren lite avstånd före skeppet.

 envrLoc = ny Vector2D (100, 0); envrLoc.setAngle (vinkel);

Och uppdatera denna vektor vid körning.

 // uppdatera miljön envr.gravityX = -40 * lof.x; // partikel accelererar i motsatt riktning envr.gravityY = -40 * lof.y; // av siktlinjen envr.emitterX = loc.x + envrLoc.x; envr.emitterY = loc.y + envrLoc.y;

Steg 13: Spridningen

Du förstår emitterXVariance och emitterYVariance hantera axlarna separat. Detta innebär att om vi roterar rymdskeppet behöver vi några sätt att bestämma längden på spridningen längs dessa två axlar.

Kolla nu vektorn för synfält. Det är alltid vinkelrätt mot spridningslinjen (den tunna mörka linjen). Vi kan skala upp denna vektor i enlighet därmed och swizzle dess x och y med de av emitterens varians på startpunkten. Kolla demon nedan. Klicka och dra musen runt. Du ser partikelflödet mer levande.

Slutligen, skala storleken på spridningen och sätt den lite längre före skeppet så att spelarna inte ser sin utsläppspunkt.

 envrLoc = ny Vector2D (200, 0); envrLoc.setAngle (vinkel);
 // uppdatera spread spread = envrLoc.clone (); spread.scale (0,5); envr.emitterXVariance = spread.y; envr.emitterYVariance = spread.x;

Steg 14: Pump upp accelerationen

Slutligen, när fartyget accelererar, låt oss öka storleken på gravityX och gravityY, plus avgasen, följaktligen.

 om (touch.phase == TouchPhase.MOVED) var distX: Number = touch.globalX - flow1.emitterX; var distY: Number = touch.globalY - flow1.emitterY; vinkel = Math.atan2 (distY, distX); // animera avgasen t = ny Tween (flöde1, 1,5, övergångar.EASE_OUT_BACK); t.animate ("emitAngle", vinkel + Math.PI); Starling.juggler.add (t); // kontrollera avgasflödet1.speed = 350; flow1.endSize = 70; // orientera skeppet och parallaxens vinkel lof.setAngle (vinkel); lof.setMagnitude (10); // justera storleken på accelerationen envrLoc.setAngle (vinkel);  om (touch.phase == TouchPhase.ENDED) // kontrollera avgasflödet1.speed = 100; flow1.endSize = 10; lof.setMagnitude (5); // justera accelerationsnivån

Steg 15: Fartyg under attack

När du går vidare i spelet kommer du definitivt att ta träffar och drabbas av skador. Eftersom skador blir svåra kommer du att bränna upp dig. En sådan effekt kan genereras här; vi kan utnyttja emissionXVariance och emissionYVariance att definiera brännare. Jag har markerat dem i koden nedan.

 envr = ny ParticleDesignerPS (XML (new InitValues ​​()), Texture.fromBitmap (new Sample ())); addChildAt (EnvR, 2); envr.blendFactorSource = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA envr.blendFactorDestination = Context3DBlendFactor.ONE; envr.emitterXVariance = theShip.width >> 2; envr.emitterYVariance = theShip.height >> 2; envr.emitAngle = 0; envr.emitAngleVariance = Math.PI; envr.speed = 0; envr.startSize = 40; envr.startSizeVariance = 0; envr.endSize = 10; envr.endSizeVariance = 0 envr.lifespan = 5.0; envr.lifespanVariance = 3.0; envr.emissionRate = 10; envr.start (); Starling.juggler.add (EnvR);

Skadornas svårighetsgrad indikeras av brännsyta och intensitet. Öka och minska emissionRate för att simulera detta. Jag har lagt till kontroller på tangentbordet "A" och "S" för att emulera detta.

 privat funktionskontrollBurn (e: KeyboardEvent): void if (e.keyCode == Keyboard.A) if (envr.emissionRate < 150) envr.emissionRate += 10; if (envr.lifespan < 8) envr.lifespan += 0.5;  if (e.keyCode == Keyboard.S)  if(envr.emissionRate > 10) envr.emissionRate - = 10; om (envr.lifespan> 5) envr.lifespan - = 0.5; 

Observera att om du ökar livslängden hos partiklar, verkar elden brinna mer intensivt. Tja, det tar tid att interpolera partiklar från dess ursprungliga storlek till slutstorlek, så om du ökar livslängden är det längre att övergå från större start till mindre slutstorlek. Eftersom fler stora partiklar stannar på samma plats längre, blandar de sig för att ge intrycket av en mer intensiv eld.


Steg 16: Burning Ship

Tryck på "A" -knappen för att se elden brinna mer intensivt och "S" -knappen för att släcka den något. Utsläppens färg har förändrats för att skilja den från förbränningen:


Steg 17: Exploderat skepp

Alla bra spel måste sluta vid någon tidpunkt. Oavsett vem som elimineras, bör en bra explosion för en målning inte missas. Så vad sägs om ett kärnvampsmoln? Klicka på demo nedan för att se en.

Nu, låt oss koda det.


Steg 18: Radial partikelflöde

Detta partikelflöde är lite annorlunda än de vi har sett. Tidigare har vi använt partikelflödet typ 0 (gravitation) och detta är typ 1 (radial). Partiklarna flyttar faktiskt till mitten med konstant hastighet.

Jag vridde vinkelvariationen till dess högsta så att du kan se att alla de genererade partiklarna bildar en cirkel. Då, genom att animera maximal radie och minsta radie, borde dessa partiklar lever över tiden med hjälp av a Tween, Vi uppnår detta resultat.

 explosion = new ParticleDesignerPS (XML (new InitValues ​​()), Texture.fromBitmap (new sample ())); addChild (explosion); explosion.emitterX = stage.stageWidth >> 1; explosion.emitterY = stage.stageHeight >> 1; explosion.emitterType = 1; explosion.emitAngle = 0; explosion.emitAngleVariance = Math.PI; explosion.maxRadius = 10; explosion.maxRadiusVariance = 0; explosion.minRadius = 0;

Här är koden för att utföra animeringen.

 privat funktionsspår (e: TouchEvent): void var touch: Touch = e.getTouch (scen); om (touch.phase == TouchPhase.BEGAN) explosion.emitterX = touch.globalX; explosion.emitterY = touch.globalY; explosion.start (); t = ny Tween (explosion, 1,0, Transitions.EASE_IN); t.animate ("maxRadius", 150); t.animate ("minRadius", 130); t.onStart = frys t.onComplete = återställ; Starling.juggler.add (t);  privata funktionsfrysning (): void stage.removeEventListener (TouchEvent.TOUCH, track);  Återställning av privat funktion (): void stage.addEventListener (TouchEvent.TOUCH, spår); explosion.stop (); explosion.maxRadius = 10; explosion.minRadius = 0; 

Steg 19: Recap

Så det här har varit en lång handledning. Låt oss göra lite omslag här. Vi har gått igenom:

  • Ställa in Starling och dess partikelförlängning
  • Egenskaper hos partikelsystemet
  • Exempel på egendomsmanipulationer baserat på scenariet i ett skjutspel.

Slutsats

Vi har täckt en hel del här. Men en viktig aspekt som jag inte har gått igenom är att sträcka sig från ParticleSystem. Detta kommer verkligen att ge dig möjlighet att koda dina egna partikelstigar istället för att förlita sig på ParticleDesignerPS. Jag måste skjuta upp det här till en annan handledning.

Tack för att du läser och ser dig i nästa handledning. Lämna kommentarer om fel och den här partikelmotorens användning i ditt projekt om du väljer att anta det.