Manipulera partikelrörelse med Stardust partikelmotor - Del 1

Stardust partikelmotor ger två stora metoder för att fritt manipulera partikelrörelse, nämligen gravitationsfält och deflektorer. Gravitationsfält är vektorfält som påverkar partikelens acceleration, och deflektorer manipulerar både partikelns position och hastighet.

Den första delen av denna handledning täcker grunderna för partikelrörelse och gravitationsfält. Det demonstrerar också hur man skapar egna anpassade gravitationsfält. Den andra delen fokuserar på deflektorer och hur man skapar anpassade deflektorer.

Förkunskaper om grundläggande användning av Stardust krävs för att fortsätta läsa denna handledning. Om du inte känner till Stardust kan du kolla in min tidigare handledning om ämnet, skjut ut stjärnor med Stardust Particle Engine innan du fortsätter.


Slutresultatförhandsvisning

Låt oss ta en titt på det slutliga resultatet vi kommer att arbeta för. Detta är ett exempel på ett anpassat vortex gravitationsfält.


Particle Motion Basics

Det är dags för en snabb återgång på gymnasiet. Kom ihåg grundläggande kinematik? Det handlar om förskjutning, vilket bara är ett mer avancerat sätt att säga "position" och dess relation till tiden. För omfattningen av denna handledning behöver vi bara en ganska enkel grepp om ämnet.

Förflyttning

Förskjutning betyder den aktuella positionen för ett objekt. I denna handledning är de "föremål" som vi huvudsakligen hanterar partiklar i 2D-utrymme. I 2D-utrymme representeras förskjutningen av ett objekt av en 2D-vektor.

Hastighet

Ett objekts hastighet anger hur snabbt ett objekts position förändras och förändringsriktningen. Hastigheten hos ett objekt i 2D-utrymme representeras också av en 2D-vektor. Vektorns x- och y-komponenter representerar riktningsändringen och vektorens absoluta värde anger objektets hastighet, dvs hur snabbt objektet rör sig.

Acceleration

Accelerationen är till hastighet, eftersom hastigheten är förskjutning. Accelereringen av ett objekt anger hur snabbt ett objekts hastighet förändras och förändringsriktningen. Precis som ett objekts hastighet i 2D-rymden representeras ett objekts acceleration av en 2D-vektor.


Vector Fields

En annan sak som är värt att nämna är begreppet vektorfält. Du kan i princip se ett vektorfält som en funktion som tar en vektor som dess inmatning och matar ut en annan vektor. Gravitationsfält är ett slags vektorfält som tar positionsvektorer som ingångar och utgångsaccelerationsvektorer. I fysiksimulering används till exempel en enhetlig tyngdkraft som pekar nedåt på alla föremål. i detta fall är gravitationsfältet som representerar gravitationen ett vektorfält som matar ut en konstant vektor (pekar ner), oavsett vad ingångspositionsvektorerna är.

Detta är en visualiserad graf för ett vektorfält. Utgångsvektorn för en given ingångsvektor (1,2) är (0,5, 0,5), medan utmatningen från en ingång (4, 3) är (-0,5, 0,5).

De Fält klass i Stardust representerar ett 2D-vektorfält och dess 3D-motsvarighet, Field3D klassen representerar ett 3D-vektorfält. I denna handledning fokuserar vi bara på 2D-vektorfält.

De Field.getMotionData () metoden tar a Particle2D objekt, som innehåller en partikel 2D-position och hastighetsinformation, som parameter. Denna metod returnerar a MotionData2D objekt, ett 2D-vektor "värdeobjekt" som består av x- och y-komponenter. Kombinerat med Allvar åtgärd, a Fält objekt kan användas som ett gravitationsfält, vars utgång används för att manipulera partikelhastigheten.

Det är allt för vår gymnasieteknik. Nu är det dags för några riktiga Stardust-saker.


Gravity Action

Som tidigare nämnts, Allvar Action klass använder sig av Fält objekt som gravitationsfält för att manipulera partikelhastigheten. Så här skapar du ett enhetligt vektorfält, ett vektorfält som returnerar en konstant vektor, oavsett vilken ingång som ges och omsluter den till en Allvar verkan.

 // skapa ett enhetligt vektorfält som pekar nedåt (kom ihåg positiva y-koordinater betyder "ner" i Flash) var-fältet: Fält = nytt UniformField (0, 1); // skapa en gravitation action gravitation: Gravity = new Gravity (); // lägg fältet till gravitationen action gravity.addField (fält);

