Manipularea mișcării particulelor cu motorul Stardust Particle - Partea 1

Stardust Particle Engine oferă două abordări majore pentru a manipula liber mișcarea particulelor, și anume câmpurile gravitaționale și deflectorii. Câmpurile gravitaționale sunt câmpuri vectoriale care afectează accelerația particulelor, iar deflectorii manipulează atât poziția și viteza particulelor.

Prima parte a acestui tutorial acoperă principiile de mișcare a particulelor și câmpurile gravitaționale. De asemenea, aceasta demonstrează cum să creați propriile câmpuri gravitaționale personalizate. Partea a doua se concentrează asupra deflectorilor și asupra modului de a crea deflectori personalizați.

Cunoașterea prealabilă a utilizării de bază a Stardust este necesară pentru a continua să citiți acest tutorial. Dacă nu sunteți familiarizați cu Stardust, puteți să vedeți tutorialul meu anterior despre acest subiect, Împușcați stelele cu Stardust Particle Engine, înainte de a merge.


Rezultatul final al rezultatelor

Să aruncăm o privire asupra rezultatului final la care vom lucra. Acesta este un exemplu de câmp gravitațional personalizat.


Particule de mișcare de bază

Este timpul pentru câteva flashback-uri rapide despre fizica liceului. Amintiți-vă de cinematica de bază? Este vorba despre deplasare, care este doar o modalitate mai bogată de a spune "poziția" și relația ei cu timpul. Pentru scopul acestui tutorial, avem nevoie doar de o înțelegere destul de simplă a subiectului.

Deplasare

Deplasare înseamnă poziția curentă a unui obiect. În acest tutorial, "obiectele" cu care ne ocupăm în primul rând sunt particulele în spațiul 2D. În spațiul 2D, deplasarea unui obiect este reprezentată de un vector 2D.

Viteză

Viteza unui obiect denotă cât de repede se schimbă poziția obiectului și direcția schimbării. Viteza unui obiect în spațiul 2D este de asemenea reprezentată de un vector 2D. Componentele vectorului x și y reprezintă direcția schimbării poziției, iar valoarea absolută a vectorului denotă viteza obiectului, adică cât de repede se mișcă obiectul.

Accelerare

Accelerația este de viteză ca viteza de deplasare. Accelerația unui obiect denotă cât de repede se schimbă viteza obiectului și direcția schimbării. La fel ca viteza unui obiect în spațiul 2D, accelerarea unui obiect este reprezentată de un vector 2D.


Câmpuri vectoriale

Un alt lucru care merită menționat este conceptul de câmpuri vectoriale. Puteți vizualiza practic un câmp vectorial ca o funcție care ia un vector ca intrare și scoate un alt vector. Câmpurile gravitaționale sunt un fel de câmpuri vectoriale care iau vectori de poziție ca vectori de intrare și ieșire de accelerare. De exemplu, în simularea fizică, de obicei, o gravitate uniformă îndreptată în jos se aplică tuturor obiectelor; în acest caz, câmpul gravitațional reprezentând gravitația este un câmp vector care emite un vector constant (îndreptat în jos), indiferent de vectorii de poziție de intrare.

Acesta este un grafic vizualizat al unui câmp vectorial. Vectorul de ieșire al unui vector de intrare dat (1, 2) este (0,5, 0,5), în timp ce ieșirea unei intrări (4, 3) este (-0,5, 0,5).

Camp clasa în Stardust reprezintă un câmp vectorial 2D, și omologul său 3D, Field3D clasa reprezintă un câmp vectorial 3D. În acest tutorial, ne vom concentra doar pe câmpurile vectoriale 2D.

Field.getMotionData () metoda ia o Particle2D obiect, care conține parametrul 2D al unei particule și informații despre viteză. Această metodă returnează a MotionData2D obiect, un obiect de valoare 2D vector care constă din componente x și y. Combinat cu Gravitatie acțiune, a Camp obiect poate fi folosit ca un câmp gravitațional, a cărui ieșire este utilizată pentru a manipula viteza particulelor.

Asta-i totul pentru recapitulări ale liceului nostru. Acum este momentul pentru unele lucruri reale Stardust.


Acțiunea gravitațională

După cum sa menționat anterior, Gravitatie clasa de acțiune face uz de Camp obiecte ca câmpuri gravitaționale pentru a manipula viteza particulelor. Iată cum creați un câmp vectorial uniform, un câmp vector care returnează un vector constant, indiferent de ce este dat, și îl înfășurați într-un câmp vectorial Gravitatie acțiune.

 // crea un câmp vectorial uniform îndreptat în jos (amintiți poziția pozitivă a coordonatei y înseamnă "jos" în Flash) câmpul var: câmp = nou UniformField (0, 1); // creaza o actiune gravitationala var gravitate: gravitatea = noua gravitate (); // adăugați câmpul la acțiunea gravitatională gravity.addField (câmpul);

