I min tidligere veiledning Shoot Out Stars med Stardust Particle Engine, forklarte jeg den grunnleggende arbeidsflyten til Stardust. Denne gangen tar vi ting videre og undersøker et par teknikker for å skape sanne 3D-partikkeleffekter!
Vi starter med en demonstrasjon av hvordan du bruker Stardust's native 3D-motor. Da vil jeg vise deg hvordan du får Stardust til å jobbe med Papervision3D; vi skal skape 3D partikkeleffekter med Papervision3Ds Particles klasse og DisplayObject3D klasse.
Vi skal plukke opp hvor vi sluttet i første opplæring. Siste gang vi opprettet stjernen og sirkelpartikler som skyter ut fra et punkt, vokser til maksimal størrelse og deretter krymper til ingenting, mens de beveger seg gradvis langsommere over tid (kalt en dempende effekt). Denne gangen vil vi gjøre det samme, men i 3D. I stedet for at partiklene flytter ut i en sirkel, vil de bevege seg ut i en sfære.
Akkurat som før, må du først opprette et nytt Flash-dokument med dimensjoner på 640x400, en ramme på 60fps, og en mørk bakgrunn (jeg brukte en mørk blå gradient).
Tegn en stjerne og en hvit sirkel, og konverter dem til symboler, separat. Dette er de to symbolene vi skal bruke som partikler senere. Navn stjernesymbolet "Star" og sirkelsymbolet "Circle", eksportert til ActionScript med samme klassenavn.
(Hvis du ikke er mye av en artist, kan du laste ned kilden øverst på siden og bruke symbolene mine fra biblioteket til min FLA.)
Klikk Vindu> Komponenter å hente opp komponentpanelet, og dra deretter en knapp fra brukergrensesnittmappen til scenen. Sett etiketten til "Pause" og navnet "pause_btn". Vi skal bruke denne knappen for å stoppe 3D-partikkeleffekter, slik at brukerne kan snurre kameraet rundt for å få bedre smak av 3D-miljøet.
Opprett en ny dokumentklasse og navnet den StarParticles3D.
pakke import flash.display.Sprite; offentlig klasse StarParticles3D utvider Sprite offentlig funksjon StarParticles ()
Ikke sikker på hvordan du bruker en dokumentklasse? Les denne Quick Tip.
De tre hovedpakken i Stardust er:
I den tidligere opplæringen brukte vi initiativer og handlinger fra de vanlige og toD-pakkene. I denne opplæringen bruker vi fortsatt elementer fra den vanlige pakken, men ikke fra toD-pakken. I stedet vil vi bruke elementer fra treD-pakken.
Klassestrukturen til treD-pakken er stort sett den samme som i toD-pakken, bortsett fra at elementene har en ekstra dimensjon. Et 3D-element har samme navn som dets 2D-motpart, men navnet endemåter med "3D". For eksempel, Move3D handling i 3D-pakken oppdaterer partikkelposisjoner i 3D-rom i henhold til hastighetene, akkurat som dens 2D-motpart i 2D-pakken, den Bevege seg handling.
Lag en ny AS-fil som heter StarEmitter.as; inne i den, opprett en ny klasse StarEmitter, som utvider Emitter3D-klassen:
pakke import idv.cjcat.stardust.threeD.emitters.Emitter3D; // ikke glem å importere dette! offentlig klasse StarEmitter utvider Emitter3D offentlig funksjon StarEmitter (klokke: Klokke) // pass klokkeobjektet til superklassens konstruksjons super (klokke);
Husker du klokkeparameteren? Det er vant til å kontrollere graden av partikkelskaping. Vi må inkludere det i konstruktørfunksjonen, slik at vi kan sende et klokke til det senere.
Siden vi lar brukerne pause partikkeleffekter, skal vi pakke alle handlingene til et enkelt CompositeAction-objekt, som egentlig er en gruppe handlinger. Ved å deaktivere denne enkeltkomposisjonen, kan vi "slå av" alle underliggende handlinger. Erklære en variabel for en sammensatt handling i emitterklassen. Vi får tilgang til denne variabelen i dokumentklassen, så den må være offentlig:
offentlig var pausibleActions: CompositeAction;
Erklære konstanter som vil bli brukt som partikkelparametere i emitterklassen. Vi har allerede dekket formålet med disse konstantene i den forrige opplæringen. De fleste navnene er selvforklarende. Disse går inn i klassen, men utenfor konstruktørfunksjonen. Du er velkommen til å komme tilbake hit senere og endre tallene for å se effektene.
privat statisk const LIFE_AVG: Number = 30; privat statisk const LIFE_VAR: Number = 10; privat statisk const SCALE_AVG: Number = 1; privat statisk konst SCALE_VAR: Nummer = 0,4; privat statisk const GROWING_TIME: Number = 5; privat statisk const SHRINKING_TIME: Nummer = 10; privat statisk const SPEED_AVG: Number = 30; privat statisk const SPEED_VAR: Number = 10; privat statisk const OMEGA_AVG: Number = 0; privat statisk const OMEGA_VAR: Number = 5; privat statisk const DAMPING: Number = 0.1;
I den tidligere opplæringen viste jeg hvordan du bruker SwitchInitializer til å lage partikler med forskjellige skjermobjekter. Jeg brukte DisplayObjectClass-initialisereren, som initierer partikkelutseende med skjermobjekter. Det var for 2D partikkel effekter; her skal vi bruke sin 3D-motpart, DisplayObject3D-initialisatoren.
Legg til følgende kode i emitterens konstruktørfunksjon:
// bytte initialiserere for stjerne og sirkelpartikler var doc1: DisplayObjectClass3D = ny DisplayObjectClass3D (Star); var doc2: DisplayObjectClass3D = ny DisplayObjectClass3D (Circle); var si: SwitchInitializer = ny SwitchInitializer ([doc1, doc2], [1, 1]); addInitializer (si);
Samme som forrige oppgave; legg til de andre initiativer som vises nedenfor. Legg merke til at noen av dem har lignende navn til de i den forrige opplæringen, men slutt i "3D".
Denne koden går i StarEmitters konstruktørfunksjon:
addInitializer (new Life (new UniformRandom (LIFE_AVG, LIFE_VAR))); addInitializer (new Scale (new UniformRandom (SCALE_AVG, SCALE_VAR))); addInitializer (ny posisjon3D (new SinglePoint3D ())); addInitializer (new Velocity3D (new SphereShell (0, 0, 0, SPEED_AVG, SPEED_VAR))); addInitializer (ny Rotation3D (null, null, ny UniformRandom (0, 180))); addInitializer (ny Omega3D (null, null, ny UniformRandom (OMEGA_AVG, OMEGA_VAR)));
Lag en sammensatt handling, og legg til noen handlinger på den. Legg deretter til denne sammensatte virkningen til emitteren; Dette vil få partiklene til å utføre handlingene. Du har sett disse handlingene i den forrige opplæringen (noen av dem i 2D-versjonen), så jeg vil ikke forklare dem igjen. Igjen, denne koden går i StarEmitters konstruktørfunksjon:
pausibleActions = ny CompositeAction (); pausibleActions.addAction (new Age ()); pausibleActions.addAction (new DeathLife ()); pausibleActions.addAction (new Move3D ()); pausibleActions.addAction (ny Spin3D ()); pausibleActions.addAction (new Damping3D (DAMPING)); pausibleActions.addAction (ny ScaleCurve (GROWING_TIME, SHRINKING_TIME)); addAction (pausibleActions);
Ok, vi er ferdige med emitteren. Nå er det på tide å bygge vår dokumentklasse.
Først erklærer du konstanter for det radiobane som er borte, kameraets avstand fra opprinnelsen og emitterens hastighet:
privat statisk konst CAMERA_RADIUS: Nummer = 250; privat statisk const PARTICLE_RATE: Number = 0.5;
(Som før går consts inne i klassen, men utenfor konstruktørfunksjonen.)
Deretter erklærer variabler for en emitter, en jevn klokke og en DisplayObjectRenderer3D (på samme sted som consts):
privat varemitter: StarEmitter; privat var klokke: SteadyClock; privat var renderer: DisplayObjectRenderer3D;
I konstruktøren skal du initialisere klokken, emitteren og rendereren. Still inn også startkameraets posisjon og retning, slik at den ser på opprinnelsen:
// lage klokke og emitter klokke = ny SteadyClock (PARTICLE_RATE); emitter = ny StarEmitter (klokke); // vi kan gjøre dette fordi vi ga StarEmitters konstruktør en klokkeparameter // lage gjengivelsen og dens container sprite var container: Sprite = new Sprite (); container.x = 320, container.y = 200; renderer = ny DisplayObjectRenderer3D (container); renderer.addEmitter (emitter); // legg beholderen til scenen addChild (container); // Legg pauseknappen igjen slik at den er på toppen av beholderen addChild (pause_btn); // angi kameraets startposisjon og retning renderer.camera.position.set (0, 0, -CAMERA_RADIUS); renderer.camera.direction.set (0, 0, CAMERA_RADIUS);
Opprett en håndteringsfunksjon i dokumentklassen for å håndtere klikkhendelsen på pause-knappen:
privat funksjon togglePause (e: MouseEvent): void if (e.target.label == "Pause") e.target.label = "Resume"; clock.ticksPerCall = 0; // stoppe klokken emitter.pausibleActions.active = false; // deaktiver emitterens handlinger else e.target.label = "Pause"; clock.ticksPerCall = PARTICLE_RATE; // start klokken emitter.pausibleActions.active = true; // reaktivere emitterens handlinger
... deretter registrer lytteren for pause-knappen, i konstruktørfunksjonen:
pause_btn.addEventListener (MouseEvent.CLICK, togglePause);
Opprett en handler for ENTER_FRAME-hendelsen. Dette er vår hovedløkke. Det oppdaterer kameraets posisjon ved å kalle metoden updatingCamera () (som vi vil kode om et minutt) og kaller emitterens trinn () -metode, som holder partikkel-effektene i gang:
privat funksjon mainLoop (e: Event): void updateCamera (); emitter.step ();
Igjen, registrer en lytter i konstruktøren:
addEventListener (Event.ENTER_FRAME, mainLoop);
Definer nå metoden updateCamera () som ble kalt i forrige trinn. Dette brukes til å flytte kameraet i 3D-plass, avhengig av musens posisjon. (Hvis du vil ha mer informasjon om hvordan det fungerer, sjekk ut denne Wikipedia-artikkelen.)
De magiske tallene som brukes til å generere theta og phi er bare resultatet av prøve og feil; vær så snill å prøve dine egne ligninger.
privat funksjon oppdateringCamera (): void var theta: Nummer = 0,02 * (mouseX - 320); var phi: tall = 0,02 * (mouseY - 200); phi = StardustMath.clamp (phi, -StardustMath.HALF_PI, StardustMath.HALF_PI); var x: Nummer = CAMERA_RADIUS * Math.cos (theta) * Math.cos (phi); var y: Nummer = CAMERA_RADIUS * Math.sin (phi); var z: Nummer = CAMERA_RADIUS * Math.sin (theta) * Math.cos (phi); renderer.camera.position.set (x, y, z); renderer.camera.direction.set (-x, -y, -z);
Merk at jeg brukte metoden StardustMath.clamp (); Dette sørger for at phi-verdien holdes mellom positiv og negativ halv PI.
Ok, vi er ferdige! Det er alt vi trenger å gjøre for å få en 3D-emitter som arbeider med Stardusts native 3D-motor. La oss se på resultatet. Du kan klikke på pause-knappen for å pause partikkel-effekten, og flytte musen rundt for å bane kameraet:
Demo Se det på nettetHvis du vil se full kildekoden, ser du i mappen kalt "01 - Stardust Native 3D Engine" i kilden.
Å bytte fra Stardust's native 3D-motor til Papervision3D er enkelt. Vi må bare bruke en annen gjengivelses- og skjermobjektinitiator.
(Har aldri brukt Papervision3D før? Ta en titt på denne nybegynnerens opplæring.)
Først bruker vi Papervision3D's Particles-klassen. Du er kanskje ikke kjent med dette; Jeg skal vise deg hvordan du bruker den mer vanlige DisplayObject3D-klassen senere.
Endre følgende kode i emitterklassen:
var doc1: DisplayObjectClass3D = ny DisplayObjectClass3D (Star); var doc2: DisplayObjectClass3D = ny DisplayObjectClass3D (Circle);
til dette:
var mat1: MovieParticleMaterial = nytt MovieParticleMaterial (new Star ()); var mat2: MovieParticleMaterial = nytt MovieParticleMaterial (new Circle ()); var doc1: PV3DParticle = ny PV3DParticle ([mat1]); var doc2: PV3DParticle = ny PV3DParticle ([mat2]);
Som du kanskje allerede vet, lar MovieParticleMaterial-klassen oss bruke displayobjekter som partikkelutseende i Papervision3D. Vi lager en Star- og Circle-forekomst som skal brukes som partikkelmateriale. PV3DParticle-initialisatoren tar stedet for DisplayObjectClass3D-initialisatoren; dets konstruktør aksepterer en rekke parametere, som alle vil bli lagt til et partikkelobjekt.
Dette er alt vi trenger å gjøre med emitteren. Deretter endrer vi dokumentklassen.
Målbeholderen for vår renderer er ikke lenger et Sprite-objekt. I stedet skal vi lage partikler i et partikkelobjekt. Vi må bytte rendererens type fra DisplayObjectRenderer3D til PV3DParticleRenderer.
Erklære følgende variabler for Papervision3D-relaterte objekter:
privat var scene: SceneObject3D; private partikler: partikler; privat var kamera: Camera3D; privat var opprinnelse: DisplayObject3D; privat var renderEngine: BasicRenderEngine; privat var viewport: Viewport3D;
Koden i dokumentklassens konstruktør er nå:
initPV3D (); // dette er nytt! klokke = ny SteadyClock (PARTICLE_RATE); emitter = ny StarEmitter (klokke); renderer = ny PV3DParticleRenderer (partikler); // dette er nytt! renderer.addEmitter (emitter); pause_btn.addEventListener (MouseEvent.CLICK, togglePause); addEventListener (Event.ENTER_FRAME, mainLoop);
Metoden initPV3D () setter opp Papervision3D-miljøet. Her er koden:
privat funksjon initPV3D (): void // lage scenen scenen = ny SceneObject3D (); // lage partiklene objektpartikler = nye partikler (); // lage kameraet og initialisere sitt posisjonskamera = nytt Camera3D (); camera.position.x = 0; camera.position.y = 0; camera.position.z = -CAMERA_RADIUS; // opprett en DO3D som representerer opprinnelsesopprinnelsen = ny DisplayObject3D (); origin.x = origin.y = origin.z = 0; // pek kameraet til opprinnelsen camera.target = origin; scene.addChild (opprinnelse); scene.addChild (partikler); // lage render motor og viewport renderEngine = new BasicRenderEngine (); viewport = ny Viewport3D (640, 400); // legg til visningsporten til scenen addChild (visningsport); // Legg pause-knappen igjen slik at den er på toppen av visningsporten addChild (pause_btn);
Nå oppdaterer Stardust bare 3D-objektets egenskaper; Papervision3Ds gjengemotor tar over ansvaret for gjengivelsen. Slik ser vår nye hovedløkke ut:
privat funksjon mainLoop (e: Event): void updateCamera (); emitter.step (); renderEngine.renderScene (scene, kamera, visningsport); // dette er nytt!
Nå som vi bruker Papervision3Ds kamera, må vi også endre metoden updateCamera ():
privat funksjon oppdateringCamera (): void var theta: Nummer = 0,02 * (mouseX - 320); var phi: tall = 0,02 * (mouseY - 200); phi = StardustMath.clamp (phi, -StardustMath.HALF_PI, StardustMath.HALF_PI); var x: Nummer = CAMERA_RADIUS * Math.cos (theta) * Math.cos (phi); var y: Nummer = -CAMERA_RADIUS * Math.sin (phi); // merk dette er negativt nå var z: Nummer = CAMERA_RADIUS * Math.sin (theta) * Math.cos (phi); kamera.x = x; // vi oppdaterer hver av x, y, z egenskapene til PV3D kameraet separat camera.y = y; camera.z = z;
Ok, vi har vellykket byttet fra Stardust's native 3D-motor til Papervision3D. La oss nå sjekke ut resultatet. Legg merke til pikseleringseffekten på partiklene. Det skyldes at Papervision3D først trekker vektorobjekter i bitmaps før de brukes som partikkelmaterialer.
Demo Se det på nettetDu finner all kildekoden for dette i mappen "02 - Papervision3D Partikler".
Så langt har vi jobbet med "2D billboards" - flate gjenstander, som papir. Det er mulig å lage "ekte" 3D-partikkelobjekter, som Papervision3Ds DisplayObject3D-objekter. Vi skal bare bruke en annen initialiserer. La oss nå komme til den siste delen av denne opplæringen. Vi vil skape røde og blå terningpartikler.
Vi skal endre initialisatoren angående partikkelutseendet for siste gang.
Før det erklærer du en LightObject3D-variabel i emitterklassen. Vi skal bruke FlatShadeMaterialet til DisplayObject3D-objektene, som krever en lyskilde. Også, erklære følgende konstanter - vi skal bruke disse som parametere for FlastShadeMaterialet, og for å bestemme størrelsen på kubene:
offentlig var lys: LightObject3D; privat statisk const LIGHT_COLOR_1: uint = 0xCC3300; privat statisk const LIGHT_COLOR_2: uint = 0x006699; privat statisk const AMBIENT_COLOR_1: uint = 0x881100; privat statisk const AMBIENT_COLOR_2: uint = 0x002244; privat statisk const CUBE_SIZE: Number = 15;
Endre deretter følgende kode i emitterklassen:
var mat1: MovieParticleMaterial = nytt MovieParticleMaterial (new Star ()); var mat2: MovieParticleMaterial = nytt MovieParticleMaterial (new Circle ()); var doc1: PV3DParticle = ny PV3DParticle ([mat1]); var doc2: PV3DParticle = ny PV3DParticle ([mat2]);
til dette:
lys = ny LightObject3D (); var mat1: FlatShadeMaterial = nytt FlatShadeMaterial (lys, LIGHT_COLOR_1, AMBIENT_COLOR_1); var mat2: FlatShadeMaterial = nytt FlatShadeMaterial (lys, LIGHT_COLOR_2, AMBIENT_COLOR_2); var matList1: MaterialsList = new MaterialsList (all: mat1); var matList2: MaterialsList = new MaterialsList (all: mat2); var params1: Array = [matList1, CUBE_SIZE, CUBE_SIZE, CUBE_SIZE]; var params2: Array = [matList2, CUBE_SIZE, CUBE_SIZE, CUBE_SIZE]; var doc1: PV3DDisplayObject3DClass = nytt PV3DDisplayObject3DClass (Cube, params1); var doc2: PV3DDisplayObject3DClass = ny PV3DDisplayObject3DClass (Cube, params2);
Det nye partikkelutseendet vil bli initialisert som rød og blå 3D-kuber. Den første konstruktørparameteren for PV3DDisplayObject3DClass initialisereren er den klassen vi ønsker å instansere for partiklene (så her er det Cube-klassen) og den andre parameteren er en rekke konstruktørparametere for denne Cube-klassen.
Tidligere, fordi vi jobbet med "2D billboards", var det bare rotasjonen om Z-aksen som var viktig. Nå som vi jobber med ekte 3D-objekter, må vi sende tre tilfeldige objektreferanser til Rotation3D- og Omega3D-konstruktørene, en for hver akse.
Endre følgende kode i emitterklassen:
addInitializer (ny Rotation3D (null, null, ny UniformRandom (0, 180))); addInitializer (ny Omega3D (null, null, ny UniformRandom (OMEGA_AVG, OMEGA_VAR)));
til dette:
var rotationRandom: UniformRandom = nytt UniformRandom (0, 180); var omegaRandom: UniformRandom = nytt UniformRandom (OMEGA_AVG, OMEGA_VAR); addInitializer (new Rotation3D (rotationRandom, rotationRandom, rotationRandom)); addInitializer (ny Omega3D (omegaRandom, omegaRandom, omegaRandom));
Denne gangen, i stedet for å bruke et Particles-objekt som vår partikkelbeholder, bruker vi en DisplayObject3D som beholder. Erklære en variabel for denne beholderen i dokumentklassen:
privat var container: DisplayObject3D;
Også, vi trenger en annen type gjengivelse for å lage partikler i den nye beholderen. Endre rendererens type fra PV3DParticleRenderer til PV3DDisplayObject3DRenderer. Koden i dokumentklassens konstruktør skal nå se slik ut:
initPV3D (); klokke = ny SteadyClock (PARTICLE_RATE); emitter = ny StarEmitter (klokke); renderer = ny PV3DDisplayObject3DRenderer (container); // dette har endret seg! renderer.addEmitter (emitter); pause_btn.addEventListener (MouseEvent.CLICK, togglePause); addEventListener (Event.ENTER_FRAME, mainLoop);
I initPV3D () -funksjonen må vi initialisere beholdervariabelen og legge den til scenen. Legg til disse to linjene til slutten av den funksjonen:
container = ny DisplayObject3D (); scene.addChild (container);
I oppdateringenCamera () -metoden ønsker vi å få lyset til å følge kameraet, så vi får en illusjon om at lyset alltid "skyter" ut fra øynene våre. Endre følgende kode:
kamera.x = x; camera.y = y; camera.z = z;
til dette:
emitter.light.x = camera.x = x; emitter.light.y = camera.y = y; emitter.light.z = camera.z = z;
Nå er lyskilden alltid på samme punkt som kameraet.
Ja, vi er endelig ferdig med denne opplæringen. Ingen mer koding. La oss se på vårt endelige resultat, med fancy røde og blå 3D-kuber!
Demo Se det på nettetKildekoden for dette finnes i mappen "Papervision3D DisplayObject3D".
Arbeidsflyten for å skape 3D-partikkeleffekter med Stardust er stort sett den samme som for 2D-effekter. Du velger bare et annet sett med initiativer, handlinger og renderere. Stardust støtter også andre 3D-motorer, inkludert ZedBox og ND3D. Bruken er nesten den samme. Du må bare bruke et annet sett med initialiserere og renderere. Du kan til og med utvide Initializer, Action og Renderer-klassene for å jobbe med hvilke 3D-motorer du liker!
Nå har du det grunnleggende, hvorfor ikke gå tilbake til consts laget i trinn 6 og lek med dem for å se effektene?
Jeg håper denne opplæringen hjelper deg med å forstå Stardust mer og gjør deg mer kjent og komfortabel med Stardusts arbeidsflyt. Takk for at du leste!