Manipolazione del movimento delle particelle con il motore a particelle di polvere di terracotta - Parte 1

Stardust Particle Engine fornisce due approcci principali per manipolare liberamente il movimento delle particelle, vale a dire campi gravitazionali e deflettori. I campi gravitazionali sono campi vettoriali che influenzano l'accelerazione di una particella ei deflettori manipolano sia la posizione che la velocità di una particella.

La prima parte di questo tutorial tratta le basi del movimento delle particelle e dei campi gravitazionali. Inoltre, dimostra come creare i propri campi gravitazionali personalizzati. La seconda parte si concentra sui deflettori e su come creare i deflettori personalizzati.

È richiesta una precedente conoscenza dell'uso di base di Stardust per continuare a leggere questo tutorial. Se non hai familiarità con Stardust, puoi controllare il mio precedente tutorial sull'argomento, Spara a stelle con Stardust Particle Engine, prima di andare avanti.


Anteprima del risultato finale

Diamo un'occhiata al risultato finale su cui lavoreremo. Questo è un esempio di un campo gravitazionale a vortice personalizzato.


Nozioni di base sulle particelle

È tempo di qualche rapido flashback sulla fisica delle scuole superiori. Ricorda la cinematica di base? È tutto incentrato sullo spostamento, che è solo un modo più elaborato per dire "posizione" e il suo rapporto con il tempo. Per lo scopo di questo tutorial, abbiamo solo bisogno di una comprensione piuttosto semplice dell'argomento.

Spostamento

Spostamento indica la posizione corrente di un oggetto. In questo tutorial gli "oggetti" di cui ci occupiamo principalmente sono le particelle nello spazio 2D. Nello spazio 2D, lo spostamento di un oggetto è rappresentato da un vettore 2D.

Velocità

La velocità di un oggetto indica quanto velocemente cambia la posizione di un oggetto e la direzione del cambiamento. La velocità di un oggetto nello spazio 2D è anche rappresentata da un vettore 2D. Le componenti xey del vettore rappresentano la direzione del cambiamento di posizione, e il valore assoluto del vettore denota la velocità dell'oggetto, cioè quanto velocemente si muove l'oggetto.

Accelerazione

L'accelerazione è alla velocità come la velocità è allo spostamento. L'accelerazione di un oggetto indica quanto velocemente cambia la velocità di un oggetto e la direzione del cambiamento. Proprio come la velocità di un oggetto nello spazio 2D, l'accelerazione di un oggetto è rappresentata da un vettore 2D.


Campi vettoriali

Un'altra cosa che vale la pena menzionare è il concetto di campi vettoriali. Puoi fondamentalmente vedere un campo vettoriale come una funzione che prende un vettore come input e emette un altro vettore. I campi gravitazionali sono un tipo di campi vettoriali che prendono i vettori di posizione come vettori di input e output di accelerazione. Ad esempio, nella simulazione fisica, di solito una gravità uniforme rivolta verso il basso viene applicata a tutti gli oggetti; in questo caso, il campo gravitazionale che rappresenta la gravità è un campo vettoriale che emette un vettore costante (rivolto verso il basso), indipendentemente da quali siano i vettori di posizione di input.

Questo è un grafico visualizzato di un campo vettoriale. Il vettore di uscita di un dato vettore di input (1, 2) è (0,5, 0,5), mentre l'uscita di un input (4, 3) è (-0,5, 0,5).

Il Campo classe in Stardust rappresenta un campo vettoriale 2D e la sua controparte 3D, il Field3D la classe rappresenta un campo vettoriale 3D. In questo tutorial, ci concentreremo solo sui campi vettoriali 2D.

Il Field.getMotionData () il metodo richiede a Particle2D oggetto, contenente la posizione 2D di una particella e le informazioni sulla velocità, come parametro. Questo metodo restituisce a MotionData2D oggetto, un "oggetto valore" vettoriale 2D costituito da componenti xey. Combinato con il Gravità azione, a Campo l'oggetto può essere usato come un campo gravitazionale, il cui output è usato per manipolare la velocità delle particelle.

Questo è tutto per il riassunto della fisica della nostra scuola superiore. Ora è il momento di fare un po 'di roba sulla Stardust.


L'azione gravitazionale