Acest Gravitatie acțiunea este acum gata să fie utilizată în același mod ca orice alte acțiuni obișnuite Stardust.

 // adăugați acțiunea gravitațională la un emițător de emițător. adație (gravitație);

Exemplu: Windy Rain

Vom crea un efect de ploaie vânt folosind Gravitatie acțiune.


Pasul 1: Efectul de ploaie de bază

Creați un nou document Flash, alegeți o culoare întunecată pentru fundal, trageți o picătură de ploaie pe scenă și convertiți pictura de ploaie într-un simbol de clip video, exportat pentru ActionScript cu numele de clasă "Raindrop". Ștergeți instanța de ploaie pe scenă după aceea.

Acum vom crea un fișier AS pentru clasa de documente și un fișier AS pentru emițătorul de ploaie. Nu voi explica codul aici, deoarece am acoperit deja utilizarea de bază a Stardust în tutorialul meu anterior. Dacă nu sunteți familiarizat cu Stardust sau aveți nevoie de o băutură răcoritoare, vă recomandăm să citiți tutorialul meu anterior înainte de a vă deplasa.

Aceasta este clasa de documente.

 pachet import flash.display. *; import flash.events. *; import idv.cjcat.stardust.common.emitters. *; import idv.cjcat.stardust.common.renderers. *; import idv.cjcat.stardust.twoD.renderers. *; clasa publică WindyRain extinde Sprite private var emitter: Emitter; privat var renderer: Renderer; funcția publică WindyRain () emitter = new RainEmitter (); renderer = nou DisplayObjectRenderer (acest lucru); renderer.addEmitter (emițător); addEventListener (Event.ENTER_FRAME, mainLoop);  funcția privată mainLoop (e: Event): void emitter.step (); 

Și aceasta este clasa emițătorului folosită în clasa de documente.

 pachet import idv.cjcat.stardust.common.clocks. *; import idv.cjcat.stardust.common.initializers. *; import idv.cjcat.stardust.common.math. *; import idv.cjcat.stardust.twoD.actions. *; import idv.cjcat.stardust.twoD.emitters. *; import idv.cjcat.stardust.twoD.initializers. *; import idv.cjcat.stardust.twoD.zones. *; clasa publică RainEmitter extinde Emitter2D funcția publică RainEmitter () super (noul SteadyClock (1)); // inițializatorii addInitializer (noul DisplayObjectClass (Raindrop)); addInitializer (poziție nouă (noul RectZone (-300, -40, 940, 20)); addInitializer (noua Velocitate (noua RectZone (-0.5, 2, 1, 3))); addInitializer (noua masă (noul UniformRandom (2, 1))); addInitializer (noua Scară (noul UniformRandom (1, 0.2))); // acțiuni addAction (new Move ()); addAction (nou orientat (1, 180)); addAction (noul DeathZone (noul RectZone (-300, -40, 960, 480), adevărat)); 

Acesta este modul în care arată progresul nostru actual.


Pasul 2: Faceți-l vânt

Acum vom face efectul de ploaie vântând prin adăugarea unui câmp gravitațional uniform care "va trage" picăturile de ploaie la dreapta. Adăugați următorul cod în constructorul RainEmitter clasă.

 // crea un câmp uniform care întoarce întotdeauna (0.5, 0) var câmp: Field = new UniformField (0.5, 0); // luați în considerare masa de particule field.massless = false; // creati o actiune gravitationala si adaugati campul la aceasta var gravitate: gravitatea = noua gravitate (); gravity.addField (câmp); // adăugați acțiunea gravitațională la adaosul de emițător (gravitatea);

Rețineți că am setat Field.massless proprietate la fals, ceea ce este adevărat în mod implicit. Când este setat la true, această proprietate determină câmpurile să acționeze ca câmpurile gravitaționale obișnuite, afectând toate obiectele în mod egal, indiferent de masa lor. Cu toate acestea, atunci când proprietatea este setată la falsă, se ia în considerare masa particulelor: particulele cu masa mai mare sunt mai puțin afectate de câmp, iar particulele cu masa mai mică sunt afectate mai mult. De aceea am folosit Masa inițializator în codul nostru emițător anterior, pentru a adăuga o anumită aleatorie în efectul de ploaie.

Testați din nou filmul și acesta este rezultatul nostru. Picăturile de ploaie sunt acum afectate de un câmp gravitațional și sunt "trase" toate spre dreapta.


Exemplu: Turbulența

