Het manipuleren van deeltjesbeweging met Stineust deeltjesmotor - Deel 1

Stardust Particle Engine biedt twee belangrijke benaderingen voor het vrij manipuleren van deeltjesbeweging, namelijk gravitatievelden en deflectors. Zwaartekrachtvelden zijn vectorvelden die de versnelling van een deeltje beïnvloeden, en deflectors manipuleren zowel de positie als de snelheid van een deeltje.

Het eerste deel van deze zelfstudie behandelt de basisprincipes van deeltjesbeweging en zwaartekrachtvelden. Ook laat het zien hoe je je eigen zwaartekrachtvelden creëert. Het tweede deel richt zich op deflectors en het maken van aangepaste deflectors.

Voorafgaande kennis van het standaardgebruik van Stardust is vereist om deze tutorial te kunnen blijven lezen. Als je niet bekend bent met Stardust, kun je mijn vorige tutorial over dit onderwerp bekijken, Sterren met Stardust Particle Engine afschieten, voordat je verder gaat.


Eindresultaat voorbeeld

Laten we eens kijken naar het uiteindelijke resultaat waar we naartoe zullen werken. Dit is een voorbeeld van een aangepast vortex zwaartekrachtveld.


Grondstofbewegingen met deeltjes

Het is tijd voor een snelle flashback op de natuurkunde van de middelbare school. Onthoud basis kinematica? Het draait allemaal om verplaatsing, wat een veel exclusievere manier is om 'positie' en zijn relatie tot tijd te zeggen. Voor de reikwijdte van deze tutorial hebben we alleen een vrij eenvoudige beschrijving van het onderwerp nodig.

Verplaatsing

Verplaatsing betekent de huidige positie van een object. In deze tutorial zijn de "objecten" waar we het vooral mee te maken hebben deeltjes in 2D-ruimte. In de 2D-ruimte wordt de verplaatsing van een object gerepresenteerd door een 2D-vector.

Snelheid

De snelheid van een object geeft aan hoe snel de positie van een object verandert en de richting van de verandering. De snelheid van een object in de 2D-ruimte wordt ook weergegeven door een 2D-vector. De x- en y-componenten van de vector vertegenwoordigen de richting van de positieverandering en de absolute waarde van de vector geeft de snelheid van het object aan, d.w.z. hoe snel het object beweegt.

Versnelling

Versnelling is snelheid als snelheid naar verplaatsing. De versnelling van een object geeft aan hoe snel de snelheid van een voorwerp verandert en de richting van de verandering. Net als de snelheid van een object in de 2D-ruimte, wordt de versnelling van een object gerepresenteerd door een 2D-vector.


Vector velden

Een ander ding dat het vermelden waard is, is het concept van vectorvelden. U kunt in feite een vectorveld bekijken als een functie die een vector als invoer neemt en een andere vector uitvoert. Zwaartekrachtvelden zijn een soort vectorvelden die positievectoren nemen als invoer- en uitvoerversnellingsvectoren. In de simulatie van de natuurkunde wordt bijvoorbeeld meestal een uniforme zwaartekracht naar beneden gericht op alle objecten toegepast; in dit geval is het zwaartekrachtveld dat de zwaartekracht vertegenwoordigt een vectorveld dat een constante vector afgeeft (naar beneden wijzend), ongeacht de invoerpositievectoren.

Dit is een gevisualiseerde grafiek van een vectorveld. De uitgangsvector van een gegeven ingangsvector (1, 2) is (0,5, 0,5), terwijl de uitgang van een ingang (4, 3) (-0,5, 0,5) is.

De Veld klasse in Stardust vertegenwoordigt een 2D vectorveld, en zijn 3D-tegenhanger, de Field3D klasse vertegenwoordigt een 3D-vectorveld. In deze zelfstudie concentreren we ons alleen op 2D-vectorvelden.

De Field.getMotionData () methode kost een Particle2D object, dat de 2D-positie en snelheidsinformatie van een deeltje bevat, als parameter. Deze methode retourneert een MotionData2D object, een 2D-vector "waardeobject" dat bestaat uit x- en y-componenten. Gecombineerd met de Zwaartekracht actie, a Veld object kan worden gebruikt als een zwaartekrachtsveld, waarvan de uitvoer wordt gebruikt om de deeltjessnelheid te manipuleren.

Dat is alles voor de samenvatting van onze natuurkunde op de middelbare school. Nu is het tijd voor een paar echte Stardust-dingen.


De zwaartekrachtactie