Detta Allvar Åtgärden är nu redo att användas på samma sätt som alla vanliga Stardust-åtgärder.

 // Lägg gravitationen till en emittentemitter.addAction (gravitation);

Exempel: Vindregn

Vi ska skapa en blåsig regn effekt med hjälp av Allvar verkan.


Steg 1: Grundläggande regnaeffekt

Skapa ett nytt Flash-dokument, välj en mörk färg för bakgrund, dra ett regndroppe på scenen och konvertera regndroppen till en filmklippssymbol som exporteras för ActionScript med ett klassnamn "Regndroppe". Ta bort regndroppsinstansen på scenen efteråt.

Nu ska vi skapa en AS-fil för dokumentklassen och en AS-fil för regndemitorn. Jag kommer inte att förklara koden här, eftersom jag redan har täckt den grundläggande användningen av Stardust i min tidigare handledning. Om du inte känner till Stardust eller behöver lite förfriskning rekommenderar jag starkt att du läser min tidigare handledning innan du går vidare.

Detta är dokumentklassen.

 paket import flash.display. *; importera flash.events. *; importera idv.cjcat.stardust.common.emitters. *; importera idv.cjcat.stardust.common.renderers. *; importera idv.cjcat.stardust.twoD.renderers. *; offentlig klass WindyRain utökar Sprite private var emitter: Emitter; privat var renderer: Renderer; offentlig funktion WindyRain () emitter = new RainEmitter (); renderer = ny DisplayObjectRenderer (this); renderer.addEmitter (emitter); addEventListener (Event.ENTER_FRAME, mainLoop);  privat funktion mainLoop (e: Event): void emitter.step (); 

Och det här är emitterklassen som används i dokumentklassen.

 paket import idv.cjcat.stardust.common.clocks. *; importera idv.cjcat.stardust.common.initializers. *; importera idv.cjcat.stardust.common.math. *; importera idv.cjcat.stardust.twoD.actions. *; importera idv.cjcat.stardust.twoD.emitters. *; importera idv.cjcat.stardust.twoD.initializers. *; importera idv.cjcat.stardust.twoD.zones. *; offentlig klass RainEmitter utökar Emitter2D offentlig funktion RainEmitter () super (new SteadyClock (1)); // initialisatorer addInitializer (nya DisplayObjectClass (regndroppe)); addInitializer (ny position (ny RectZone (-300, -40, 940, 20))); addInitializer (ny hastighet (ny RectZone (-0,5, 2, 1, 3))); addInitializer (new Mass (new UniformRandom (2, 1))); addInitializer (new Scale (new UniformRandom (1, 0.2))); // åtgärder addAction (new Move ()); addAction (new Oriented (1, 180)); addAction (ny DeathZone (ny RectZone (-300, -40, 960, 480), true)); 

Så här ser vår nuvarande framsteg ut.


Steg 2: Gör det Blåsigt

Nu ska vi göra regnet effekt blåsigt genom att lägga till ett enhetligt gravitationsfält som "drar" regndropparna till höger. Lägg till följande kod i konstruktören av RainEmitter klass.

 // skapa ett enhetligt fält som alltid returnerar (0.5, 0) var-fält: Fält = nytt UniformField (0,5, 0); // ta hänsyn till partikelmassa field.massless = false; // skapa en gravitation och lägg till fältet till det var gravitation: Gravity = new Gravity (); gravity.addField (fält); // Lägg gravitationen till sändarens addAction (gravitation);

Observera att vi ställer in Field.massless egendom till falskt, vilket är sant som standard. När den är satt till sann, orsakar den här egenskapen fält att fungera som vanliga gravitationsfält, vilket påverkar alla objekt lika oavsett deras massa. När egenskapen är inställd på falskt beaktas partikelmassa: partiklar med större massa påverkas mindre av fältet, medan partiklar med mindre massa påverkas mer. Det är därför vi använde Massa initialiseraren i vår tidigare emitterkod, för att lägga till lite slumpmässig inverkan i regneffekten.

Testa filmen igen, och det här är vad vårt resultat ser ut. Regndroppar påverkas nu av ett gravitationsfält och alla "dras" till höger.


Exempel: Turbulens

Nu ska vi byta ut UniformField objekt med a BitmapField objekt, retur vektorfält baserat på en bitmapps färgkanaler, för att skapa en turbulenseffekt. Om du har arbetat med ActionScript ett tag kan du förvänta dig att använda BitmapData.perlinNoise () metod när man tänker på turbulens, och det är precis vad vi ska göra.