Come accennato in precedenza, il Gravità la classe d'azione fa uso di Campo oggetti come campi gravitazionali per manipolare la velocità delle particelle. Ecco come si crea un campo vettoriale uniforme, un campo vettoriale che restituisce un vettore costante indipendentemente dall'input fornito, e lo avvolge in un Gravità azione.

 // crea un campo vettoriale uniforme che punta verso il basso (ricorda la coordinata y positiva significa "giù" in Flash) campo var: Field = new UniformField (0, 1); // crea una gravità gravità gravità azione: Gravità = nuova Gravità (); // aggiungi il campo all'azione gravitazionale gravity.addField (campo);

Questo Gravità l'azione è ora pronta per essere utilizzata allo stesso modo di qualsiasi altra azione ordinaria di Polvere di Stelle.

 // aggiunge l'azione gravitazionale a un emitter emitter.addAction (gravità);

Esempio: Windy Rain

Creeremo un effetto pioggia ventoso usando il Gravità azione.


Step 1: Effetto Raining di base

Crea un nuovo documento Flash, scegli un colore scuro per lo sfondo, disegna una goccia di pioggia sullo stage e converti la goccia di pioggia in un simbolo di clip filmato, esportato per ActionScript con un nome di classe "Raindrop". Eliminare l'istanza raindrop sullo stage in seguito.

Ora creeremo un file AS per la classe del documento e un file AS per l'emettitore di pioggia. Non spiegherò il codice qui, dato che ho già trattato l'uso di base di Stardust nel mio tutorial precedente. Se non hai familiarità con Stardust o hai bisogno di un po 'di ristoro, ti consiglio vivamente di leggere il mio tutorial precedente prima di proseguire.

Questa è la classe del documento.

 pacchetto import flash.display. *; import flash.events. *; import idv.cjcat.stardust.common.emitters. *; import idv.cjcat.stardust.common.renderers. *; import idv.cjcat.stardust.twoD.renderers. *; public class WindyRain estende Sprite emettitore private var: Emitter; renderer var privato: Renderer; funzione pubblica WindyRain () emitter = new RainEmitter (); renderer = new DisplayObjectRenderer (this); renderer.addEmitter (emettitore); addEventListener (Event.ENTER_FRAME, mainLoop);  private function mainLoop (e: Event): void emitter.step (); 

E questa è la classe emettitore utilizzata nella classe del documento.

 package 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. *; public class RainEmitter estende Emitter2D public function RainEmitter () super (new SteadyClock (1)); // inizializzatori addInitializer (new DisplayObjectClass (Raindrop)); addInitializer (new Position (new RectZone (-300, -40, 940, 20))); addInitializer (nuovo Velocity (nuovo RectZone (-0.5, 2, 1, 3))); addInitializer (new Mass (new UniformRandom (2, 1))); addInitializer (new Scale (new UniformRandom (1, 0.2))); // azioni addAction (new Move ()); addAction (new Oriented (1, 180)); addAction (nuovo DeathZone (nuovo RectZone (-300, -40, 960, 480), vero)); 

Questo è come stanno i nostri attuali progressi.


Passo 2: Falla ventoso

Ora renderemo l'effetto pioggia ventoso aggiungendo un campo gravitazionale uniforme che "tira" le gocce di pioggia verso destra. Aggiungi il seguente codice nel costruttore del file RainEmitter classe.

 // crea un campo uniforme che restituisce sempre (0.5, 0) var: Field = new UniformField (0.5, 0); // prende in considerazione la massa delle particelle field.massless = false; // crea un'azione gravitazionale e aggiunge il campo a essa gravità: Gravity = new Gravity (); gravity.addField (campo); // aggiunge l'azione gravitazionale all'emettitore addAction (gravità);

Si noti che abbiamo impostato il Field.massless proprietà su false, che è true per impostazione predefinita. Se impostato su true, questa proprietà fa sì che i campi agiscano come normali campi gravitazionali, influenzando tutti gli oggetti allo stesso modo indipendentemente dalla loro massa. Tuttavia, quando la proprietà è impostata su false, viene presa in considerazione la massa delle particelle: le particelle con una massa più grande sono meno influenzate dal campo, mentre le particelle con una massa minore ne sono maggiormente influenzate. Questo è il motivo per cui abbiamo usato il Massa inizializzatore nel nostro precedente codice di emissione, per aggiungere un po 'di casualità all'effetto pioggia.

Prova di nuovo il film, e questo è come appare il nostro risultato. Le gocce di pioggia sono ora interessate da un campo gravitazionale e sono tutte "tirate" a destra.