Acum o vom schimba UniformField obiect cu a BitmapField obiect, returnarea câmpurilor vectoriale pe baza canalelor de culori ale unui bitmap, pentru a crea un efect de turbulență. Dacă ați lucrat cu ActionScript pentru o perioadă de timp, s-ar putea să vă așteptați să utilizați BitmapData.perlinNoise () atunci când gândim la turbulențe și asta este exact ceea ce vom face.

Iată ce arată un exemplu de bitmap de zgomot Perlin. Zgomotul Perlin este un algoritm excelent de generare a zgomotului pentru simularea turbulențelor, a undelor de apă, a norilor etc. Puteți găsi mai multe informații despre zgomotul Perlin aici.

Câmpuri Bitmap

S-ar putea să te întrebi dacă o să folosim BitmapData.perlinNoise () pentru a genera un bitmap de zgomot pe perlină, cum vom folosi acest bitmap ca câmp vectorial? Ei bine, asta e ceea ce BitmapField clasa este pentru. Este nevoie de un bitmap și îl convertește într-un câmp vectorial.

Să presupunem că avem un câmp bitmap al cărui Canal X este setat sa roșu și Canal Y este verde. Aceasta înseamnă că atunci când câmpul are un vector de intrare, să zicem, (2, 3), acesta privește până la pixelul bitmapului la (2, 3) și componenta X a vectorului de ieșire este determinată de canalul roșu al pixelului, iar componenta Y este determinată de canalul verde. Când componentele unui vector de intrare nu sunt întregi, ele sunt rotunjite mai întâi.

Valoarea canalului de culoare variază de la 0 la 255, 127 fiind media. O valoare mai mică de 127 este considerată negativă de câmpul bitmap, în timp ce o valoare mai mare de 127 este considerată pozitivă. Zero este cel mai negativ numărul și 255 este cel mai pozitiv unu. De exemplu, dacă avem un pixel cu o culoare 0xFF0000 în reprezentare hexazecimală, adică un canal roșu cu valoarea 255 și un canal verde cu 0, atunci ieșirea câmpului bitmap pentru acest pixel ar fi un vector cu componenta X dintr-un cel mai pozitiv numărul posibil și componenta Y a cel mai negativ număr posibil, în cazul în care acest lucru numărul cel mai pozitiv / negativ posibil, sau numărul maxim, este specific câmpului bitmap. Pentru a fi mai precis, iată formula conversiei pixel-vector.


Pasul 1: Săgeți de zbor de bază

Creați un nou document Flash. Desenați o săgeată pe scenă, convertiți-o într-un simbol numit "Arrow" și exportați-o pentru ActionScript.

Creați un fișier AS pentru clasa de documente. Acest lucru este aproape identic cu cel din exemplul precedent.

 pachet import flash.display. *; import flash.events. *; import idv.cjcat.stardust.common.emitters. *; import idv.cjcat.stardust.common.renderers. *; import idv.cjcat.stardust.twoD.renderers. *; clasa publica Turbulence extinde Sprite private var emitter: Emitter; privat var renderer: Renderer; funcție publică Turbulență () emitter = new ArrowEmitter (); renderer = nou DisplayObjectRenderer (acest lucru); renderer.addEmitter (emițător); addEventListener (Event.ENTER_FRAME, mainLoop);  funcția privată mainLoop (e: Event): void emitter.step (); 

Creați un alt fișier AS pentru clasa emițătorului nostru.

 pachet import idv.cjcat.stardust.common.actions. *; importă idv.cjcat.stardust.common.clocks. *; import idv.cjcat.stardust.common.initializers. *; import idv.cjcat.stardust.common.math. *; import idv.cjcat.stardust.twoD.actions. *; import idv.cjcat.stardust.twoD.emitters. *; import idv.cjcat.stardust.twoD.initializers. *; import idv.cjcat.stardust.twoD.zones. *; clasa publică ArrowEmitter extinde Emitter2D funcția publică ArrowEmitter () super (noul SteadyClock (1)); // inițializatorii addInitializer (noul DisplayObjectClass (Arrow)); addInitializer (viata noua (noua UniformRandom (50, 10))); addInitializer (poziție nouă (noul SinglePoint (320, 200))); addInitializer (noua Velocity (noua LazySectorZone (3, 2))); addInitializer (noua masă (noul UniformRandom (2, 1))); addInitializer (noua Scară (noul UniformRandom (1, 0.2))); // actions addAction (new Age ()); addAction (noul DeathLife ()); addAction (new Move ()); addAction (nou orientat ()); addAction (noul ScaleCurve (10, 10)); 

Progresul actual arată astfel.


Pasul 2: Faceți-l turbulent

