În acest tutorial vă voi prezenta motorul Stardust Particle. În primul rând vă voi arăta cum să înființați Stardust, iar apoi voi acoperi responsabilitățile de bază ale Stardust și modul în care acestea colaborează împreună pentru ca Stardust să funcționeze ca un întreg.
Apoi, ne vom uita la fluxul general de lucru al lui Stardust și vom ajunge la crearea unui efect de particule cu stelele care se aruncă de pe cursorul mouse-ului; stelele vor încetini treptat, vor crește mai mari după naștere și se vor micsora când vor muri.
În cele din urmă, voi demonstra flexibilitatea Stardust creând mai multe variante de la exemplul deja complet, inclusiv folosind clipuri video animate ca particule, interval de timp pentru simularea variabilelor de particule și filmarea obiectelor de afișare de diferite clase de la un singur emițător.
Acest tutorial este destinat persoanelor care sunt deja familiarizate cu programarea orientată pe obiecte ActionScript 3.0 (OOP), așa că presupun că deja știți foarte bine ce înseamnă clase, obiecte, moștenire și interfață. Nici o problema cu OOP? Atunci hai să tragem niște stele!
După cum sugerează și numele, Stardust este folosit pentru crearea efectelor particulelor. Dacă sunteți un ActionScriptor cu experiență, s-ar putea să fi creat efectele particulelor de la zero de mai multe ori și să spuneți "Sunt complet cool cu crearea efectelor particulelor de la zero, de ce aș avea nevoie oricum de un motor cu particule?" Stardust este aici pentru a vă ajuta să vă concentrați mai mult pe designul comportamentului fizic al particulelor decât să vă faceți griji cu privire la lucrurile de nivel inferior, cum ar fi gestionarea memoriei. În loc să scrieți codul pentru a avea grijă de datele despre particule, inițierea și eliminarea resurselor, cu Stardust, puteți trece peste aceste rutine plictisitoare și doar decideți cum doriți să se comporte particulele dvs..
Structura de clasă a Stardust a fost inspirată de sistemul FLiNT Particle System, un alt motor cu particule ActionScript 3.0. Astfel, aceștia împărtășesc câteva trăsături de bază similare.
În plus față de aceste caracteristici de bază, Stardust oferă și câteva caracteristici avansate pentru utilizatorii experimentați.
Când vine vorba de efectele particulelor, este foarte important să se gestioneze eficient datele particulelor masive. Stardust folosește în mare măsură bazinele de obiecte și listele asociate pentru a spori performanța:
Înainte de a ajunge la o codificare reală, va trebui să luăm o copie a Stardust Particle Engine. Este lansat sub licență MIT, ceea ce înseamnă că este total gratuit, indiferent dacă doriți să îl utilizați într-un proiect comercial sau necomercial.
Iată pagina de pornire a proiectului Stardust: http://code.google.com/p/stardust-particle-engine/
Puteți descărca Stardust aici: http://code.google.com/p/stardust-particle-engine/downloads/list
La momentul redactării, ultima versiune care poate fi descărcată din lista de descărcări este 1.1.132 Beta. Puteți lua întotdeauna cea mai recentă revizuire din depozitul SVN (care ar putea să nu fie stabilă, cu toate acestea).
Pe pagina de pornire a proiectului puteți găsi și alte accesorii, cum ar fi documentația API și o copie a manualului PDF. Există chiar tutoriale video pe YouTube.
Aici voi trece pe scurt clasele de bază Stardust și responsabilitățile lor.
Această clasă este superclajul tuturor claselor de bază, care definește proprietățile și metodele, în special pentru serializarea XML.
În general, efectele particulelor privesc controlul unei cantități de entități cu un aspect similar și comportament similar, dar randomizat. Clasa Random este pentru generarea numerelor aleatoare, care pot fi folosite în Stardust pentru a randomiza proprietățile particulelor. De exemplu, clasa UniformRandom este o subclasă a clasei Random și numele său spune totul: numărul aleatoriu generat de un obiect UniformRandom este distribuit uniform și voi folosi această clasă în special pentru întregul tutorial.
Există momente când un număr aleator unidimensional nu este suficient. Uneori avem nevoie de numere aleatorii bidimensionale, care sunt în esență perechi de numere aleatorii, pentru proprietăți precum poziția și viteza. Clasa Zone este pentru generarea de perechi de numere aleatoare bidimensionale. Această clasă modelează o pereche de numere aleatorii ca un punct aleator într-o zonă 2D. De exemplu, CircleZone generează perechi de numere aleatoare (x, y) de la puncte aleatorii dintr-o regiune circulară. Clasele Random și Zone sunt utilizate în principal de clasa Initializer, care va fi acoperită ulterior. Clasa Zone3D este omologul 3D al acestei clase, pentru efectele 3D ale particulelor.
Clasa Emitter este în principiu în cazul în care toate lucrurile de nivel scăzut sunt încapsulate. Un emițător inițiază particule nou create înainte de a fi adăugate în simulare, actualizează proprietățile particulelor în fiecare iterație de buclă principală și elimină particule moarte din simulare. Metoda Emitter.step () este ceea ce doriți să invocați în mod repetat, pentru a menține Stardust în funcțiune.
Clasa Ceas determină viteza de creare a particulelor noi pentru emițători. Un obiect Emitter conține exact o referință la un obiect Ceas. La începutul fiecărui apel al metodei Emitter.step (), emițătorul cere obiectului ceas câte particule noi ar trebui să creeze. Luați în clasă SteadyClock, de exemplu, îi spune emițătorilor să creeze particule noi la o rată constantă.
Această clasă este pentru inițializarea particulelor nou create. Un obiect inițializator trebuie adăugat la un emițător pentru ca acesta să funcționeze. Practic, o subclasă de inițializator inițializează numai o proprietate a particulelor. De exemplu, clasa de inițializare a masei inițiază masa de particule noi. Unii inițializatori acceptă un obiect Rand ca parametru constructor pentru inițializarea particulelor cu valori aleatorii. Următorul cod creează un inițializator de viață care inițializează durata de viață a particulelor la valori centrate la 50 cu variație de 10, și anume între intervalul de la 40 la 60.
Viața nouă (noul UniformRandom (50, 10));
Obiectele de acțiune actualizează proprietățile particulelor în fiecare iterație a bucla principală (metoda Emiter.step ()). De exemplu, clasa de acțiune Mută actualizează pozițiile particulelor în funcție de viteză. Un obiect de acțiune trebuie adăugat unui emițător pentru ca acesta să funcționeze.
Acum că știți cum colaborează clasele de bază, să aruncăm o privire la un flux general de lucru pentru Stardust.
Începeți prin a crea un emițător. Utilizați clasa Emitter2D pentru efectele particulelor 2D și clasa Emitter3D pentru efecte 3D.
var emitter: emitorul = noul Emitter2D ();
Pentru a specifica viteza de creare a particulelor, avem nevoie de un ceas. Acest lucru poate fi setat fie de proprietatea Emitter.clock, fie prin trecerea unui ceas ca prim parametru la constructorul emițătorului.
// abordare proprietate emitter.clock = noul SteadyClock (1); // abordare constructor var emitter: Emitter = nou Emitter2D (nou SteadyClock (1));
Adăugați inițializatorii la emițător prin metoda Emitter.addInitializer ().
emitter.addInitializer (noua viață (noul UniformRandom (50, 10))); emitter.addInitializer (noua Scară (noul UniformRandom (1, 0,2)));
Adăugați acțiuni la emițător prin metoda Emitter.addAction ().
emitter.addAction (noua mișcare ()); emitter.addAction (nou Spin ());
Creați un renderer și adăugați emițătorul la redare prin metoda Renderer.addEmitter ().
var renderer: Renderer = noul DisplayObjectRenderer (container); // "container" este containerul nostru sprite renderer.addEmitter (emitter);
În cele din urmă, apelați în mod repetat metoda Emitter.step () pentru a menține simularea particulelor. S-ar putea să doriți să utilizați evenimentul enter-frame sau un cronometru pentru a face acest lucru. Într-o singură chemare a metodei Emitter.step (), ceasul determină câte particule noi ar trebui create, aceste particule noi sunt inițializate de inițializatori, toate particulele sunt actualizate prin acțiuni, particulele moarte sunt îndepărtate și, în cele din urmă, redarea redă efectul particulei.
// abordarea evenimentului enter-frame addEventListener (Event.ENTER_FRAME, mainLoop); // Timer abordare timer.addEventListener (TimerEvent.TIMER, mainLoop); funcția mainLoop (e: Eveniment): void emitter.step ();
În regulă. Asta e destul de mult pentru Stardust. Acum este momentul să deschideți IDE-ul Flash și să vă murdăriți mâinile.
Creați un nou document Flash cu o dimensiune de 640X400, o rată a cadrelor de 60fps și un fundal întunecat. Aici am făcut un fundal de gradient albastru închis. Apropo, Stardust funcționează bine atât cu Flash Player 9 și 10, deci este în regulă indiferent dacă utilizați Flash CS3 sau CS4. În acest tutorial voi folosi Flash CS3.
Creăm un efect de particule cu stele, așa că va trebui să desenați o stea și să o convertim într-un simbol, exportat, firește, pentru ActionScript. Acest simbol va fi folosit mai târziu pentru a face efectul nostru de particule. Denumiți simbolul și clasa exportată "Star".
Creați o nouă clasă de documente și numiți-o StarParticles.
pachet import flash.display.Sprite; public class StarParticles extinde Sprite funcția publică StarParticles ()
După cum sa menționat în fluxul general de lucru, primul pas este crearea unui emițător. Următorul pas este să adăugați inițiale și acțiuni către emițător. În timp ce acest lucru se poate face în constructorul de clase de documente, recomand insistent ca acesta să se facă într-o subclasă separată a emițătorului. Este întotdeauna mai bine să separăm designul de comportament al particulelor din programul principal; prin aceasta, codul este mult mai curat și mai ușor de modificat în viitor, fără a fi amestecat cu programul principal.
Vom crea un efect de particule 2D, astfel încât Emitter2D este clasa emițător pe care o vom extinde. Extindeți clasa Emitter2D și numiți-o StarEmitter, deoarece vom încerca să împușcăm stele mai târziu. Constructorul emițător acceptă un parametru Ceas, deci vom declara un parametru constructor pentru a transmite o referință de obiect Ceas la constructorul superclass.
pachet import idv.cjcat.stardust.twoD.emitters.Emitter2D; clasa publică StarEmitter extinde Emitter2D funcția publică StarEmitter (ceas: ceas) // trece pe obiectul ceasului super constructor super (ceas);
O abordare mai bună pentru a crea o subclasă a emițătorului este de a declara parametrii particulelor ca fiind constante statice, grupate într-un singur loc. Deci, în cazul în care doriți să modificați parametrii, veți ști întotdeauna unde să găsiți declarațiile. Semnificația acestor constante va fi explicată mai târziu când vor fi folosite.
// durata medie de viață statică const LIFE_AVG: Number = 30; // variație de viață statică statică const LIFE_VAR: Number = 10; // scara medie statică privată const SCALE_AVG: Number = 1; // variația scalei private static const SCALE_VAR: Number = 0.4; // scară timp de creștere statică statică const GROWING_TIME: Number = 5; // scară timp scadere privat static const SHRINKING_TIME: Number = 10; // viteza medie statică statică const SPEED_AVG: număr = 10; // variația vitezei const static privat const SPEED_VAR: Number = 8; // medie de omega (viteză unghiulară) statică statică privată OMEGA_AVG: Number = 0; // omega variație privată statică const OMEGA_VAR: Number = 5; // coeficient de amortizare const static privat DAMPING: Number = 0.1;
Ce inițializatori trebuie să creăm efectul nostru de particule? Să aruncăm o privire la lista de mai jos:
Iată codul:
punct = nou SinglePoint (); addInitializer (noul DisplayObjectClass (Star)); addInitializer (noua viață (noul UniformRandom (LIFE_AVG, LIFE_VAR))); addInitializer (noua scară (noul UniformRandom (SCALE_AVG, SCALE_VAR))); addInitializer (noua poziție (punct)); addInitializer (noua Velocity (noua LazySectorZone (SPEED_AVG, SPEED_VAR))); addInitializer (noua rotație (nouă UniformRandom (0, 180))); addInitializer (noul Omega (noul UniformRandom (OMEGA_AVG, OMEGA_VAR)));
Bine, am terminat cu inițializatorii. Acum este momentul să adăugați acțiuni la emițător. Mai jos este o listă de acțiuni de care avem nevoie:
Asta e. Emitorul nostru sa terminat. Iată codul pentru acest emițător în întregime, inclusiv declarațiile de import incluse.
pachet import idv.cjcat.stardust.common.actions.Age; import idv.cjcat.stardust.common.actions.DeathLife; import idv.cjcat.stardust.common.actions.ScaleCurve; import idv.cjcat.stardust.common.clocks.Clock; import idv.cjcat.stardust.common.initializers.Life; import idv.cjcat.stardust.common.initializers.Scale; import idv.cjcat.stardust.common.math.UniformRandom; import idv.cjcat.stardust.twoD.actions.Damping; import idv.cjcat.stardust.twoD.actions.Move; import idv.cjcat.stardust.twoD.actions.Spin; import idv.cjcat.stardust.twoD.emitters.Emitter2D; import idv.cjcat.stardust.twoD.initializers.DisplayObjectClass; import idv.cjcat.stardust.twoD.initializers.Omega; import idv.cjcat.stardust.twoD.initializers.Position; import idv.cjcat.stardust.twoD.initializers.Rotation; import idv.cjcat.stardust.twoD.initializers.Velocity; import idv.cjcat.stardust.twoD.zones.LazySectorZone; import idv.cjcat.stardust.twoD.zones.SinglePoint; clasa publica StarEmitter extinde Emitter2D / ** * Constante * / const static privat LIFE_AVG: Number = 30; contabil static privat LIFE_VAR: Number = 10; static static const SCALE_AVG: Number = 1; static static const SCALE_VAR: Number = 0.4; constr. statică privată GROWING_TIME: Număr = 5; constrângere statică privată SHRINKING_TIME: număr = 10; static static const SPEED_AVG: Number = 10; constrângere statică privată SPEED_VAR: Number = 8; constrângere statică privată OMEGA_AVG: Number = 0; static static const OMEGA_VAR: Number = 5; constanta statică privată DAMPING: Number = 0.1; public var punct: SinglePoint; funcție publică StarEmitter (ceas: ceas) super (ceas); punct = nou SinglePoint (); // inițializatorii addInitializer (noul DisplayObjectClass (Star)); addInitializer (noua viață (noul UniformRandom (LIFE_AVG, LIFE_VAR))); addInitializer (noua scară (noul UniformRandom (SCALE_AVG, SCALE_VAR))); addInitializer (noua poziție (punct)); addInitializer (noua Velocity (noua LazySectorZone (SPEED_AVG, SPEED_VAR))); addInitializer (noua rotație (nouă UniformRandom (0, 180))); addInitializer (noul Omega (noul UniformRandom (OMEGA_AVG, OMEGA_VAR))); // actions addAction (new Age ()); addAction (noul DeathLife ()); addAction (new Move ()); addAction (nou Spin ()); addAction (noua amortizare (DAMPING)); addAction (noua ScaleCurve (GROWING_TIME, SHRINKING_TIME));
Acum este momentul să vă întoarceți la clasa de documente și să o terminați. Să aruncăm o privire la sarcinile rămase.
Mai jos este codul complet pentru clasa de documente, inclusiv declarațiile de import incluse.
pachet import flash.display.Sprite; import flash.display.StageScaleMode; importul flash.events.Event; import flash.geom.Rectangle; import idv.cjcat.stardust.common.clocks.SteadyClock; import idv.cjcat.stardust.common.renderers.Renderer; import idv.cjcat.stardust.twoD.renderers.DisplayObjectRenderer; clasa publica StarParticles extinde Sprite private var emitter: StarEmitter; funcția publică StarParticles () // instanțiează emițătorul StarEmitter = noul StarEmitter (noul SteadyClock (0.5)); // container container sprite var: Sprite = Sprite nou (); // renderer care face efectul de particule var renderer: Renderer = new DisplayObjectRenderer (container); renderer.addEmitter (emițător); // adăugați containerul în lista de afișare, deasupra fundalului addChildAt (container, 1); // utilizați evenimentul enter-frame addEventListener (Event.ENTER_FRAME, mainLoop); funcția privată mainLoop (e: Event): void // actualizați poziția SinglePoint la poziția mouse-ului emitter.point.x = mouseX; emitter.point.y = mouseY; // apelați buclă principală emitter.step ();
În cele din urmă, am terminat! Acum, să aruncăm o privire asupra rezultatului. Apăsați CTRL + ENTER în Flash pentru a testa filmul și veți vedea rezultatul.
Nu am terminat încă! Să mai facem câteva variante. Primul folosește clipuri video animate pentru particulele noastre.
Această primă variație este destul de simplă, fără implicarea codării suplimentare. Este la fel de simplu ca crearea unei animații de bază a cronologiei. Editați simbolul Star în Flash IDE, creați un alt cadru cheie și modificați culoarea stelei în acest cadru în roșu. Acest lucru determină, în esență, ca stelele să clipească între galben și roșu. S-ar putea să doriți să introduceți mai multe cadre goale între ele, deoarece o rată a cadrelor de 60fps este prea rapidă pentru a clipi două cadre.
Acum, testați filmul și verificați rezultatul. Efectul intermitent al stelei arată desen animat; acest lucru poate fi folosit pentru efecte clasice de amețeală, care este văzut de obicei în desene animate.
Așa cum am menționat mai devreme, una dintre trăsăturile Stardust este "scala de simulare reglabilă", ceea ce înseamnă că scala de timp folosită de Stardust pentru simularea particulelor poate fi ajustată dinamic. Totul se face schimbând proprietatea Emitter.stepTimeInterval, care este 1 în mod implicit. Următorul fragment de cod modifică această valoare la 2, rezultând particule care se mișcă de două ori mai repede, iar emițătorul creând particule noi la viteză dublă.
emitter.stepTimeInterval = 2;
În această variantă, vom crea un cursor pe scenă și îl vom folosi pentru a regla dinamic intervalul de timp al simulării.
Glisați o componentă a cursorului din panoul Componente în scenă. Denumiți "cursorul".
Ne-ar plăcea să alunecați cursorul între 0,5 și 2, ceea ce înseamnă că dorim ca simularea noastră de particule să fie cel puțin jumătate mai rapidă decât cea normală și cel mult de două ori la fast. De asemenea, setați "liveDragging" la adevărat, pentru a putea vedea actualizarea pe măsură ce curățăm degetul mare al glisorului.
Acum trebuie să ascultăm schimbarea evenimentului pentru a schimba dinamic intervalul de timp al simulării. În primul rând, importați clasa SliderEvent în clasa de documente.
import fl.events.SliderEvent;
Apoi, ascultați cursorul evenimentului de schimbare al constructorului de clase de documente.
slider.addEventListener (SliderEvent.CHANGE, changeTimescale);
În cele din urmă, schimbați intervalul de timp al simulării în ascultător. Adăugați următorul ascultător la clasa de documente.
schimbarea funcției privateTimescale (e: SliderEvent): void emitter.stepTimeInterval = slider.value;
Să vedem rezultatul. Observați că, pe măsură ce îndepărtați degetul mare al glisorului spre stânga, simularea particulelor încetinește și, dacă sculați, simularea merge mai repede.