Esempio: turbolenza

Ora andiamo a scambiare il UniformField oggetto con a BitmapField oggetto, restituendo campi vettoriali basati sui canali cromatici di una bitmap, per creare un effetto di turbolenza. Se hai lavorato con ActionScript per un po ', potresti aspettarti di usare il BitmapData.perlinNoise () metodo quando si pensa alla turbolenza, ed è esattamente quello che faremo.

Ecco come appare una bitmap di rumore Perlin di esempio. Il rumore di Perlin è un eccellente algoritmo per generare rumore per simulare turbolenze, onde d'acqua, nuvole, ecc. Puoi trovare maggiori informazioni sul rumore di Perlin qui.

Campi bitmap

Forse ti starai chiedendo se useremo il BitmapData.perlinNoise () metodo per generare una bitmap perlin noise, come useremo questa bitmap come campo vettoriale? Bene, questo è ciò che BitmapField la classe è per. Prende una bitmap e la converte in un campo vettoriale.

Diciamo che abbiamo un campo bitmap di cui Canale X. è impostato per rosso e Canale Y. è verde. Ciò significa che quando il campo accetta un vettore di input, ad esempio (2, 3), cerca il pixel della bitmap su (2, 3) e il componente X del vettore di uscita è determinato dal canale rosso del pixel e il componente Y è determinato dal canale verde. Quando i componenti di un vettore di input non sono numeri interi, vengono arrotondati per primi.

Il valore di un canale di colore varia da 0 a 255, mentre 127 è la media. Un valore inferiore a 127 viene considerato negativo dal campo bitmap, mentre un valore maggiore di 127 viene considerato positivo. Zero è il più negativo il numero e 255 è il più positivo uno. Ad esempio, se abbiamo un pixel con un colore 0xFF0000 in rappresentazione esadecimale, ovvero un canale rosso con valore 255 e un canale verde con 0, l'output del campo bitmap per questo pixel sarebbe un vettore con componente X di un pixel più positivo numero possibile e componente Y di a più negativo numero possibile, dove questo numero più positivo / negativo possibile, o numero massimo, è specifico per il campo della bitmap. Per essere più precisi, ecco la formula della conversione da pixel a vettore.


Passaggio 1: frecce di volo di base

Crea un nuovo documento Flash. Disegna una freccia sul palco, convertila in un simbolo chiamato "Freccia" ed esportalo per ActionScript.

Creare un file AS per la classe del documento. Questo è quasi lo stesso dell'esempio precedente.

 pacchetto import flash.display. *; import flash.events. *; import idv.cjcat.stardust.common.emitters. *; import idv.cjcat.stardust.common.renderers. *; import idv.cjcat.stardust.twoD.renderers. *; public class Turbulence estende Sprite emettitore privato var: Emitter; renderer var privato: Renderer; funzione pubblica Turbulence () emitter = new ArrowEmitter (); renderer = new DisplayObjectRenderer (this); renderer.addEmitter (emettitore); addEventListener (Event.ENTER_FRAME, mainLoop);  private function mainLoop (e: Event): void emitter.step (); 

Crea un altro file AS per la nostra classe di emettitore.

 package 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. *; public class ArrowEmitter estende Emitter2D public function ArrowEmitter () super (new SteadyClock (1)); // inizializzatori addInitializer (new DisplayObjectClass (Arrow)); addInitializer (new Life (new UniformRandom (50, 10))); addInitializer (new Position (new SinglePoint (320, 200))); addInitializer (nuovo Velocity (nuovo LazySectorZone (3, 2))); addInitializer (new Mass (new UniformRandom (2, 1))); addInitializer (new Scale (new UniformRandom (1, 0.2))); // azioni addAction (new Age ()); addAction (new DeathLife ()); addAction (new Move ()); addAction (new Oriented ()); addAction (new ScaleCurve (10, 10)); 

Il progresso attuale sembra così.


Passaggio 2: renderlo turbolento

Aggiungere il seguente codice che crea un bitmap di rumore Perlin 640 da 480 nel costruttore di emitter. Per spiegazioni dettagliate su ciascun parametro di BitmapData.perlinNoise () metodo, è possibile fare riferimento a questa documentazione. Per semplificare, il codice seguente crea una bitmap rumore Perlin con "ottave" approssimativamente della dimensione 50X50 e il disturbo è costituito da canali di colore rosso e verde.

 // crea un rumore var per Perlin noise bitmap: BitmapData = new BitmapData (640, 400); noise.perlinNoise (50, 50, 1, 0, true, true, 1 | 2);

