I denna handledning presenterar jag dig för Stardust Particle Engine. Först ska jag visa dig hur du sätter upp Stardust, och sedan täcker jag de grundläggande Stardust-gruppansvaren och hur de samarbetar för att göra Stardust-arbetet i sin helhet.
Därefter kommer vi att titta på en Stardusts allmänna arbetsflöde och komma ner för att skapa en partikeleffekt med stjärnor som skjuter ut från muspekaren. stjärnorna kommer att sakta gradvis, växa större efter födseln och krympa när de dör.
Slutligen demonstrerar jag flexibiliteten hos Stardust genom att skapa flera variationer från det redan fullständiga exemplet, inklusive att använda animerade filmklipp som partiklar, variabel partikel-simuleringstidsskala och skjuta ut visningsobjekt av olika klasser från en enda emitter.
Denna handledning är avsedd för personer som redan är bekanta med ActionScript 3.0 objektorienterad programmering (OOP), så jag antar att du redan vet väldigt bra vilka klasser, objekt, arv och gränssnitt som betyder. Inget problem med OOP? Låt oss sedan skjuta några stjärnor!
Som namnet antyder, används Stardust för att skapa partikeleffekter. Om du är en erfaren ActionScripter kanske du har skapat partikeleffekter från början, och säger "Jag är helt cool med att skapa partikeleffekter från början, så varför skulle jag behöva en partikelmotor i alla fall?" Jo, Stardust är här för att hjälpa dig att fokusera mer på verkliga partikelbeteenden än att oroa dig för de tråkiga underliggande lågnivåerna, till exempel minneshantering. I stället för att skriva kod för att ta hand om partikeldata, initiera och använda resurser, med Stardust, kommer du att hoppa över dessa tråkiga rutiner och bara bestämma hur du vill att dina partiklar ska uppträda.
Klassstrukturen i Stardust inspirerades av FLiNT Particle System, en annan ActionScript 3.0 partikelmotor. De delar sålunda några liknande grundläggande funktioner.
Förutom dessa grundläggande funktioner ger Stardust flera avancerade funktioner för erfarna användare.
När det gäller partikel effekter är det mycket viktigt att hantera massiva partikeldata effektivt. Stardust gör stor användning av objektpooler och länkade listor för att förbättra prestanda:
Innan vi kommer ner till den faktiska kodningen måste vi ta en kopia av Stardust Particle Engine. Det släpps ut under MIT-licens, vilket betyder att det är helt gratis, oavsett om du vill använda det i ett kommersiellt eller icke-kommersiellt projekt.
Här är Stardusts projektwebbplats: http://code.google.com/p/stardust-particle-engine/
Du kan ladda ner Stardust här: http://code.google.com/p/stardust-particle-engine/downloads/list
Vid skrivningstillfället är den senaste versionen som kan hämtas från nedladdningslistan 1.1.132 Beta. Du kan alltid fånga den senaste versionen från SVN-förvaret (vilket kanske inte är stabilt).
På projektets hemsida kan du också hitta fler tillbehör som API-dokumentation och en kopia av PDF-manualen. Det finns även videotutorials på YouTube.
Här kommer jag kortfattat att täcka Stardust-kärnklasserna och deras ansvar.
Denna klass är superklassen för alla kärnklasser, som definierar egenskaper och metoder speciellt för XML-serialisering.
I allmänhet handlar partikeleffekter om att styra en mängd enheter med liknande men slumpmässigt utseende och beteende. Den slumpmässiga klassen är för att generera slumptal, som kan användas i Stardust för randomisering av partikelegenskaper. UniformRandom-klassen är till exempel en underklass i Random-klassen och namnet berättar allt: det slumptal som genereras av ett UniformRandom-objekt är jämnt fördelat, och jag kommer att använda den här klassen speciellt för hela handledningen.
Det finns tillfällen då ett ettdimensionellt slumptal inte räcker. Ibland behöver vi tvådimensionella slumptal, som i huvudsak är par av slumptal, för egenskaper som position och hastighet. Zonklassen är för att generera tvådimensionella slumptalspar. Denna klass modellerar ett slumptalspar som en slumpmässig punkt i en 2D-zon. Cirkelsonen genererar till exempel slumptalspar (x, y) från slumpmässiga punkter i en cirkulär region. Random och Zone klasserna används huvudsakligen av Initializer-klassen, som kommer att täckas senare. Zone3D-klassen är 3D-motsvarigheten till den här klassen, för 3D-partikeleffekter.
Emitter klassen är i grunden där alla lågnivån är inkapslade. En emitter initierar nyskapade partiklar innan de läggs till i simuleringen, uppdaterar partikelegenskaper i varje huvudlocks iteration och tar bort döda partiklar från simuleringen. Metoden Emitter.step () är vad du vill åberopa flera gånger för att hålla Stardust igång.
Klockklassen bestämmer graden av ny partikelskapande för sändare. Ett Emitterobjekt innehar exakt en referens till ett klockobjekt. I början av varje Emitter.step () metodsamtal frågar emitorn klockobjektet hur många nya partiklar den ska skapa. Ta till exempel SteadyClock-klassen, det berättar emittenter att skapa nya partiklar i en konstant takt.
Denna klass är för initiering av nyskapade partiklar. Ett Initializer-objekt måste läggas till en emitter för att det ska fungera. I grunden initialiserar ett Initializer-underklass endast en partikelegenskap. Till exempel initialiserar massinitierarklassen massan av nya partiklar. Vissa initialiserare accepterar ett slumpmässigt objekt som en konstruktorparameter för initiering av partiklar med slumpmässiga värden. Följande kod skapar en Life initializer som initierar partikelliv till värden centrerad vid 50 med variation av 10, nämligen mellan intervallet 40 till 60.
nytt liv (nytt UniformRandom (50, 10));
Åtgärdsobjekt uppdaterar partikelegenskaper i varje iteration av huvudslingan (Emiter.step () -metoden). Till exempel uppdaterar Action Action-klassen partikelpositioner enligt hastighet. Ett åtgärdsobjekt måste läggas till en emitter för att det ska fungera.
Nu när du vet hur kärnklasserna samarbetar tillsammans, låt oss ta en titt på ett generellt arbetsflöde för Stardust.
Du börjar med att skapa en emitter. Använd Emitter2D-klassen för 2D-partikeleffekter och Emitter3D-klassen för 3D-effekter.
var emitter: Emitter = ny Emitter2D ();
För att ange graden av partikelskapande behöver vi en klocka. Detta kan antingen ställas in av egenskapen Emitter.clock eller genom att överföra en klocka som den första parametern till emittentens konstruktör.
// property approach emitter.clock = nytt SteadyClock (1); // Constructor approach var emitter: Emitter = Ny Emitter2D (New SteadyClock (1));
Lägg till initialisatorer till emitern genom metoden Emitter.addInitializer ().
emitter.addInitializer (New Life (New UniformRandom (50, 10))); emitter.addInitializer (new Scale (new UniformRandom (1, 0.2)));
Lägg till åtgärder i emitern genom metoden Emitter.addAction ().
emitter.addAction (new Move ()); emitter.addAction (new Spin ());
Skapa en renderer och lägg emitteren till renderaren genom metoden Renderer.addEmitter ().
var renderer: Renderer = ny DisplayObjectRenderer (container); // "behållare" är vår behållare sprite renderer.addEmitter (emitter);
Slutligen, ring upprepade gånger med Emitter.step () -metoden för att hålla partikel-simuleringen på gång. Du kanske vill använda enter-frame-händelsen eller en timer för att göra detta. I en enda anrop av Emitter.step () -metoden bestämmer klockan hur många nya partiklar som ska skapas, dessa nya partiklar initieras av initieringsmedel, alla partiklar uppdateras av åtgärder, döda partiklar avlägsnas och slutligen gör renderaren partikel effekten.
// enter-frame händelse tillvägagångssätt addEventListener (Event.ENTER_FRAME, mainLoop); // timer-inställningstimer.addEventListener (TimerEvent.TIMER, mainLoop); funktion mainLoop (e: Event): void emitter.step ();
OK. Det är ganska mycket allt för Stardust primer. Nu är det dags att öppna Flash IDE och få dina händer smutsiga.
Skapa ett nytt Flash-dokument med en dimension på 640X400 en bildfrekvens på 60fps och en mörk bakgrund. Här gjorde jag en mörkblå gradientbakgrund. Förresten fungerar Stardust bra med både Flash Player 9 och 10, så det är okej, oavsett om du använder Flash CS3 eller CS4. I den här handledningen använder jag Flash CS3.
Vi skapar en partikeleffekt med stjärnor, så vi måste dra en stjärna och konvertera den till en symbol, som naturligtvis exporteras till ActionScript. Denna symbol kommer att användas senare för att göra vår partikel effekt. Namn på symbolen och den exporterade klassen "Star".
Skapa en ny dokumentklass och namnge den StarParticles.
paket import flash.display.Sprite; offentlig klass StarParticles utökar Sprite public function StarParticles ()
Som nämnts i det allmänna arbetsflödet är det första steget att skapa en emitter. Och nästa steg är att lägga till initialisatorer och åtgärder till emitern. Även om detta kan göras i dokument klasskonstruktören rekommenderar jag starkt att det görs i en separat Emitter-underklass. Det är alltid bättre att skilja utformningen av partikelbeteendet från huvudprogrammet. Genom att göra så är koden mycket renare och lättare att ändra i framtiden utan att vara blandad med huvudprogrammet.
Vi ska skapa en 2D-partikeleffekt, så Emitter2D är emitterklassen vi kommer att utöka. Utvid Emitter2D-klassen och namnge den StarEmitter, eftersom vi ska göra det skjuta ut stjärnor senare. Emitterkonstruktören accepterar en klockparameter, så vi kommer att deklarera en konstruktorparameter för att vidarebefordra en klockobjektreferens till superklassens konstruktör.
paket import idv.cjcat.stardust.twoD.emitters.Emitter2D; offentlig klass StarEmitter utökar Emitter2D offentlig funktion StarEmitter (klocka: Klocka) // överför klockobjektet till superklassens konstruktör super (klocka);
Ett bättre tillvägagångssätt att skapa en emitter-underklass är att deklarera partikelparametrar som statiska konstanter, grupperade på en enda plats. Så om du vill tweak parametrarna vet du alltid var du ska hitta deklarationerna. Betydelsen av dessa konstanter kommer att förklaras senare när de används.
// genomsnittlig livslängd privat statisk konst LIFE_AVG: Number = 30; // livslängdsvarig privat statisk const LIFE_VAR: Number = 10; // genomsnittlig skala privat statisk konst SCALE_AVG: Number = 1; // skala variation privat statisk konst SCALE_VAR: Number = 0.4; // skala odlingstid privat statisk const GROWING_TIME: Number = 5; // skala krympningstid privat statisk const SHRINKING_TIME: Number = 10; // medelhastighet privat statisk const SPEED_AVG: Number = 10; // hastighetsvariationen privat statisk const SPEED_VAR: Number = 8; // genomsnittlig omega (vinkelhastighet) privat statisk konst OMEGA_AVG: Number = 0; // omega variant privat statisk konst OMEGA_VAR: Number = 5; // dämpningskoefficient privat statisk konst DAMPING: Number = 0.1;
Vilka initialiserare behöver vi skapa vår partikel effekt? Låt oss ta en titt på listan nedan:
Och här är koden:
punkt = ny SinglePoint (); addInitializer (new DisplayObjectClass (Star)); addInitializer (nytt liv (nytt UniformRandom (LIFE_AVG, LIFE_VAR))); addInitializer (new Scale (new UniformRandom (SCALE_AVG, SCALE_VAR))); addInitializer (ny position (punkt)); addInitializer (new Velocity (new LazySectorZone (SPEED_AVG, SPEED_VAR))); addInitializer (new Rotation (new UniformRandom (0, 180))); addInitializer (new Omega (new UniformRandom (OMEGA_AVG, OMEGA_VAR)));
Okej, vi är klara med initialisatorerna. Nu är det dags att lägga till åtgärder till emitern. Nedan finns en lista över åtgärder vi behöver:
Det är allt. Vår emitter är klar. Här är koden för den här emitteren i dess helhet, nödvändiga importmeddelanden inkluderade.
paket import idv.cjcat.stardust.common.actions.Age; importera idv.cjcat.stardust.common.actions.DeathLife; importera idv.cjcat.stardust.common.actions.ScaleCurve; importera idv.cjcat.stardust.common.clocks.Clock; importera idv.cjcat.stardust.common.initializers.Life; importera idv.cjcat.stardust.common.initializers.Scale; importera idv.cjcat.stardust.common.math.UniformRandom; importera idv.cjcat.stardust.twoD.actions.Damping; importera idv.cjcat.stardust.twoD.actions.Move; importera idv.cjcat.stardust.twoD.actions.Spin; importera idv.cjcat.stardust.twoD.emitters.Emitter2D; importera idv.cjcat.stardust.twoD.initializers.DisplayObjectClass; importera idv.cjcat.stardust.twoD.initializers.Omega; importera idv.cjcat.stardust.twoD.initializers.Position; importera idv.cjcat.stardust.twoD.initializers.Rotation; importera idv.cjcat.stardust.twoD.initializers.Velocity; importera idv.cjcat.stardust.twoD.zones.LazySectorZone; importera idv.cjcat.stardust.twoD.zones.SinglePoint; offentlig klass StarEmitter utökar Emitter2D / ** * Konstanter * / privat statisk const LIFE_AVG: Number = 30; privat statisk const LIFE_VAR: Number = 10; privat statisk konst SCALE_AVG: Number = 1; privat statisk konst SCALE_VAR: Number = 0.4; privat statisk const GROWING_TIME: Number = 5; privat statisk konst SHRINKING_TIME: Number = 10; privat statisk const SPEED_AVG: Number = 10; privat statisk const SPEED_VAR: Number = 8; privat statisk konst OMEGA_AVG: Number = 0; privat statisk konst OMEGA_VAR: Number = 5; privat statisk konst DAMPING: Number = 0.1; allmän var punkt: SinglePoint; offentlig funktion StarEmitter (klocka: Klocka) super (klocka); punkt = ny SinglePoint (); // initialisatorer addInitializer (new DisplayObjectClass (Star)); addInitializer (nytt liv (nytt UniformRandom (LIFE_AVG, LIFE_VAR))); addInitializer (new Scale (new UniformRandom (SCALE_AVG, SCALE_VAR))); addInitializer (ny position (punkt)); addInitializer (new Velocity (new LazySectorZone (SPEED_AVG, SPEED_VAR))); addInitializer (new Rotation (new UniformRandom (0, 180))); addInitializer (new Omega (new UniformRandom (OMEGA_AVG, OMEGA_VAR))); // åtgärder addAction (new Age ()); addAction (new DeathLife ()); addAction (new Move ()); addAction (new Spin ()); addAction (new Damping (DAMPING)); addAction (ny ScaleCurve (GROWING_TIME, SHRINKING_TIME));
Nu är det dags att gå tillbaka till dokumentklassen och avsluta det. Låt oss ta en titt på de återstående uppgifterna.
Nedan finns den fullständiga koden för dokumentklassen, nödvändiga importmeddelanden inkluderade.
paket import flash.display.Sprite; importera flash.display.StageScaleMode; importera flash.events.Event; importera flash.geom.Rectangle; importera idv.cjcat.stardust.common.clocks.SteadyClock; importera idv.cjcat.stardust.common.renderers.Renderer; importera idv.cjcat.stardust.twoD.renderers.DisplayObjectRenderer; offentlig klass StarParticles utökar Sprite private var emitter: StarEmitter; offentlig funktion StarParticles () // instantiate StarEmitter emitter = ny StarEmitter (new SteadyClock (0.5)); // behållarens sprite var container: Sprite = new Sprite (); // den renderare som gör partikel effekten var renderer: Renderer = ny DisplayObjectRenderer (container); renderer.addEmitter (emitter); // lägg till behållaren till visningslistan ovanför bakgrunden addChildAt (behållare, 1); // utnyttja enter-frame händelsen addEventListener (Event.ENTER_FRAME, mainLoop); privat funktion mainLoop (e: Event): void // uppdatera SinglePoint-positionen till muspositionen emitter.point.x = mouseX; emitter.point.y = mouseY; // ring huvudlösen emitter.step ();
Slutligen är vi färdiga! Låt oss nu titta på resultatet. Tryck CTRL + ENTER i Flash för att testa filmen, så ser du resultatet.
Vi är inte färdiga ännu! Låt oss göra några fler variationer. Den första använder animerade filmklipp till våra partiklar.
Denna första variationen är ganska enkel, utan att någon extra kodning ingår. Det är lika enkelt som att skapa en grundläggande tidslinjeanimation. Redigera stjärnasymbolen i Flash IDE, skapa en annan nyckelram och ändra stjärnans färg i den här ramen till röd. Detta orsakar i huvudsak stjärnorna att blinka mellan gult och rött. Du kanske vill infoga några fler tomma ramar mellan, eftersom en bildhastighet på 60 bilder per sekund är för snabb för att två-ramar blinkar.
Testa nu filmen och kolla resultatet. Den blinkande stjärn-effekten ser skönhet ut; Detta kan användas för klassiska svimma-stjärniga effekter, vilket vanligtvis ses i tecknade film.
Som jag nämnde tidigare är en av Stardust-funktionen "justerbar simuleringstidskala", vilket innebär att tidsskala som används av Stardust för partikel-simulering kan justeras dynamiskt. Allt görs genom att ändra egenskapen Emitter.stepTimeInterval, som är 1 som standard. Följande kodbitning ändrar detta värde till 2, vilket resulterar i att partiklar rör sig två gånger så fort och emitterar skapar nya partiklar med dubbel hastighet.
emitter.stepTimeInterval = 2;
I denna variation skapar vi en skjutreglage på scenen och använder den för att dynamiskt justera simuleringstiden.
Dra en Slider-komponent ut från komponentpanelen till scenen. Namnge det "slider".
Vi skulle vilja ha skjutreglaget mellan 0,5 och 2, vilket innebär att vi vill att vår partikel simulering ska vara minst hälften så snabb som normalt och högst två gånger snabbt. Ställ också "liveDragging" på sant så vi kan se uppdateringen när vi skrubbar skjutreglaget tummen.
Nu måste vi lyssna på reglaget ändringshändelse för att dynamiskt ändra simuleringstiden. Först importera du SliderEvent-klassen i dokumentklassen.
importera fl.events.SliderEvent;
Och lyssna sedan på reglaget ändringshändelse i dokumentkonstruktionen.
slider.addEventListener (SliderEvent.CHANGE, changeTimescale);
Ändra ändå simuleringstiden i lyssnaren. Lägg till följande lyssnare i dokumentklassen.
privat funktion förändringTimeskala (e: SliderEvent): void emitter.stepTimeInterval = slider.value;
Låt oss titta på resultatet. Observera att när du skrubbar reglaget tummen åt vänster, sänker partikel-simuleringen, och om du skrubbar höger går simuleringen snabbare.
Stardust tillhandahåller en speciell initialiserare som heter SwitchInitializer. Konstruktören accepterar två arrays som parametrar; den första är en uppsättning initialisatorer, och den andra, med samma längd som den första, är en uppsättning av "vikter". Varje gång Stardust använder denna initialiserare för att initiera partiklar, väljs en av initialisatorerna i den första matrisen slumpmässigt för att initiera partiklarna. Ju mer vikt en initialiserare har desto mer sannolikt kommer den att plockas för initialisering. I den här variationen kommer vi att göra emitern skjuta ut inte bara stjärnor utan även vita cirklar.
Rita en vit cirkel, konvertera den till en symbol, exporteras för ActionScript, och namnge symbolen och klassen "Circle". Det här är den klass vi ska använda för de vita cirklarna.
Öppna StarEmitter-klassen och radera den följande raden. Detta inaktiverar DisplayObjectClass-initieraren som vi ställde in i tidigare steg.
addInitializer (new DisplayObjectClass (Star));
Lägg nu följande kod till konstruktören. Vi skulle vilja ha stjärnor och cirklar som ska emitteras lika troligt, så vi ger dem båda en vikt på 1.
// initialiseraren för stjärnor var doc1: DisplayObjectClass = ny DisplayObjectClass (Star); // initialiseraren för vita cirklar var doc2: DisplayObjectClass = ny DisplayObjectClass (Circle); // switchinitialiseraren var: SwitchInitializer = ny SwitchInitializer ([doc1, doc2], [1, 1]); // lägg till omkopplarinitieraren till emitern addInitiali