Zoals eerder vermeld, de Zwaartekracht actieklasse maakt gebruik van Veld objecten als zwaartekrachtvelden om deeltjessnelheid te manipuleren. Zo maak je een uniform vectorveld, een vectorveld dat een constante vector retourneert, ongeacht welke invoer wordt gegeven, en deze in een Zwaartekracht actie.

 // maak een uniform vectorveld dat naar beneden wijst (denk eraan dat positieve y-coördinaat "omlaag" betekent in Flash) var field: Field = new UniformField (0, 1); // maak een zwaartekrachtactie var zwaartekracht: zwaartekracht = nieuwe zwaartekracht (); // voeg het veld toe aan de zwaartekracht-actie gravity.addField (veld);

Deze Zwaartekracht actie is nu klaar om op dezelfde manier te worden gebruikt als andere gewone Stardust-acties.

 // voeg de zwaartekracht toe aan een emitter emitter.addAction (zwaartekracht);

Voorbeeld: winderige regen

We gaan een winderig regeneffect creëren met behulp van de Zwaartekracht actie.


Stap 1: Fundamenteel regeneffect

Maak een nieuw Flash-document, kies een donkere achtergrondkleur, teken een regendruppel op het werkgebied en converteer de regendruppel naar een filmclipsymbool, geëxporteerd voor ActionScript met de naam "Raindrop". Verwijder daarna de regendruppelinstantie op het werkgebied.

Nu gaan we een AS-bestand maken voor de documentklasse en een AS-bestand voor de regengever. Ik zal de code hier niet uitleggen, omdat ik in mijn vorige zelfstudie al het basisgebruik van Stardust heb behandeld. Als u niet bekend bent met Stardust of als u wat verfrissing nodig hebt, raad ik u ten zeerste aan om mijn vorige zelfstudie te lezen voordat u doorgaat.

Dit is de documentklasse.

 pakket 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 breidt Sprite uit private var emitter: Emitter; private var renderer: Renderer; publieke functie WindyRain () emitter = new RainEmitter (); renderer = nieuw DisplayObjectRenderer (this); renderer.addEmitter (emitter); addEventListener (Event.ENTER_FRAME, mainLoop);  private function mainLoop (e: Event): void emitter.step (); 

En dit is de emmerklasse die in de documentklasse wordt gebruikt.

 pakket 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 breidt Emitter2D uit openbare functie RainEmitter () super (nieuwe SteadyClock (1)); // initializers addInitializer (nieuwe DisplayObjectClass (Raindrop)); addInitializer (nieuwe positie (nieuwe RectZone (-300, -40, 940, 20))); addInitializer (nieuwe Velocity (nieuwe RectZone (-0,5, 2, 1, 3))); addInitializer (nieuwe Massa (nieuwe UniformRandom (2, 1))); addInitializer (nieuwe schaal (nieuwe UniformRandom (1, 0.2))); // acties addAction (nieuwe verplaatsing ()); addAction (nieuw georiënteerd (1, 180)); addAction (nieuwe DeathZone (nieuwe RectZone (-300, -40, 960, 480), true)); 

Dit is hoe onze huidige voortgang eruit ziet.


Stap 2: Maak het winderig

Nu gaan we het regeneffect winderig maken door een uniform zwaartekrachtveld toe te voegen dat de regendruppels naar rechts "trekt". Voeg de volgende code toe aan de constructor van de RainEmitter klasse.

 // maak een uniform veld dat altijd terugkeert (0.5, 0) var field: Field = new UniformField (0.5, 0); // neem deeltjesmassa in rekening field.massless = false; // creëer een zwaartekrachtactie en voeg het veld eraan toe var gravity: Gravity = new Gravity (); gravity.addField (veld); // voeg de zwaartekracht toe aan de emitter addAction (zwaartekracht);

Merk op dat we de Field.massless eigenschap false, is standaard waar. Wanneer ingesteld op true, zorgt deze eigenschap ervoor dat velden fungeren als gewone zwaartekrachtvelden, waardoor alle objecten evenveel worden beïnvloed, ongeacht hun massa. Wanneer de eigenschap echter op false is ingesteld, wordt de deeltjesmassa in aanmerking genomen: deeltjes met grotere massa worden minder door het veld beïnvloed, terwijl deeltjes met een kleinere massa meer worden beïnvloed. Dat is waarom we de Massa initializer in onze vorige emittercode, om wat willekeur toe te voegen aan het regeneffect.

Test de film opnieuw, en dit is hoe onze uitkomst eruit ziet. Regendruppels worden nu beïnvloed door een zwaartekrachtveld en worden allemaal naar rechts getrokken.


Voorbeeld: turbulentie