Här ser du hur ett exemplar Perlin brusbitmap ser ut. Perlin brus är en utmärkt algoritm för att generera ljud för att simulera turbulens, vattenvåg, moln, etc. Du kan hitta mer information om Perlin brus här.

Bitmappsfält

Du kanske undrar om vi ska använda BitmapData.perlinNoise () metod för att generera en bitljud perlin brus, hur ska vi använda denna bitmapp som ett vektorfält? Tja, det här är vad BitmapField klassen är för. Det tar en bitmapp och omvandlar den till ett vektorfält.

Låt oss säga att vi har ett bitmappsfält vars X-kanal är satt till röd och Y-kanal är grön. Detta betyder att när fältet tar en ingångsvektor, säg (2, 3), ser den upp till bitmappens pixel vid (2, 3) och utgångsvektorns X-komponent bestäms från pixelens röda kanal och Y-komponenten är bestämd från den gröna kanalen. När komponenterna i en ingångsvektor inte är heltal, avrundas de först.

En färgkanals värde varierar mellan 0 och 255, 127 är genomsnittet. Ett värde mindre än 127 anses vara negativt av bitmappsfältet, medan ett värde större än 127 är positivt. Noll är den mest negativa nummer och 255 är mest positiva ett. Om vi ​​till exempel har en pixel med en färg 0xFF0000 i hexadecimal representation, vilket betyder en röd kanal med värde 255 och grön kanal med 0, skulle bitmappsfältets utdata för denna pixel vara en vektor med X-komponenten i en mest positiva möjligt tal och Y-komponent i a mest negativa möjligt antal, var detta mest positiva / negativa möjliga antal, eller maximalt antal, är specifik för bitmappsfältet. För att vara mer exakt, här är formeln för pixel-till-vektor konvertering.


Steg 1: Grundläggande flygpilar

Skapa ett nytt Flash-dokument. Rita en pil på scenen, konvertera den till en symbol som heter "Arrow" och exportera den till ActionScript.

Skapa en AS-fil för dokumentklassen. Detta är nästan detsamma som föregående exempel.

 paket import flash.display. *; importera flash.events. *; importera idv.cjcat.stardust.common.emitters. *; importera idv.cjcat.stardust.common.renderers. *; importera idv.cjcat.stardust.twoD.renderers. *; offentlig klass Turbulens sträcker sig Sprite private var emitter: Emitter; privat var renderer: Renderer; allmän funktion Turbulens () emitter = new ArrowEmitter (); renderer = ny DisplayObjectRenderer (this); renderer.addEmitter (emitter); addEventListener (Event.ENTER_FRAME, mainLoop);  privat funktion mainLoop (e: Event): void emitter.step (); 

Skapa en annan AS-fil för vår emitterklass.

 paket import idv.cjcat.stardust.common.actions. *; importera idv.cjcat.stardust.common.clocks. *; importera idv.cjcat.stardust.common.initializers. *; importera idv.cjcat.stardust.common.math. *; importera idv.cjcat.stardust.twoD.actions. *; importera idv.cjcat.stardust.twoD.emitters. *; importera idv.cjcat.stardust.twoD.initializers. *; importera idv.cjcat.stardust.twoD.zones. *; allmän klass ArrowEmitter utökar Emitter2D allmän funktion ArrowEmitter () super (new SteadyClock (1)); // initialisatorer addInitializer (new DisplayObjectClass (Arrow)); addInitializer (nytt liv (nytt UniformRandom (50, 10))); addInitializer (new Position (new SinglePoint (320, 200))); addInitializer (new Velocity (new LazySectorZone (3, 2))); addInitializer (new Mass (new UniformRandom (2, 1))); addInitializer (new Scale (new UniformRandom (1, 0.2))); // åtgärder addAction (new Age ()); addAction (new DeathLife ()); addAction (new Move ()); addAction (new Oriented ()); addAction (ny ScaleCurve (10, 10)); 

Nuvarande framsteg ser ut så här.


Steg 2: Gör det turbulent

