In mijn vorige tutorial Shoot Out Stars met de Stardust Particle Engine, heb ik de basisworkflow van Stardust uitgelegd. Deze keer gaan we verder en onderzoeken we een aantal technieken om echte 3D-deeltjeseffecten te creëren!
We beginnen met een demonstratie van hoe de Stardust's eigen 3D-engine te gebruiken. Vervolgens zal ik u laten zien hoe u Stardust kunt laten werken met Papervision3D; we maken 3D-deeltjeseffecten met Papervision3D's deeltjesklasse en DisplayObject3D-klasse.
We gaan verder waar we gebleven waren in de eerste tutorial. De laatste keer hebben we ster gecreëerd en cirkelden deeltjes die vanaf een punt schoten, uitgroeiden tot een maximale grootte en vervolgens krimpen tot niets, terwijl ze geleidelijk langzamer bewegen (een dempend effect genoemd). Deze keer doen we hetzelfde, maar dan in 3D. In plaats van dat de deeltjes in een cirkel uitkomen, gaan ze in een bol weg.
Net als voorheen maak je eerst een nieuw Flash-document met dimensies van 640x400, een framesnelheid van 60 fps en een donkere achtergrond (ik heb een donkerblauw verloop gebruikt).
Teken een ster en een witte cirkel en converteer ze vervolgens naar symbolen, afzonderlijk. Dit zijn de twee symbolen die we later als deeltjes gaan gebruiken. Noem het stersymbool "Ster" en cirkel symbool "Cirkel", geëxporteerd naar ActionScript met dezelfde klassenamen.
(Als je niet echt een artiest bent, kun je de bron bovenaan de pagina downloaden en mijn symbolen uit de bibliotheek van mijn FLA gebruiken.)
Klik Venster> Componenten om het componentenpaneel te openen en sleep vervolgens een knop uit de map Gebruikersinterface naar het werkgebied. Zet het label op "Pause" en noem het "pause_btn". We gaan deze knop gebruiken om de 3D-deeltjeseffecten te onderbreken, zodat gebruikers de camera kunnen ronddraaien om een betere smaak van de 3D-omgeving te krijgen.
Maak een nieuwe documentklasse en noem deze StarParticles3D.
pakket import flash.display.Sprite; openbare klasse StarParticles3D breidt uit openbare functie StarParticles ()
Weet u niet zeker hoe u een documentklasse moet gebruiken? Lees deze snelle tip.
De drie hoofdpakketten in Stardust zijn:
In de vorige zelfstudie hebben we initializers en acties van de algemene en tweedelige pakketten gebruikt. In deze zelfstudie gebruiken we nog steeds elementen uit het algemene pakket, maar niet uit het tweed pakket. In plaats daarvan zullen we elementen uit het threeD-pakket gebruiken.
De klassestructuur van het threeD-pakket is vrijwel hetzelfde als in het tweed-pakket, behalve dat de elementen een extra dimensie hebben. Een 3D-element heeft dezelfde naam als zijn 2D-tegenhanger, maar de naam eindigt met "3D". Bijvoorbeeld de Move3D actie in het 3D-pakket update deeltijdposities in de 3D-ruimte volgens de snelheden, net als de 2D-tegenhanger in het 2D-pakket, de verhuizing actie.
Maak een nieuw AS-bestand met de naam StarEmitter.as; erin, maak een nieuwe klas StarEmitter, die de klasse Emitter3D uitbreidt:
pakket import idv.cjcat.stardust.threeD.emitters.Emitter3D; // vergeet dit niet te importeren! openbare klasse StarEmitter breidt Emitter3D uit openbare functie StarEmitter (klok: klok) // geeft het klokobject door aan de constructor super van de superklasse (klok);
Weet je nog de parameter Clock? Het wordt gebruikt om de snelheid waarmee deeltjes worden gemaakt te regelen. We moeten het opnemen in de constructorfunctie, zodat we er later een klok aan kunnen doorgeven.
Omdat we toestaan dat gebruikers de partikeleffecten pauzeren, gaan we alle acties in een enkel CompositeAction-object inpakken, wat in wezen een groep acties is. Door deze enkele samengestelde actie te deactiveren, kunnen we alle onderliggende acties "uitschakelen". Declareer een variabele voor een samengestelde actie in de emittorklasse. We zullen deze variabele in de documentklasse benaderen, dus het moet openbaar zijn:
public var pausibleActions: CompositeAction;
Declareer constanten die zullen worden gebruikt als deeltjesparameters in de emitterklasse. In de vorige tutorial hebben we al het doel van deze constanten behandeld. De meeste namen spreken voor zich. Deze gaan de klas in, maar buiten de constructorfunctie. Kom gerust later terug en verander de cijfers om de effecten te zien.
private static const LIFE_AVG: Number = 30; private static const LIFE_VAR: Number = 10; private static const SCALE_AVG: Number = 1; private static const SCALE_VAR: Number = 0.4; privé statische const GROWING_TIME: Number = 5; private static const SHRINKING_TIME: Number = 10; private static const SPEED_AVG: Number = 30; private static const SPEED_VAR: Number = 10; private static const OMEGA_AVG: Number = 0; private static const OMEGA_VAR: Number = 5; privé statische const DAMPING: Number = 0.1;
In de vorige tutorial liet ik zien hoe je de SwitchInitializer gebruikt om deeltjes te maken met verschillende weergaveobjecten. Ik gebruikte de DisplayObjectClass-initialisator, die het uiterlijk van de deeltjes initialiseert met weergaveobjecten. Dat was voor 2D-deeltjeseffecten; hier gaan we de 3D-tegenpartij ervan gebruiken, de DisplayObject3D-initialisator.
Voeg de volgende code toe aan de constructorfunctie van de emitter:
// schakel initializers voor ster- en cirkelpartikels var doc1: DisplayObjectClass3D = new DisplayObjectClass3D (Star); var doc2: DisplayObjectClass3D = nieuw DisplayObjectClass3D (Circle); var si: SwitchInitializer = nieuwe SwitchInitializer ([doc1, doc2], [1, 1]); addInitializer (si);
Hetzelfde als de vorige zelfstudie; voeg de andere initializers toe die hieronder worden getoond. Merk op dat sommige van hen dezelfde namen hebben als die in de vorige tutorial, maar eindigen in "3D".
Deze code wordt gebruikt in de constructorfunctie van de StarEmitter:
addInitializer (nieuwe Life (nieuwe UniformRandom (LIFE_AVG, LIFE_VAR))); addInitializer (nieuwe schaal (nieuwe UniformRandom (SCALE_AVG, SCALE_VAR))); addInitializer (nieuwe Position3D (nieuwe SinglePoint3D ())); addInitializer (nieuwe Velocity3D (nieuwe SphereShell (0, 0, 0, SPEED_AVG, SPEED_VAR))); addInitializer (nieuwe Rotation3D (null, null, nieuwe UniformRandom (0, 180))); addInitializer (nieuwe Omega3D (null, null, nieuwe UniformRandom (OMEGA_AVG, OMEGA_VAR)));
Maak een samengestelde actie en voeg er enkele acties aan toe. Voeg vervolgens deze samengestelde actie toe aan de emitter; dit zorgt ervoor dat de deeltjes de acties uitvoeren. U hebt deze acties in de vorige zelfstudie (sommige in 2D-versie) gezien, dus ik zal ze niet allemaal opnieuw uitleggen. Nogmaals, deze code wordt gebruikt in de constructorfunctie van de StarEmitter:
pausibleActions = new CompositeAction (); pausibleActions.addAction (new Age ()); pausibleActions.addAction (nieuwe DeathLife ()); pausibleActions.addAction (nieuwe Move3D ()); pausibleActions.addAction (nieuwe Spin3D ()); pausibleActions.addAction (new Damping3D (DAMPING)); pausibleActions.addAction (nieuwe ScaleCurve (GROWING_TIME, SHRINKING_TIME)); Addaction (pausibleActions);
Oké, we zijn klaar met de zender. Nu is het tijd om onze documentklasse te bouwen.
Verklaar allereerst constanten voor de radius van de rond de camera, de afstand van de camera tot de oorsprong en de emittersnelheid:
privé statische const CAMERA_RADIUS: Number = 250; privé statische const PARTICLE_RATE: Number = 0.5;
(Net als voorheen gaan consts naar de klas maar buiten de constructorfunctie.)
Declareer vervolgens variabelen voor een emitter, een stabiele klok en een DisplayObjectRenderer3D (op dezelfde plaats als de consts):
privé-var-emitter: StarEmitter; privé var-klok: SteadyClock; private var renderer: DisplayObjectRenderer3D;
In de constructor initialiseert u de klok, de emitter en de renderer. Stel ook de positie en richting van de initiële camera in en kijk naar de oorsprong:
// maak de klok en emitterklok = nieuwe SteadyClock (PARTICLE_RATE); emitter = nieuwe StarEmitter (klok); // we kunnen dit doen omdat we de constructor van StarEmitter een klokparameter hebben gegeven // creëer de renderer en de container sprite var container: Sprite = new Sprite (); container.x = 320, container.y = 200; renderer = nieuw DisplayObjectRenderer3D (container); renderer.addEmitter (emitter); // voeg de container toe aan de stage addChild (container); // voeg de pauzeknop opnieuw toe zodat deze zich boven de container addChild (pause_btn) bevindt; // stel de beginpositie en richting van de camera in renderer.camera.position.set (0, 0, -CAMERA_RADIUS); renderer.camera.direction.set (0, 0, CAMERA_RADIUS);
Maak een handler-functie in de documentklasse om de klikgebeurtenis van de pauzeknop af te handelen:
privéfunctie togglePause (e: MouseEvent): void if (e.target.label == "Pauze") e.target.label = "Hervatten"; clock.ticksPerCall = 0; // stop de klok emitter.pausibleActions.active = false; // deactiveer de acties van de zender else e.target.label = "Pauze"; clock.ticksPerCall = PARTICLE_RATE; // herstart de klok emitter.pausibleActions.active = true; // reactiveer de acties van de zender
... registreer dan de luisteraar voor de pauzeknop, in de constructorfunctie:
pause_btn.addEventListener (MouseEvent.CLICK, togglePause);
Maak een handler voor de gebeurtenis ENTER_FRAME. Dit is onze belangrijkste lus. Het werkt de positie van de camera bij door de updateCamera () -methode aan te roepen (die we in een minuut zullen coderen) en roept de step () -methode aan, die de partikeleffecten in stand houdt:
private function mainLoop (e: Event): void updateCamera (); emitter.step ();
Nogmaals, registreer een luisteraar in de constructor:
addEventListener (Event.ENTER_FRAME, mainLoop);
Definieer nu de updateCamera () -methode die in de vorige stap werd aangeroepen. Dit wordt gebruikt om de camera in 3D-ruimte te verplaatsen, afhankelijk van de positie van de muis. (Als u meer informatie wilt over hoe het werkt, bekijk dan dit Wikipedia-artikel.)
De magische getallen die worden gebruikt om theta en phi te genereren zijn slechts het resultaat van vallen en opstaan; voel je vrij om je eigen vergelijkingen te proberen.
persoonlijke functie updateCamera (): void var theta: Number = 0.02 * (mouseX - 320); var phi: Number = 0.02 * (mouseY - 200); phi = StardustMath.clamp (phi, -StardustMath.HALF_PI, StardustMath.HALF_PI); var x: Number = CAMERA_RADIUS * Math.cos (theta) * Math.cos (phi); var y: Number = CAMERA_RADIUS * Math.sin (phi); var z: Number = CAMERA_RADIUS * Math.sin (theta) * Math.cos (phi); renderer.camera.position.set (x, y, z); renderer.camera.direction.set (-x, -y, -z);
Merk op dat ik de StardustMath.clamp () -methode heb gebruikt; dit zorgt ervoor dat de phi-waarde tussen positieve en negatieve halve PI wordt gehouden.
Oké, we zijn klaar! Dat is alles wat we moeten doen om een 3D-zender te laten werken met de eigen 3D-engine van Stardust. Laten we naar het resultaat kijken. U kunt op de pauzeknop klikken om het partikeleffect te pauzeren en de muis rond te bewegen om in een baan om de camera te draaien:
demonstratie Bekijk het onlineAls je de volledige broncode wilt zien, kijk dan in de map "01 - Stardust Native 3D Engine" in de bron.
Overstappen van Stardust's eigen 3D-engine naar Papervision3D is eenvoudig. We zullen gewoon een andere renderer moeten gebruiken en de initialisatie van objecten moeten weergeven.
(Nooit eerder Papervision3D gebruikt? Bekijk deze beginnershandleiding.)
Eerst zullen we de Particles-klasse van Papervision3D gebruiken. Je bent hier misschien niet bekend mee; Ik zal u laten zien hoe u de meer algemene DisplayObject3D-klasse later kunt gebruiken.
Wijzig de volgende code in de emittorklasse:
var doc1: DisplayObjectClass3D = nieuwe DisplayObjectClass3D (ster); var doc2: DisplayObjectClass3D = nieuw DisplayObjectClass3D (Circle);
hieraan:
var mat1: MovieParticleMaterial = new MovieParticleMaterial (new Star ()); var mat2: MovieParticleMaterial = new MovieParticleMaterial (nieuwe Circle ()); var doc1: PV3DParticle = new PV3DParticle ([mat1]); var doc2: PV3DParticle = new PV3DParticle ([mat2]);
Zoals u wellicht al weet, kunnen we met de klasse MovieParticleMaterial weergaveobjecten gebruiken als uiterlijk van deeltjes in Papervision3D. We maken een Star- en Circle-instantie die als deeltjesmateriaal kan worden gebruikt. De PV3DP-partikelinitialisator neemt de plaats in van de DisplayObjectClass3D-initialisator; de constructor accepteert een array van parameters, die allemaal zullen worden toegevoegd aan een Particles-object.
Dit is alles wat we moeten doen met betrekking tot de zender. Vervolgens zullen we de documentklasse aanpassen.
De doelcontainer voor onze renderer is niet langer een Sprite-object. In plaats daarvan gaan we deeltjes creëren in een Particles-object. We zullen het type van de renderer moeten veranderen van DisplayObjectRenderer3D naar PV3DParticleRenderer.
Verklaar de volgende variabelen voor aan Papervision3D gerelateerde objecten:
privé var-scène: SceneObject3D; Partik var deeltjes: deeltjes; privé var camera: Camera3D; private var origin: DisplayObject3D; private var renderEngine: BasicRenderEngine; privé var viewport: Viewport3D;
De code in de constructor van de documentklasse is nu:
initPV3D (); //dit is nieuw! klok = nieuwe SteadyClock (PARTICLE_RATE); emitter = nieuwe StarEmitter (klok); renderer = nieuwe PV3DParticleRenderer (particles); //dit is nieuw! renderer.addEmitter (emitter); pause_btn.addEventListener (MouseEvent.CLICK, togglePause); addEventListener (Event.ENTER_FRAME, mainLoop);
De methode initPV3D () stelt de Papervision3D-omgeving in. Hier is de code:
private function initPV3D (): void // create the scene scene = new SceneObject3D (); // create the Particles object particles = new Particles (); // maak de camera en initialiseer de positiecamera = nieuwe Camera3D (); camera.position.x = 0; camera.position.y = 0; camera.position.z = -CAMERA_RADIUS; // maak een DO3D die de oorsprong oorsprong = nieuw DisplayObject3D (); origin.x = origin.y = origin.z = 0; // richt de camera op de oorsprong camera.target = oorsprong; scene.addChild (oorsprong); scene.addChild (deeltjes); // create the render engine and viewport renderEngine = new BasicRenderEngine (); viewport = nieuwe Viewport3D (640, 400); // voeg de viewport toe aan de stage addChild (viewport); // voeg de pauzeknop opnieuw toe zodat deze bovenop de viewport addChild (pause_btn) staat;
Nu werkt Stardust alleen de eigenschappen van de 3D-objecten bij; De renderengine van Papervision3D neemt de verantwoordelijkheid van rendering over. Dit is hoe onze nieuwe hoofdlus eruit ziet:
private function mainLoop (e: Event): void updateCamera (); emitter.step (); renderEngine.renderScene (scène, camera, viewport); //dit is nieuw!
Nu we de camera van Papervision3D gebruiken, moeten we ook de methode updateCamera () aanpassen:
persoonlijke functie updateCamera (): void var theta: Number = 0.02 * (mouseX - 320); var phi: Number = 0.02 * (mouseY - 200); phi = StardustMath.clamp (phi, -StardustMath.HALF_PI, StardustMath.HALF_PI); var x: Number = CAMERA_RADIUS * Math.cos (theta) * Math.cos (phi); var y: Number = -CAMERA_RADIUS * Math.sin (phi); // merk op dat dit nu negatief is var z: Number = CAMERA_RADIUS * Math.sin (theta) * Math.cos (phi); camera.x = x; // we updaten elk van de x, y, z-eigenschappen van de camera van de PV3D afzonderlijk camera.y = y; camera.z = z;
Oké, we zijn succesvol overgeschakeld van Stardust's eigen 3D-engine naar Papervision3D. Laten we nu eens kijken naar het resultaat. Let op het pixelatie-effect op de deeltjes. Dat komt omdat Papervision3D eerst vectorobjecten in bitmaps tekent voordat ze als deeltjesmateriaal worden gebruikt.
demonstratie Bekijk het onlineJe vindt hier alle broncode in de map "02 - Papervision3D Particles".
Tot nu toe hebben we gewerkt met "2D-reclameborden" - platte objecten, zoals papier. Het is mogelijk om "echte" 3D-deeltjesobjecten te maken, zoals de DisplayObject3D-objecten van Papervision3D. We zullen gewoon een andere initializer moeten gebruiken. Laten we nu beginnen met het laatste deel van deze tutorial. We zullen rode en blauwe kubusdeeltjes maken.
We gaan de initializer voor het uiterlijk van de deeltjes voor de laatste keer wijzigen.
Maak daarvoor een variabele LightObject3D in de emmerklasse. We gaan het FlatShadeMaterial gebruiken voor de DisplayObject3D-objecten waarvoor een lichtbron nodig is. Verklaar ook de volgende constanten - we gebruiken deze als parameters voor het FlastShadeMaterial en voor het bepalen van de kubussenmaten:
public var light: LightObject3D; private static const LIGHT_COLOR_1: uint = 0xCC3300; private static const LIGHT_COLOR_2: uint = 0x006699; privé statische const AMBIENT_COLOR_1: uint = 0x881100; private static const AMBIENT_COLOR_2: uint = 0x002244; private static const CUBE_SIZE: Number = 15;
Wijzig nu de volgende code in de emittorklasse:
var mat1: MovieParticleMaterial = new MovieParticleMaterial (new Star ()); var mat2: MovieParticleMaterial = new MovieParticleMaterial (nieuwe Circle ()); var doc1: PV3DParticle = new PV3DParticle ([mat1]); var doc2: PV3DParticle = new PV3DParticle ([mat2]);
hieraan:
licht = nieuw LightObject3D (); var mat1: FlatShadeMaterial = nieuw FlatShadeMaterial (light, LIGHT_COLOR_1, AMBIENT_COLOR_1); var mat2: FlatShadeMaterial = nieuw FlatShadeMaterial (light, 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 = nieuwe PV3DDisplayObject3DClass (Cube, params1); var doc2: PV3DDisplayObject3DClass = nieuwe PV3DDisplayObject3DClass (Cube, params2);
Het uiterlijk van de nieuwe deeltjes wordt geïnitialiseerd als rode en blauwe 3D-kubussen. De eerste constructor-parameter voor de PV3DDisplayObject3DClass-initialisator is de klasse die we willen instantiëren voor de deeltjes (dus hier is het de klasse Cube) en de tweede parameter is een array constructorparameters voor deze kubusklasse.
Omdat we eerder met "2D-reclameborden" werkten, was alleen de rotatie rond de Z-as van belang. Nu we met echte 3D-objecten werken, moeten we drie willekeurige objectreferenties doorgeven aan de Rotation3D- en Omega3D-constructors, één voor elke as.
Wijzig de volgende code in de emittorklasse:
addInitializer (nieuwe Rotation3D (null, null, nieuwe UniformRandom (0, 180))); addInitializer (nieuwe Omega3D (null, null, nieuwe UniformRandom (OMEGA_AVG, OMEGA_VAR)));
hieraan:
var rotationRandom: UniformRandom = new UniformRandom (0, 180); var omegaRandom: UniformRandom = new UniformRandom (OMEGA_AVG, OMEGA_VAR); addInitializer (nieuwe Rotation3D (rotationRandom, rotationRandom, rotationRandom)); addInitializer (nieuwe Omega3D (omegaRandom, omegaRandom, omegaRandom));
Deze keer gebruiken we, in plaats van een Particles-object als onze deeltjescontainer, een DisplayObject3D als de container. Een variabele voor deze container in de documentklasse declareren:
private var-container: DisplayObject3D;
We hebben ook een ander type renderer nodig voor het maken van deeltjes in de nieuwe container. Wijzig het type van de renderer van PV3DParticleRenderer in PV3DDisplayObject3DRenderer. De code in de constructor van de documentklasse ziet er nu als volgt uit:
initPV3D (); klok = nieuwe SteadyClock (PARTICLE_RATE); emitter = nieuwe StarEmitter (klok); renderer = nieuwe PV3DDisplayObject3DRenderer (container); // dit is veranderd! renderer.addEmitter (emitter); pause_btn.addEventListener (MouseEvent.CLICK, togglePause); addEventListener (Event.ENTER_FRAME, mainLoop);
In de functie initPV3D () moeten we nu de containervariabele initialiseren en deze aan de scène toevoegen. Voeg deze twee regels toe aan het einde van die functie:
container = nieuw DisplayObject3D (); scene.addChild (container);
In de updateCamera () -methode willen we ervoor zorgen dat het licht de camera volgt, dus we zullen de illusie hebben dat het licht altijd uit onze ogen "schiet". Wijzig de volgende code:
camera.x = x; camera.y = y; camera.z = z;
hieraan:
emitter.light.x = camera.x = x; emitter.light.y = camera.y = y; emitter.light.z = camera.z = z;
Nu is de lichtbron altijd op hetzelfde punt als de camera.
Ja, we zijn eindelijk klaar met deze tutorial. Geen codering meer. Laten we eens kijken naar ons eindresultaat, met mooie rode en blauwe 3D-kubussen!
demonstratie Bekijk het onlineDe broncode hiervoor is te vinden in de map "Papervision3D DisplayObject3D".
De workflow voor het maken van 3D-deeltjeseffecten met Stardust is vrijwel hetzelfde als voor 2D-effecten. U kiest gewoon een andere set van initializers, acties en renderers. Stardust ondersteunt ook andere 3D-engines, waaronder ZedBox en ND3D. Het gebruik is bijna hetzelfde. U zult gewoon een andere set initializers en renderers moeten gebruiken. U kunt zelfs de klassen Initializer, Action en Renderer uitbreiden met welke 3D-engine u maar wilt!
Nu heb je de basis, waarom ga je niet terug naar de consts die je in stap 6 hebt gemaakt en speel met ze om de effecten te zien?
Ik hoop dat deze tutorial je helpt Stardust beter te begrijpen en je vertrouwd en comfortabeler maakt met de workflow van Stardust. Bedankt voor het lezen!