Nu gaan we de UniformField object met een BitmapField object, vectorvelden terugzenden op basis van de kleurkanalen van een bitmap, om een ​​turbulentie-effect te creëren. Als u al een tijdje met ActionScript hebt gewerkt, verwacht u mogelijk het BitmapData.perlinNoise () methode bij het denken aan turbulentie, en dat is precies wat we gaan doen.

Dit is hoe een voorbeeld Perlin-ruisbitmap eruitziet. Perlin-ruis is een uitstekend algoritme om ruis te genereren voor het simuleren van turbulentie, watergolven, wolken, etc. U kunt hier meer informatie over Perlin-ruis vinden.

Bitmapvelden

Je vraagt ​​je misschien af ​​of we het gaan gebruiken BitmapData.perlinNoise () methode om een ​​perlin-ruisbitmap te genereren, hoe gaan we deze bitmap gebruiken als een vectorveld? Nou, dit is wat de BitmapField klasse is voor. Er is een bitmap voor nodig en deze converteert naar een vectorveld.

Laten we zeggen dat we een bitmapveld hebben waarvan X kanaal ingesteld op rood en Y-kanaal is groen. Dit betekent dat wanneer het veld een invoervector neemt, zeg, (2, 3), het naar de pixel van de bitmap op (2, 3) kijkt en de X-component van de uitvoervector wordt bepaald uit het rode kanaal van de pixel en de Y-component bepaald van het groene kanaal. Als de componenten van een ingangsvector geen gehele getallen zijn, worden ze eerst afgerond.

De waarde van een kleurenkanaal varieert van 0 tot 255, waarbij 127 het gemiddelde is. Een waarde kleiner dan 127 wordt door het bitmapveld als negatief beschouwd, terwijl een waarde groter dan 127 positief wordt aangenomen. Nul is het meest negatief nummer en 255 is het meest positief een. Als we bijvoorbeeld een pixel met een kleur 0xFF0000 in hexadecimale weergave hebben, wat betekent een rood kanaal met waarde 255 en groen kanaal met 0, dan is de uitvoer van het bitmapveld voor deze pixel een vector met X-component van een meest positief mogelijk aantal en Y-component van a meest negatief mogelijk nummer, waar dit meest positieve / negatieve mogelijke nummer, of Maximaal nummer, is specifiek voor het bitmapveld. Om preciezer te zijn, hier is de formule van de pixel-naar-vector-conversie.


Stap 1: Basisvliegpijlen

Maak een nieuw Flash-document. Teken een pijl op het werkgebied, converteer het naar een symbool met de naam "Arrow" en exporteer het voor ActionScript.

Maak een AS-bestand voor de documentklasse. Dit is bijna hetzelfde als in het vorige voorbeeld.

 pakket 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 breidt Sprite uit private var emitter: Emitter; private var renderer: Renderer; publieke functie Turbulence () emitter = nieuwe ArrowEmitter (); renderer = nieuw DisplayObjectRenderer (this); renderer.addEmitter (emitter); addEventListener (Event.ENTER_FRAME, mainLoop);  private function mainLoop (e: Event): void emitter.step (); 

Maak een ander AS-bestand voor onze emmerklasse.

 pakket 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. *; openbare klasse ArrowEmitter breidt Emitter2D uit openbare functie ArrowEmitter () super (nieuwe SteadyClock (1)); // initializers addInitializer (nieuwe DisplayObjectClass (pijl)); addInitializer (nieuwe Life (nieuwe UniformRandom (50, 10))); addInitializer (nieuwe positie (nieuwe singlePoint (320, 200))); addInitializer (nieuwe Velocity (nieuwe LazySectorZone (3, 2))); addInitializer (nieuwe Massa (nieuwe UniformRandom (2, 1))); addInitializer (nieuwe schaal (nieuwe UniformRandom (1, 0.2))); // actions addAction (new Age ()); addAction (nieuwe DeathLife ()); addAction (nieuwe verplaatsing ()); addAction (nieuw georiënteerd ()); addAction (nieuwe ScaleCurve (10, 10)); 

De huidige voortgang ziet er als volgt uit.


Stap 2: Maak het turbulent

Voeg de volgende code toe die een 640-by-480 Perlin-ruisbitmap-gegevens in de emitterconstructor maakt. Voor gedetailleerde uitleg over elke parameter van de BitmapData.perlinNoise () methode, kunt u deze documentatie raadplegen. Om het eenvoudig te maken, maakt de volgende code een Perlin-ruisbitmap met "octaven" ruwweg van de grootte 50X50, en de ruis bestaat uit rode en groene kleurkanalen.

 // een Perlin-ruisbitmap maken data var noise: BitmapData = nieuwe BitmapData (640, 400); noise.perlinNoise (50, 50, 1, 0, waar, waar, 1 | 2);

