Nel mio precedente tutorial Shoot Out Stars con Stardust Particle Engine, ho spiegato il flusso di lavoro di base di Stardust. Questa volta, prenderemo le cose in ordine ed esamineremo un paio di tecniche per creare veri effetti particellari 3D!
Inizieremo con una dimostrazione di come utilizzare il motore 3D nativo di Stardust. Poi, ti mostrerò come far funzionare Stardust con Papervision3D; creeremo effetti particellari 3D con la classe Particelle di Papervision3D e la classe DisplayObject3D.
Stiamo andando a riprendere da dove avevamo lasciato nel primo tutorial. L'ultima volta abbiamo creato particelle stellari e circolari che sparano da un punto, crescendo fino a una dimensione massima e poi riducendo a zero, mentre si muovono gradualmente più lentamente nel tempo (chiamato effetto di smorzamento). Questa volta, faremo la stessa cosa, ma in 3D. Invece delle particelle che si muovono in un cerchio, si muoveranno in una sfera.
Come prima, crea un nuovo documento Flash con dimensioni di 640 x 400, una frequenza di fotogrammi di 60 fps e uno sfondo scuro (ho utilizzato un gradiente blu scuro).
Disegna una stella e un cerchio bianco, quindi convertili in simboli, separatamente. Questi sono i due simboli che useremo come particelle più tardi. Assegna un nome al simbolo stella "Stella" e al simbolo del cerchio "Cerchio", esportato per ActionScript con gli stessi nomi di classe.
(Se non sei un grande artista, puoi scaricare la fonte nella parte superiore della pagina e usare i miei simboli dalla libreria del mio FLA.)
Clic Finestra> Componenti per aprire il pannello Componenti, quindi trascinare un pulsante dalla cartella dell'interfaccia utente sul palco. Impostare l'etichetta su "Pausa" e denominarla "pause_btn". Utilizzeremo questo pulsante per mettere in pausa gli effetti particellari 3D, consentendo in tal modo agli utenti di girare la fotocamera per ottenere un migliore assaggio dell'ambiente 3D.
Creare una nuova classe di documento e denominarla StarParticles3D.
pacchetto import flash.display.Sprite; la classe pubblica StarParticles3D estende Sprite public function StarParticles ()
Non sai come usare una classe di documenti? Leggi questo suggerimento rapido.
I tre pacchetti principali di Stardust sono:
Nel tutorial precedente abbiamo utilizzato inizializzatori e azioni dai pacchetti comuni e 2D. In questo tutorial, utilizzeremo ancora elementi dal pacchetto comune, ma non dal pacchetto twoD. Invece, utilizzeremo elementi dal pacchetto threeD.
La struttura di classe del pacchetto threeD è praticamente la stessa del pacchetto twoD, tranne per il fatto che gli elementi hanno una dimensione extra. Un elemento 3D ha lo stesso nome della sua controparte 2D, ma il suo nome termina con "3D". Ad esempio, il MOVE3D l'azione nel pacchetto 3D aggiorna le posizioni delle particelle nello spazio 3D secondo le velocità, proprio come la sua controparte 2D nel pacchetto 2D, la Mossa azione.
Crea un nuovo file AS chiamato StarEmitter.as; al suo interno, crea una nuova classe StarEmitter, che estende la classe Emitter3D:
package import idv.cjcat.stardust.threeD.emitters.Emitter3D; // non dimenticare di importarlo! public star StarEmitter estende Emitter3D public function StarEmitter (clock: Clock) // passa l'oggetto clock al super costruttore della superclasse (clock);
Ricorda il parametro Orologio? È usato per controllare il tasso di creazione di particelle. Dobbiamo includerlo nella funzione di costruzione, in modo che possiamo passare un orologio in seguito.
Dato che stiamo permettendo agli utenti di mettere in pausa gli effetti particellari, metteremo tutte le azioni in un singolo oggetto CompositeAction, che è essenzialmente un gruppo di azioni. Disattivando questa singola azione composita, possiamo "disattivare" tutte le azioni sottostanti. Dichiarare una variabile per un'azione composita nella classe emettitore. Accederemo a questa variabile nella classe del documento, quindi deve essere pubblica:
public var pausibleActions: CompositeAction;
Dichiarare le costanti che verranno utilizzate come parametri delle particelle nella classe dell'emettitore. Abbiamo già trattato lo scopo di queste costanti nel tutorial precedente. La maggior parte dei nomi è auto-esplicativa. Questi vanno all'interno della classe ma all'esterno della funzione di costruzione. Sentiti libero di tornare qui più tardi e modificare i numeri per vedere gli effetti.
private static const LIFE_AVG: Number = 30; private static const LIFE_VAR: Number = 10; const statico privato SCALE_AVG: Number = 1; const statico privato SCALE_VAR: Number = 0.4; const statico privato GROWING_TIME: Number = 5; private static const SHRINKING_TIME: Number = 10; const statico privato SPEED_AVG: Number = 30; const statico privato SPEED_VAR: Number = 10; const statico privato OMEGA_AVG: Number = 0; const statico privato OMEGA_VAR: Number = 5; private static const DAMPING: Number = 0.1;
Nel tutorial precedente ho dimostrato come utilizzare SwitchInitializer per creare particelle con diversi oggetti di visualizzazione. Stavo usando l'inizializzatore DisplayObjectClass, che inizializza l'aspetto delle particelle con gli oggetti di visualizzazione. Questo era per effetti particellari 2D; qui useremo la sua controparte 3D, l'inizializzatore DisplayObject3D.
Aggiungi il seguente codice alla funzione di costruzione dell'emettitore:
// cambia gli inizializzatori per le particelle stella e cerchio var doc1: DisplayObjectClass3D = new DisplayObjectClass3D (Star); var doc2: DisplayObjectClass3D = new DisplayObjectClass3D (Circle); var si: SwitchInitializer = new SwitchInitializer ([doc1, doc2], [1, 1]); addInitializer (SI);
Come il precedente tutorial; aggiungi gli altri inizializzatori mostrati sotto. Nota che alcuni di loro hanno nomi simili a quelli del precedente tutorial, ma terminano in "3D".
Questo codice va nella funzione di costruzione di StarEmitter:
addInitializer (new Life (new UniformRandom (LIFE_AVG, LIFE_VAR))); addInitializer (new Scale (new UniformRandom (SCALE_AVG, SCALE_VAR))); addInitializer (new Position3D (new SinglePoint3D ())); addInitializer (nuovo Velocity3D (nuovo SphereShell (0, 0, 0, SPEED_AVG, SPEED_VAR))); addInitializer (new Rotation3D (null, null, new UniformRandom (0, 180))); addInitializer (nuovo Omega3D (null, null, new UniformRandom (OMEGA_AVG, OMEGA_VAR)));
Crea un'azione composita e aggiungine alcune azioni. Quindi aggiungere questa azione composita all'emettitore; questo farà sì che le particelle eseguano le azioni. Hai visto queste azioni nel tutorial precedente (alcune di esse in versione 2D), quindi non le spiegherò più. Di nuovo, questo codice va nella funzione di costruzione di StarEmitter:
pausibleActions = new CompositeAction (); pausibleActions.addAction (new Age ()); pausibleActions.addAction (new DeathLife ()); pausibleActions.addAction (new Move3D ()); pausibleActions.addAction (new Spin3D ()); pausibleActions.addAction (new Damping3D (DAMPING)); pausibleActions.addAction (new ScaleCurve (GROWING_TIME, SHRINKING_TIME)); addAction (pausibleActions);
Va bene, abbiamo finito con l'emettitore. Ora è il momento di costruire la nostra classe di documenti.
Innanzitutto, dichiarare le costanti per il raggio della telecamera orbitante, la distanza della telecamera dall'origine e la velocità dell'emettitore:
const statico privato CAMERA_RADIUS: Number = 250; private static const PARTICLE_RATE: Number = 0.5;
(Come prima, i consts entrano nella classe ma fuori dalla funzione di costruzione).
Quindi, dichiarare le variabili per un emettitore, un orologio costante e un DisplayObjectRenderer3D (nello stesso punto dei comandi):
emettitore privato var: StarEmitter; orologio var privato: SteadyClock; renderer var privato: DisplayObjectRenderer3D;
Nel costruttore, inizializza l'orologio, l'emettitore e il renderer. Inoltre, imposta la posizione e la direzione della videocamera iniziale, facendola guardare all'origine:
// crea l'orologio e l'orologio emettitore = new SteadyClock (PARTICLE_RATE); emitter = new StarEmitter (orologio); // possiamo fare questo perché abbiamo dato al costruttore di StarEmitter un parametro di clock // crea il renderer e il contenitore container sprite var: Sprite = new Sprite (); container.x = 320, container.y = 200; renderer = new DisplayObjectRenderer3D (container); renderer.addEmitter (emettitore); // aggiungi il contenitore allo stage addChild (container); // aggiungi nuovamente il pulsante di pausa in modo che si trovi sopra il contenitore addChild (pause_btn); // imposta la posizione e la direzione iniziale della camera renderer.camera.position.set (0, 0, -CAMERA_RADIUS); renderer.camera.direction.set (0, 0, CAMERA_RADIUS);
Creare una funzione di gestione nella classe del documento per gestire l'evento click del pulsante di pausa:
funzione privata togglePause (e: MouseEvent): void if (e.target.label == "Pause") e.target.label = "Resume"; clock.ticksPerCall = 0; // stop the clock emitter.pausibleActions.active = false; // disattiva le azioni dell'emettitore else e.target.label = "Pause"; clock.ticksPerCall = PARTICLE_RATE; // restart the clock emitter.pausibleActions.active = true; // riattivare le azioni dell'emettitore
... quindi registra l'ascoltatore per il pulsante di pausa, nella funzione di costruzione:
pause_btn.addEventListener (MouseEvent.CLICK, togglePause);
Crea un gestore per l'evento ENTER_FRAME. Questo è il nostro ciclo principale. Aggiorna la posizione della telecamera chiamando il metodo updateCamera () (che codificheremo in un minuto) e chiama il metodo step () dell'emettitore, che mantiene attivi gli effetti delle particelle:
funzione privata mainLoop (e: Event): void updateCamera (); emitter.step ();
Di nuovo, registra un listener nel costruttore:
addEventListener (Event.ENTER_FRAME, mainLoop);
Definire ora il metodo updateCamera () richiamato nel passaggio precedente. Questo viene utilizzato per spostare la fotocamera nello spazio 3D a seconda della posizione del mouse. (Se desideri maggiori informazioni su come funziona consulta questo articolo di Wikipedia.)
I numeri magici usati per generare theta e phi sono solo il risultato di tentativi ed errori; sentiti libero di provare le tue equazioni.
funzione privata 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);
Si noti che ho usato il metodo StardustMath.clamp (); questo assicura che il valore phi sia mantenuto tra la metà positiva e quella negativa PI.
Ok, abbiamo finito! Questo è tutto ciò che dobbiamo fare per far funzionare un emettitore 3D con il motore 3D nativo di Stardust. Diamo un'occhiata al risultato. È possibile fare clic sul pulsante di pausa per mettere in pausa l'effetto particella e spostare il mouse per orbitare attorno alla telecamera:
dimostrazione Visualizza onlineSe desideri vedere il codice sorgente completo, cerca nella cartella denominata "01 - Stardust Native 3D Engine" nell'origine.
Passare dal motore 3D nativo di Stardust a Papervision3D è facile. Dovremo solo utilizzare un renderer diverso e visualizzare l'inizializzatore dell'oggetto.
(Non hai mai usato Papervision3D prima? Dai un'occhiata a questo tutorial per principianti.)
Per prima cosa useremo la classe Particelle di Papervision3D. Potresti non avere familiarità con questo; Ti mostrerò come utilizzare la classe DisplayObject3D più comune in seguito.
Cambia il seguente codice nella classe emitter:
var doc1: DisplayObjectClass3D = new DisplayObjectClass3D (Star); var doc2: DisplayObjectClass3D = new DisplayObjectClass3D (Circle);
a questo:
var mat1: MovieParticleMaterial = new MovieParticleMaterial (new Star ()); var mat2: MovieParticleMaterial = new MovieParticleMaterial (new Circle ()); var doc1: PV3DParticle = new PV3DParticle ([mat1]); var doc2: PV3DParticle = new PV3DParticle ([mat2]);
Come forse già sapete, la classe MovieParticleMaterial ci consente di utilizzare gli oggetti di visualizzazione come l'aspetto delle particelle in Papervision3D. Creiamo un'istanza Star e Circle da utilizzare come materiale particellare. L'inizializzatore PV3DParticle prende il posto dell'inizializzatore DisplayObjectClass3D; il suo costruttore accetta una serie di parametri, che saranno tutti aggiunti ad un oggetto Particles.
Questo è tutto ciò che dobbiamo fare riguardo all'emettitore. Ora modificheremo la classe del documento.
Il contenitore di destinazione per il nostro renderer non è più un oggetto Sprite. Invece, creeremo particelle in un oggetto Particles. Dovremo cambiare il tipo di renderer da DisplayObjectRenderer3D a PV3DParticleRenderer.
Dichiarare le seguenti variabili per gli oggetti relativi a Papervision3D:
scena var privata: SceneObject3D; particelle private var: particelle; fotocamera privata var: Camera3D; origine var privata: DisplayObject3D; private var renderEngine: BasicRenderEngine; viewport var privata: Viewport3D;
Il codice nel costruttore della classe del documento è ora:
initPV3D (); //questa è nuova! clock = new SteadyClock (PARTICLE_RATE); emitter = new StarEmitter (orologio); renderer = new PV3DParticleRenderer (particelle); //questa è nuova! renderer.addEmitter (emettitore); pause_btn.addEventListener (MouseEvent.CLICK, togglePause); addEventListener (Event.ENTER_FRAME, mainLoop);
Il metodo initPV3D () imposta l'ambiente Papervision3D. Ecco il codice:
funzione privata initPV3D (): void // crea la scena scena = new SceneObject3D (); // crea Particles object particles = new Particles (); // crea la camera e inizializza la sua posizione camera = nuova Camera3D (); camera.position.x = 0; camera.position.y = 0; camera.position.z = -CAMERA_RADIUS; // crea un DO3D che rappresenta l'origine dell'origine = new DisplayObject3D (); origin.x = origine.y = origine.z = 0; // puntare la telecamera all'origine camera.target = origine; scene.addChild (origine); scene.addChild (particelle); // crea il motore di rendering e viewport renderEngine = new BasicRenderEngine (); viewport = new Viewport3D (640, 400); // aggiunge il viewport allo stage addChild (viewport); // aggiungi di nuovo il pulsante di pausa in modo che si trovi sopra la vista addChild (pause_btn);
Ora Stardust aggiorna solo le proprietà degli oggetti 3D; Il motore di rendering di Papervision3D si sta assumendo la responsabilità del rendering. Questo è come appare il nostro nuovo ciclo principale:
funzione privata mainLoop (e: Event): void updateCamera (); emitter.step (); renderEngine.renderScene (scene, camera, viewport); //questa è nuova!
Ora che stiamo usando la fotocamera di Papervision3D, dovremo anche modificare il metodo updateCamera ():
funzione privata 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); // nota che questo è negativo ora var z: Number = CAMERA_RADIUS * Math.sin (theta) * Math.cos (phi); camera.x = x; // aggiorniamo ciascuna delle proprietà x, y, z della telecamera di PV3D separatamente camera.y = y; camera.z = z;
Ok, siamo passati con successo dal motore 3D nativo di Stardust a Papervision3D. Ora controlliamo il risultato. Nota l'effetto di pixelizzazione sulle particelle. Questo perché Papervision3D prima disegna gli oggetti vettoriali in bitmap prima di utilizzarli come materiali particellari.
dimostrazione Visualizza onlinePuoi trovare tutto il codice sorgente per questo nella cartella "02 - Papervision3D Particles".
Finora, abbiamo lavorato con "cartelloni 2D": oggetti piatti, come la carta. È possibile creare oggetti 3D "reali" di particelle, come gli oggetti DisplayObject3D di Papervision3D. Dovremo solo usare un altro inizializzatore. Ora passiamo alla parte finale di questo tutorial. Creeremo particelle cubo rosso e blu.
Cambiamo l'inizializzatore relativo all'aspetto delle particelle per l'ultima volta.
Prima di ciò, dichiarare una variabile LightObject3D nella classe emitter. Useremo il FlatShadeMaterial per gli oggetti DisplayObject3D, che richiedono una fonte di luce. Inoltre, dichiara le seguenti costanti: le useremo come parametri per FlastShadeMaterial e per determinare le dimensioni dei cubi:
public var light: LightObject3D; const statico privato LIGHT_COLOR_1: uint = 0xCC3300; statico privato const LIGHT_COLOR_2: uint = 0x006699; const statico privato AMBIENT_COLOR_1: uint = 0x881100; const statico privato AMBIENT_COLOR_2: uint = 0x002244; const statico privato CUBE_SIZE: Number = 15;
Ora cambia il seguente codice nella classe emitter:
var mat1: MovieParticleMaterial = new MovieParticleMaterial (new Star ()); var mat2: MovieParticleMaterial = new MovieParticleMaterial (new Circle ()); var doc1: PV3DParticle = new PV3DParticle ([mat1]); var doc2: PV3DParticle = new PV3DParticle ([mat2]);
a questo:
light = new LightObject3D (); var mat1: FlatShadeMaterial = new FlatShadeMaterial (light, LIGHT_COLOR_1, AMBIENT_COLOR_1); var mat2: FlatShadeMaterial = new 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 = new PV3DDisplayObject3DClass (Cube, params1); var doc2: PV3DDisplayObject3DClass = new PV3DDisplayObject3DClass (Cube, params2);
Il nuovo aspetto delle particelle verrà inizializzato come cubi 3D rossi e blu. Il primo parametro costruttore per l'inizializzatore PV3DDisplayObject3DClass è la classe che intendiamo istanziare per le particelle (quindi qui, è la classe Cube) e il secondo parametro è una matrice di parametri costruttore per questa classe Cube.
In precedenza, poiché stavamo lavorando con "cartelloni 2D", contava solo la rotazione attorno all'asse Z. Ora che stiamo lavorando con veri oggetti 3D, dobbiamo passare tre riferimenti di oggetti Random ai costruttori Rotation3D e Omega3D, uno per ciascun asse.
Cambia il seguente codice nella classe emitter:
addInitializer (new Rotation3D (null, null, new UniformRandom (0, 180))); addInitializer (nuovo Omega3D (null, null, new UniformRandom (OMEGA_AVG, OMEGA_VAR)));
a questo:
var rotationRandom: UniformRandom = new UniformRandom (0, 180); var omegaRandom: UniformRandom = new UniformRandom (OMEGA_AVG, OMEGA_VAR); addInitializer (new Rotation3D (rotationRandom, rotationRandom, rotationRandom)); addInitializer (nuovo Omega3D (omegaRandom, omegaRandom, omegaRandom));
Questa volta, anziché utilizzare un oggetto Particles come contenitore delle particelle, utilizzeremo un oggetto DisplayObject3D come contenitore. Dichiarare una variabile per questo contenitore nella classe del documento:
contenitore var privato: DisplayObject3D;
Inoltre, avremo bisogno di un altro tipo di renderer per creare particelle nel nuovo contenitore. Cambia il tipo di renderer da PV3DParticleRenderer a PV3DDisplayObject3DRenderer. Il codice nel costruttore della classe del documento dovrebbe ora assomigliare a questo:
initPV3D (); clock = new SteadyClock (PARTICLE_RATE); emitter = new StarEmitter (orologio); renderer = new PV3DDisplayObject3DRenderer (container); // questo è cambiato! renderer.addEmitter (emettitore); pause_btn.addEventListener (MouseEvent.CLICK, togglePause); addEventListener (Event.ENTER_FRAME, mainLoop);
Nella funzione initPV3D (), ora è necessario inizializzare la variabile contenitore e aggiungerla alla scena. Aggiungi queste due linee alla fine di tale funzione:
container = new DisplayObject3D (); scene.addChild (contenitori);
Nel metodo updateCamera (), desideriamo che la luce segua la fotocamera, quindi avremo l'illusione che la luce "spara" sempre dai nostri occhi. Cambia il seguente codice:
camera.x = x; camera.y = y; camera.z = z;
a questo:
emitter.light.x = camera.x = x; emitter.light.y = camera.y = y; emitter.light.z = camera.z = z;
Ora la fonte di luce è sempre nello stesso punto della fotocamera.
Sì, abbiamo finalmente finito con questo tutorial. Non più codifica. Diamo un'occhiata al nostro risultato finale, con fantastici cubi 3D rossi e blu!
dimostrazione Visualizza onlineIl codice sorgente per questo può essere trovato nella cartella "Papervision3D DisplayObject3D".
Il flusso di lavoro per la creazione di effetti particellari 3D con Stardust è praticamente lo stesso di quello per gli effetti 2D. Devi solo scegliere un diverso set di inizializzatori, azioni e riproduttori. Stardust supporta anche altri motori 3D, inclusi ZedBox e ND3D. L'utilizzo è quasi lo stesso. Dovrai solo utilizzare un diverso set di inizializzatori e renderer. Potresti anche estendere le classi Initializer, Action e Renderer per lavorare con qualsiasi motore 3D che ti piace!
Ora hai le basi, perché non tornare alle console create nel passaggio 6 e giocare con loro per vedere gli effetti?
Spero che questo tutorial ti aiuti a capire meglio Stardust e ti rende più familiare e a tuo agio con il flusso di lavoro di Stardust. Grazie per aver letto!