Quindi, crea un BitmapField oggetto e assegnare i dati bitmap ad esso attraverso il aggiornare() metodo. Quindi il resto del codice riguardante il Gravità l'azione è esattamente la stessa dell'esempio precedente.

 // crea un campo uniforme che restituisce sempre (0.5, 0) campo var: BitmapField = new BitmapField (); field.channelX = 1; // imposta il canale X su red field.channelY = 2; // imposta il canale Y su green field.max = 1; // imposta il valore assoluto del componente vettoriale massimo field.update (rumore); // aggiorna il campo con la bitmap del rumore // attira la massa delle particelle nell'account field.massless = false; // crea un'azione gravitazionale e aggiunge il campo a essa gravità: Gravity = new Gravity (); gravity.addField (campo); // aggiunge l'azione gravitazionale all'emettitore addAction (gravità);

Ora prova di nuovo il film e vedrai che le nostre frecce volanti stanno vivendo turbolenze.


Campi personalizzati

Abbiamo usato il UniformField e BitmapField fornito da Stardust, e ora stiamo andando a creare i nostri campi personalizzati estendendo il Campo classe e override del getMotionData2D () metodo.

Il getMotionData2D prende un Particle2D parametro come input, che contiene l'informazione del vettore di posizione della particella, e questo è l'input per il campo. Il metodo restituisce a MotionData2D oggetto, che rappresenta l'output del campo. Questo è tutto ciò che devi sapere per creare un campo personalizzato. Creiamo un campo vortice personalizzato.

Di seguito è riportato il grafico visualizzato del nostro campo di vortici. È abbastanza auto-esplicativo perché è chiamato un campo di vortici.

Ecco la formula per il nostro campo di vortici.

E la classe del campo vortice è semplice come il codice qui sotto. Abbiamo usato il Vec2D classe per fare tutto il lavoro sporco di ruotare un vettore di 90 gradi in senso orario e impostare il valore assoluto del vettore. Quindi, eseguiamo il dump delle componenti xey del vettore in a MotionData2D oggetto, che è del tipo di oggetto da restituire.

 package import idv.cjcat.stardust.twoD.fields. *; import idv.cjcat.stardust.twoD.geom. *; import idv.cjcat.stardust.twoD.particles. *; public class VortexField estende Field public var centerX: Number; public var centerY: Number; forza var pubblica: numero; funzione pubblica VortexField (centerX: Number = 0, centerY: Number = 0, strength: Number = 1) this.centerX = centerX; this.centerY = centerY; this.strength = strength;  override protected function calculateMotionData2D (particle: Particle2D): MotionData2D var dx: Number = particle.x - centerX; var dy: Number = particle.y - centerY; var vec: Vec2D = new Vec2D (dx, dy); vec.length = strength; vec.rotateThis (90); restituire nuovo MotionData2D (vec.x, vec.y); 

Ora che abbiamo il nostro campo personalizzato, proviamo nell'esempio seguente.


Esempio: Vortex

Questo esempio funge da test drive per il nostro campo vettoriale a vortice. È semplice come scambiare un campo con uno nuovo. Cambia il seguente codice dall'esempio precedente da questo

 // crea un campo uniforme che restituisce sempre (0.5, 0) campo var: BitmapField = new BitmapField (); field.channelX = 1; // imposta il canale X su red field.channelY = 2; // imposta il canale Y su green field.max = 1; // imposta la lunghezza massima vecotr field.update (noise); // aggiorna il campo con il bitmap del rumore

a questo

 // crea un campo vortex centrato su (320, 200) con il campo var di forza 1: VortexField = new VortexField (); field.centerX = 320; field.centerY = 200; field.strength = 1;

E abbiamo finito. Puoi testare il film e vedere l'effetto vortice. Dolce!


Conclusione

Hai visto come usare il Gravità azione e Campo classe per influenzare la velocità delle particelle. E hai imparato come creare i tuoi campi vettoriali personalizzati da utilizzare come campi gravitazionali. La prossima parte di questo tutorial ti mostrerà come utilizzare i deflettori per manipolare contemporaneamente sia la posizione delle particelle che la velocità.

Grazie mille per la lettura!