Adăugați următorul cod care creează date bitmap de zgomot permise de 640 pe 480 în constructorul emițătorului. Pentru explicații detaliate despre fiecare parametru al parametrului BitmapData.perlinNoise () , vă puteți referi la această documentație. Pentru a simplifica, codul următor creează un bitmap de zgomot Perlin cu "octave" de aproximativ 50X50, iar zgomotul constă în canale de culoare roșii și verzi.

 // a crea un bitmap de zgomot Perlin zgomot var: BitmapData = noi BitmapData (640, 400); noise.perlinNoise (50, 50, 1, 0, adevărat, adevărat, 1 | 2);

Apoi, creați un BitmapField obiecte și să le atribuiți datele bitmap prin intermediul acestora Actualizați() metodă. Apoi, restul codului cu privire la Gravitatie acțiunea este exact aceeași ca în exemplul anterior.

 // crea un câmp uniform care întoarce întotdeauna (0.5, 0) câmpul var: BitmapField = nou BitmapField (); field.channelX = 1; // setați canalul X pe câmpul roșu field.channelY = 2; // setați canalul Y la câmpul verde.max = 1; // setați valoarea maximă a domeniului vectorial absolute field.update (zgomot); // actualizați câmpul cu bitmap de zgomot // luați masa particulelor în considerare field.massless = false; // creati o actiune gravitationala si adaugati campul la aceasta var gravitate: gravitatea = noua gravitate (); gravity.addField (câmp); // adăugați acțiunea gravitațională la adaosul de emițător (gravitatea);

Acum încercați din nou filmul și veți vedea că săgețile noastre de zbor se confruntă acum cu turbulențe.


câmpuri customizate

Am folosit-o UniformField și BitmapField furnizate de Stardust, iar acum vom crea propriile câmpuri personalizate prin extinderea Camp clasa și suprascrierea getMotionData2D () metodă.

getMotionData2D ia a Particle2D parametru ca intrare, care conține informația despre vectorul de poziție a particulei și aceasta este intrarea în câmp. Metoda returnează a MotionData2D obiect, reprezentând ieșirea câmpului. Asta e tot ce trebuie să știți pentru a crea un câmp particularizat. Să creăm un câmp de vortex personalizat.

Mai jos este graficul vizualizat al câmpului nostru vortex. E destul de explicativă de ce se numește câmp vortex.

Iată formula pentru câmpul vortex.

Și clasa câmpului vortex este la fel de simplă ca și codul de mai jos. Am folosit Vec2D clasa de a face toate lucrurile murdare de rotire a unui vector de 90 de grade în sensul acelor de ceasornic și de stabilire a vectorului valoarea absolută. Apoi, dăm componentele vectorului x și y într-un a MotionData2D obiect, care este de tip obiect care trebuie returnat.

 pachet import idv.cjcat.stardust.twoD.fields. *; import idv.cjcat.stardust.twoD.geom. *; import idv.cjcat.stardust.twoD.particles. *; clasa publică VortexField extinde câmpul public var centerX: Number; public var centerY: Număr; public var strength: Number; funcție publică VortexField (centerX: număr = 0, centerY: număr = 0, putere: număr = 1) this.centerX = centerX; acest centru = centruY; această intensitate = putere;  suprascrie funcția protejată calculateMotionData2D (particulă: Particle2D): MotionData2D var dx: Number = particle.x - centerX; var dy: Numărul = particle.y - centerY; var vec: Vec2D = nou Vec2D (dx, dy); vec.length = putere; vec.rotateThis (90); returnează MotionData2D (vec.x, vec.y); 

Acum că avem domeniul nostru personalizat, să-l testăm în exemplul următor.


Exemplu: Vortex

Acest exemplu servește drept unitate de test pentru câmpul vectorului vortex. Este la fel de simplu ca schimbarea unui câmp cu unul nou. Modificați următorul cod din exemplul anterior din acest

 // crea un câmp uniform care întoarce întotdeauna (0.5, 0) câmpul var: BitmapField = nou BitmapField (); field.channelX = 1; // setați canalul X pe câmpul roșu field.channelY = 2; // setați canalul Y la câmpul verde.max = 1; / / setați câmpul de lungime maximă vecotr field.update (zgomot); // actualizați câmpul cu bitmap de zgomot

la acest

 // crea un câmp vortex centrat la (320, 200) cu câmpul de putere 1 var: VortexField = new VortexField (); field.centerX = 320; field.centerY = 200; field.strength = 1;

Și am terminat. Puteți testa filmul și puteți vedea efectul de vortex. Dulce!


Concluzie

Ați văzut cum să utilizați Gravitatie acțiune și Camp clasa de a afecta viteza particulelor. Și ați învățat cum să creați propriile câmpuri vectoriale personalizate pentru a fi utilizate ca câmpuri gravitaționale. Următoarea parte a acestui tutorial vă va arăta cum să utilizați deflectorii pentru a manipula simultan poziția și viteza particulelor.

Vă mulțumesc foarte mult pentru lectură!

Cod