Lägg till följande kod som skapar en 640-by-480 Perlin brusbitmapdata i emitterkonstruktorn. För utförliga förklaringar om varje parameter i BitmapData.perlinNoise () metod, kan du hänvisa till den här dokumentationen. För att göra det enkelt skapar följande kod en Perlin brusbitmap med "oktav" ungefär av storleken 50X50, och bruset består av röda och gröna färgkanaler.

 // skapa ett Perlin brus bitmapp data var brus: BitmapData = ny BitmapData (640, 400); noise.perlinNoise (50, 50, 1, 0, true, true, 1 | 2);

Skapa sedan en BitmapField objekt och tilldela bitmappsdata till det via uppdatering() metod. Då är resten av koden gällande Allvar åtgärden är exakt samma som föregående exempel.

 // skapa ett enhetligt fält som alltid returnerar (0.5, 0) var-fältet: BitmapField = nytt BitmapField (); field.channelX = 1; // Ange X-kanal till röd field.channelY = 2; // Ange Y-kanal till grönt fält.max = 1; // Ange maxvärdet för vektorkomponentets absolutvärde field.update (brus); // uppdatera fältet med brusbitmappen // ta hänsyn till partikelmassa field.massless = false; // skapa en gravitation och lägg till fältet till det var gravitation: Gravity = new Gravity (); gravity.addField (fält); // Lägg gravitationen till sändarens addAction (gravitation);

Testa nu filmen igen, och du ska se att våra flygande pilar nu upplever turbulens.


Anpassade fält

Vi har använt UniformField och BitmapField levereras av Stardust, och nu ska vi skapa egna egna fält genom att utöka Fält klass och övertyga getMotionData2D () metod.

De getMotionData2D tar a Particle2D parameter som ingång, som innehåller partikelpositionspositionsinformationen, och det är inmatningen till fältet. Metoden returnerar a MotionData2D objekt som representerar uteffekten i fältet. Det är allt du behöver veta för att skapa ett anpassat fält. Låt oss skapa ett anpassat virvelfält.

Nedan är det visualiserade diagrammet för vårt virvelfält. Det är ganska självförklarande varför det kallas ett virvelfält.

Här är formeln för vårt virvelområde.

Och vortexfältet klass är lika enkelt som koden nedan. Vi har utnyttjat Vec2D klass för att göra allt det smutsiga arbetet med att rotera en vektor med 90 grader medurs och ställa in vektorens absoluta värde. Sedan dumpar vi vektorns x- och y-komponenter i en MotionData2D objekt, vilket är av objekttypen som ska returneras.

 paket import idv.cjcat.stardust.twoD.fields. *; importera idv.cjcat.stardust.twoD.geom. *; importera idv.cjcat.stardust.twoD.particles. *; public class VortexField utökar fält public var centerX: Number; offentliga var centerY: Number; offentlig var styrka: nummer; allmän funktion VortexField (centerX: Number = 0, centerY: Number = 0, strength: Number = 1) this.centerX = centerX; this.centerY = centerY; this.strength = styrka;  åsidosätta skyddad funktion calculateMotionData2D (partikel: Particle2D): MotionData2D var dx: Number = particle.x - centerX; var dy: Number = particle.y - centerY; var vec: Vec2D = ny Vec2D (dx, dy); vec.length = styrka; vec.rotateThis (90); returnera nya MotionData2D (vec.x, vec.y); 

Nu när vi har vårt anpassade fält, låt oss testa det i följande exempel.


Exempel: Vortex

Detta exempel fungerar som en provkörning för vårt virvelvektorfält. Det är lika enkelt som att byta ett fält med en ny. Ändra följande kod från det föregående exemplet från det här

 // skapa ett enhetligt fält som alltid returnerar (0.5, 0) var-fältet: BitmapField = nytt BitmapField (); field.channelX = 1; // Ange X-kanal till röd field.channelY = 2; // Ange Y-kanal till grönt fält.max = 1; // ställa in max vecotr längd field.update (brus); // uppdatera fältet med brusbitmapen

till detta

 // skapa ett virvelfält centrerat vid (320, 200) med styrka 1 var-fält: VortexField = nytt VortexField (); field.centerX = 320; fält.centerY = 200; field.strength = 1;

Och vi är klara. Du kan testa filmen och se virvel effekten. Ljuv!


Slutsats

Du har sett hur du använder Allvar åtgärd och Fält klass för att påverka partikelhastigheten. Och du har lärt dig hur man skapar egna anpassade vektorfält för att användas som gravitationsfält. Nästa del av denna handledning visar dig hur man använder deflektor för att manipulera både partikelposition och hastighet på en gång.

Tack så mycket för att du läser!