Maak vervolgens een BitmapField object en wijs de bitmapgegevens toe via de bijwerken() methode. Dan is de rest van de code betreffende de Zwaartekracht actie is precies hetzelfde als in het vorige voorbeeld.

 // maak een uniform veld dat altijd terugkeert (0,5, 0) var field: BitmapField = new BitmapField (); field.channelX = 1; // stel X kanaal in op rood veld.kanaal Y = 2; // stel Y-kanaal in op groen field.max = 1; // stel de max. vectorcomponent absolute waarde field.update (ruis) in; // werk het veld bij met de ruisbitmap // neem de deeltjesmassa mee in rekening field.massless = false; // creëer een zwaartekrachtactie en voeg het veld eraan toe var gravity: Gravity = new Gravity (); gravity.addField (veld); // voeg de zwaartekracht toe aan de emitter addAction (zwaartekracht);

Test de film opnieuw en je zult zien dat onze vliegende pijlen nu turbulentie ervaren.


Aangepaste velden

We hebben de UniformField en BitmapField geleverd door Stardust, en nu gaan we onze eigen aangepaste velden maken door de Veld class en overschrijft de getMotionData2D () methode.

De getMotionData2D duurt een Particle2D parameter als invoer, die de positievectorinformatie van het deeltje bevat, en dat is de invoer voor het veld. De methode retourneert een MotionData2D object, dat de uitvoer van het veld vertegenwoordigt. Dat is alles wat u moet weten om een ​​aangepast veld te maken. Laten we een aangepast vortexveld maken.

Hieronder ziet u de gevisualiseerde grafiek van ons vortexveld. Het is nogal vanzelfsprekend waarom het een vortexveld wordt genoemd.

Dit is de formule voor ons vortexveld.

En de vortexveldklasse is net zo eenvoudig als de onderstaande code. We hebben gebruik gemaakt van de Vec2D klas om al het vuile werk te doen door een vector 90 graden met de klok mee te draaien en de absolute waarde van de vector in te stellen. Vervolgens dumpen we de x- en y-componenten van de vector in een MotionData2D object, dat van het objecttype is dat moet worden geretourneerd.

 pakket import idv.cjcat.stardust.twoD.fields. *; import idv.cjcat.stardust.twoD.geom. *; import idv.cjcat.stardust.twoD.particles. *; public class VortexField breidt Field uit public var centerX: Number; public var centerY: Number; public var strength: Number; openbare functie VortexField (centerX: Number = 0, centerY: Number = 0, strength: Number is 1) this.centerX = centerX; this.centerY = centerY; this.streng = sterkte;  override-beschermde functie calculationMotionData2D (particle: Particle2D): MotionData2D var dx: Number = particle.x - centerX; var dy: Number = particle.y - centerY; var vec: Vec2D = nieuwe Vec2D (dx, dy); vec.length = sterkte; vec.rotateThis (90); retourneer nieuwe MotionData2D (vec.x, vec.y); 

Nu we ons eigen veld hebben, laten we het uittesten in het volgende voorbeeld.


Voorbeeld: Vortex

Dit voorbeeld dient als een testrit voor ons vortexvectorveld. Het is zo simpel als het ruilen van een veld met een nieuw veld. Wijzig hier de volgende code van het vorige voorbeeld

 // maak een uniform veld dat altijd terugkeert (0,5, 0) var field: BitmapField = new BitmapField (); field.channelX = 1; // stel X kanaal in op rood veld.kanaal Y = 2; // stel Y-kanaal in op groen field.max = 1; // stel de max vecotr length field.update (noise) in; // werk het veld bij met de ruisbitmap

hieraan

 // maak een vortexveld gecentreerd op (320, 200) met sterkte 1 var-veld: VortexField = new VortexField (); field.centerX = 320; field.centerY = 200; field.strength = 1;

En we zijn klaar. U kunt de film testen en het vortexeffect zien. Zoet!


Conclusie

Je hebt gezien hoe je de Zwaartekracht actie en Veld klasse om de deeltjessnelheid te beïnvloeden. En u hebt geleerd hoe u uw eigen aangepaste vectorvelden kunt maken die als zwaartekrachtvelden kunnen worden gebruikt. Het volgende deel van deze tutorial laat zien hoe je deflectors kunt gebruiken om zowel de positie van de deeltjes als de snelheid tegelijkertijd te manipuleren.

Heel erg bedankt voor het lezen!