Uno dei fenomeni più interessanti del mondo Flash è costituito dal numero di progetti disponibili liberamente. Alcuni di questi progetti sono diventati dei veri e propri capisaldi, basti pensare a Papervision3D e al successo che ha riscosso.
Recentemente stanno nascendo diversi progetti di "motori di particelle" (particle engine), che grazie anche alle performance garantite da Actionscript 3 e dal Flash Player 10, sono decisamente più realizzabili.
Un motore di particelle infatti è un sistema in grado di generare forme ed elementi partendo da uno o più emettitori, questi elementi possono poi interagire tra loro o con l'ambiente circostante simulando ad esempio vento, gravità e altri parametri.
L'uso più frequente delle particelle avviene probabilmente nel campo dell'editing video, After Effects ad esempio include un motore di rendering di particelle, ed esistono estensioni dedicate esclusivamente a questo compito; anche per questo i primi motori di particelle per Flash sono stati definiti una sorta di "punto di incontro" tra i due software di casa Adobe.
Sistemi di particelle
Un motore di particelle serve a gestire un "sistema di particelle", (particle system) che viene definito come:
«una tecnica usata nella computer grafica per simulare effetti che sarebbe complesso riprodurre con le tecniche di rendering tradizionale. [...] fuoco, esplosioni, fumo, acqua in movimento, foglie cadenti, nuvole, nebbia, neve, polvere, code di meteore, capelli, pellicce, erba o effetti visuali astratti come incantesimi e effetti di luce. Sebbene nella maggior parte dei casi i sistemi di particelle siano implementati in sistemi a tre dimensioni, vengono usati anche sistemi di particelle bidimensionali» (tradotto da Wikipedia).
Questi effetti solitamente sono creati sfruttando un vasto numero di elementi che hanno proprietà simili, ma non uguali. Per effetti come fuoco o fumo, può essere necessario istanziare molti oggetti e Actionscript 3 si presta molto meglio dei suoi predecessori a questo genere di creazioni grazie all'incremento di performance, in particolare nel player 10 (AS3 è supportato anche dal Flash Player 9).
Perchè usare un motore di particelle in Flash?
Uno degli ambiti più interessati a questi componenti quello dei giochi, tuttavia possiamo anche utilizzarli per arricchire un sito web o un semplice banner.
Un motore di particelle offre poi l'ovvio vantaggio di avere gli elementi già pronti all'uso: non sarà necessario creare da zero il proprio effetto ma sarà possibile usare un sistema già testato e versatile, risparmiando di conseguenza molto tempo e non ridisegnando tutto da zero.
Inoltre alcune librerie introducono effetti cinematici (gravità, vento, etc.) e gestiscono le collisioni tra gli oggetti. Possiamo quindi ottenere, in pochi passaggi, animazioni dal forte impatto che sarebbe molto impegnativo realizzare da zero.
Un motore di particelle in Flash: Stardust
In questo articolo proviamo Stardust (letteralmente polvere di stelle, nome decisamente appropriato!), realizzato in Actionscript 3 dal giovane Allen Chou (CJ Cat).
Il progetto è piuttosto giovane (la versione attuale è la 1.1, anche se è "l'erede" di Emitter, altro progetto dello stesso Chou, che era giunto alla versione 2.1) ma decisamente completo.
Non è l'unica libreria disponibile di questo genere, tuttavia offre un elenco ci feature decisamente interessante, è gratuita (si può fare una donazione all'autore), gli esempi sulla pagina Google Code del progetto sono molto promettenti, è possibile scaricare un vasto set di esempi con incluso il sorgente e infine, aspetto decisamente di rilievo rispetto a molti altri progetti nati da sviluppatori, è ottimamente documentato con testi e videotutorial.
Vediamo allora come installare Stardust e come creare qualche piccolo esempio pratico.
Installare Stardust
Scarichiamo Stardust dal sito del progetto. L'ultima versione attualmente disponibile è la 1.1.132 Beta, per ogni package sono disponibili solitamente tre file: un RAR contenente gli esempi comprensivi di codice sorgente (Beta examples.rar), uno contenente l'engine con classi e documentazione (Beta.rar) e un SWC.
Nel file RAR dell'engine è disponibile anche un template per FlashDevelop, ottimo editor di cui abbiamo già parlato in passato.
Un'altra via per scaricare Stardust è connettersi al repository SVN del progetto, in questo modo avremo a disposizione sempre la versione più aggiornata, che tuttavia potrebbe risultare meno stabile rispetto a quelle disponibili nella sezione dei download, che sono invece le release ufficialmente stabili.
All'interno del file RAR che abbiamo scaricato, troviamo le cartelle:
docs
- contiene la documentazione HTML delle classimanual
- contiene un PDF e 3 esempisrc
- contiene le classi e un progetto FlashDevelop utilizzabile eventualmente come base
Nel nostro tutorial utilizzeremo FlashDevelop abbinato Flash CS4, senza tuttavia utilizzare i template di Stardust così da rendere i passaggi pratici più facili da seguire anche a chi volesse utilizzare il solo Flash.
Impostazione del progetto
Per semplicità utilizziamo una struttura molto semplice per il nostro progetto: creiamo una nuova cartella e al suo interno copiamo dal file RAR la cartella idv
(che troviamo nella directory src
). Inseriremo nella cartella del nostro progetto anche il nostro file FLA.
Aggiungiamo alla cartella del progetto la classe che useremo come "Document Class" e la chiamiamo Esempio_Stardust.as
. Il codice all'inizio sarà il seguente:
package { import flash.display.MovieClip; public class Esempio_Stardust extends MovieClip { public function Esempio_Stardust() { } } }
Per verificare che le classi di Stardust vengano correttamente viste dal nostro file possiamo provare a importarne una, ad esempio così:
package { import flash.display.MovieClip; import idv.cjcat.stardust.twoD.emitters.Emitter2D; public class Esempio_Stardust extends MovieClip { public function Esempio_Stardust() { } } }
Se compilando il file non ci vengono segnalati errori, la struttura è corretta e possiamo iniziare a creare il nostro primo esperimento con Stardust, aspetto che affronteremo nella prossima parte.
Creare le particelle con Stardust
Nella prima parte dell'articolo abbiamo visto come impostare un progetto per utilizzare Stardust, ora è il momento di inserire le particelle nel nostro SWF!
Prima di iniziare la parte "pratica" è bene conoscere gli elementi che Stardust utilizza per generare le particelle; questi elementi sono in parte comuni per tutti i particle engine, tuttavia ogni motore può utilizzare sintassi e metodologie diverse, inoltre ovviamente ogni progetto ha delle caratteristiche esclusive e quindi non comuni a tutti i generatori di particelle. Da questo punto di vista Stardust è un progetto decisamente interessante poichè offre diverse classi, di seguito vediamo le principali e il loro scopo.
Elementi e classi principali di Stardust
Gli elementi principali di ogni sistema di particelle sono essenzialmente due: le particelle stesse (quindi il simbolo grafico che viene usato come fonte) e l'emettitore, ovvero l'elemento che si occupa di generare le varie particelle variandone eventualmente le proprietà.
Ogni particella ha un ciclo di vita, che inizia quando viene generata e si conclude quando viene eliminata. In questo intervallo la particella compie alcune azioni, le più comuni sono il movimento ed eventualmente la trasformazione (solitamente limitata alla dimensione, o in alcuni casi ad esempio al colore).
Per impostare queste operazioni Stardust utilizza diverse classi, ma gli elementi principali necessari per ogni proggetto sono tre:
Elemento | Descrizione |
---|---|
Clock | un oggetto che serve per impostare il numero di particelle da creare e la loro temporizzazione |
Emitter | l'oggetto da cui verranno generate le particelle, quindi è importante disporlo nel punto in cui vogliamo far iniziare il nostro effetto |
Renderer | è la classe che si occupa di costruire e mostrare le particelle |
Ad ogni emitter vengono associati initializer e action che permettono di impostare il comportamento delle particelle, come vedremo a breve.
Questi tre elementi interagiscono tra loro in quanto l'emitter ricava le impostazioni dal clock per il numero di particelle da generare, quindi una volta impostate le azioni per le particelle l'emitter viene aggiunto al renderer che si occupa di mostrare a schermo l'animazione.
Il ciclo di azioni di Stardust
Come ogni motore di particelle, Stardust ha un comportamento ciclico, infatti la stessa serie di azioni viene eseguita più volte per una o più particelle. Il ciclo principale di Stardust è composto da 10 fasi, che vanno dalla creazione alla distruzione di una o più particelle; vediamo in breve le varie operazioni che vengono eseguite:
- Caricamento impostazioni: viene controllato l'elemento
clock
e ricavato il numero di particelle da creare - Creazione: l'
emitter
crea le particelle in base alle impostazioni del clock, le particelle vengono aggiunte allaparticle list
- Inizializzazione: vengono impostate le proprietà e le eventuali azioni delle le particelle
- Rendering delle particelle le particelle vengono aggiunte al rispettivo
Renderer
e vengono visualizzate. Inizia a questo punto il ciclo di vita della particella, dove per ogni passaggio vengono eseguite diverse fasi - Pre-aggiornamento: vengono calcolate ed eseguite le eventuali azioni che possono modificare il comportamento della particella (ad esempio una collisione)
- Azione: le particelle vengono aggiornate, con le eventuali variazioni generate dal punto precedente
- Post-aggiornamento: viene gestita la fase successiva al movimento della particella, solitamente in questa fase non si verificano sostanziali variazioni
- Distruzione della particella: l'emitter controlla se sono presenti particelle che devono essere eliminate perchè hanno concluso il loro ciclo di vita, nel qual caso le rimuove dalla particle list
- Rendering delle particelle distrutte: le particelle che devono essere eliminate vengono rimosse anche dal renderer
- Conclusione del ciclo: viene concluso lo step che ha generato le particelle e si passa allo step successivo
Questi sono i passaggi che Stardust esegue ad ogni step dell'emitter, da ActionScript in realtà noi potremo impostare meno elementi, specialmente per le animazioni che non richiedono particolari interazioni: vediamo un esempio pratico!
Fiocchi di neve con Stardust
Stardust, a differenza di altri engine che offrono alcuni elementi e azioni già impostate, richiede sempre la creazione dell'elemento grafico che verrà poi replicato come particella. Perciò creiamo un nuovo MovieClip con la grafica della nostra particella (in questo caso un fiocco di neve). Ricordiamo inoltre che Stardust è realizzato in ActionScript 3, funziona quindi con i Flash Player 9 e 10 ed è possibile utilizzarlo sia con Flash CS3 che con Flash CS4.
Torniamo allora al documento creato nella prima parte dell'articolo, disegniamo l'elemento che vogliamo utilizzare come particella e convertiamolo in MovieClip: è obbligatorio spuntare l'opzione Export for Actionscript, ricordiamo il nome impostato come identificatore poichè lo andremo ad utilizzare nel codice.
Torniamo quindi alla Document Class Esempio_Stardust.as
. Per prima cosa inseriamo i package da importare che verranno utilizzati nel nostro esempio; da notare che nell'utilizzo di classi e package personalizzati, come in questo caso, editor come FlashDevelop sono molto utili poichè durante la stesura del codice incorporano automaticamente le classi necessarie in base alle azioni che scriviamo, inoltre offrono il "code hinting" anche per le classi personalizzate; sotto questo aspetto Flash è invece più carente, queste due caratteristiche pare saranno implementate infatti solo in Flash CS5.
Per comodità ecco l'elenco delle classi da importare per questo esempio:
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.renderers.*; import idv.cjcat.stardust.twoD.zones.*;
A questo punto passiamo alle azioni che dovranno generare le particelle. Portiamoci all'interno della funzione Esempio_Stardust
. Per prima cosa dobbiamo creare un emitter, a cui associare un clock
dove specificare quante particelle generare ad ogni step. In questo caso creeremo una particella alla volta, in questo modo:
// creiamo l'emettitore, che creerà una particella alla volta var emettitore:Emitter2D = new Emitter2D(new SteadyClock(1));
Il valore 1
, passato al costruttore di SteadyClock
, non indica che sarà creata una sola particella in tutta l'animazione, ma che sarà generata una particella ad ogni step dell'emitter, in seguito associato all'evento ENTER_FRAME
perché si ripeta nell'esecuzione del filmato.
Creato l'emettitore dobbiamo creare un DisplayObject
che assoceremo al renderer: questo oggetto farà da "contenitore" per le nostre particelle. Useremo in questo caso un oggetto di tipo Sprite
che imposteremo come renderer, quindi assoceremo al renderer stesso il nostro emettitore, in questo modo:
// creiamo il contenitore per le particelle e lo associamo ad un renderer var sprite:Sprite = new Sprite(); addChild(sprite); var renderer:DisplayObjectRenderer = new DisplayObjectRenderer(sprite); renderer.addEmitter(emettitore);
A questo punto possiamo passare all'impostazione della particella: dobbiamo associare il MovieClip Fiocco
e inoltre dobbiamo impostare gli inizialiter, che ci permetteranno in questo caso di indicare a Stardust quale oggetto grafico usare come particella (Fiocco
appunto), in quale posizione crearla e quale movimento associarvi. Ecco il codice:
// impostiamo le proprietà della particella var displayObjectClass:DisplayObjectClass = new DisplayObjectClass(Fiocco); var position:Position = new Position(new Line(0, 0, 300, 0)); var velocity:Velocity = new Velocity(new SinglePoint(0, 5)); emettitore.addInitializer(displayObjectClass); emettitore.addInitializer(position); emettitore.addInitializer(velocity);
Come possiamo notare, anche in questo passaggio è pressoché totale l'uso delle classi di Stardust. La classe DisplayObjectClass permette di utilizzare come particella un oggetto di tipo DisplayObject (come è il nostro movieclip Fiocco
specificato come parametro), la posizione viene estratta casualmente su una linea che va da 0,0
a 0,300
mentre la velocità è un vettore con x=0
e y=5
, questo significa che la nostra particella si muoverà solo in verticale.
Queste tre proprietà vengono associate all'emettitore tramite il comando addInitializer.
Le azioni
Una volta creata la nostra particella e impostate le sue proprietà, non ci resta che indicare l'azione da compiere: infatti le proprietà di posizione e velocità per ora sono solo associate al fiocco di neve, ma non eseguono alcuna azione.
Stardust mette a disposizione diverse azioni, nel nostro caso useremo l'azione Move che permette di spostare una particella. Il movimento come già accennato sarà basato sul vettore velocity
impostato nel passaggio precedente, che comporta un movimento solo in verticale.
var move:Move = new Move(); emettitore.addAction(move);
Proviamo ora ad associare all'evento EnterFrame
del nostro stage l'esecuzione di uno step dell'emettitore, così:
addEventListener(Event.ENTER_FRAME, emettitore.step);
Testando il filmato dovremmo vedre i nostri fiocchi di neve scendere dal cielo. Potrebbe sembrare conclusa qui, ma dobbiamo ancora impostare un'ultima cosa: al momento infatti le particelle continuano ad essere perennemente allocate, anche quando escono fuori dal campo visivo dello stage, per cui dopo un po' il filmato comincerà a risentirne dato che saranno gestiti e presenti in memoria anche dei fiocchi di neve non più visibili a schermo.
Per eliminare gli oggetti non necessari e preservare così memoria e processore, Stardust mette a disposizione la classe DeathZone ("zona morta"), che permette di eliminare le particelle interne o esterne a tale zona.
var deathZone:DeathZone = new DeathZone(new RectZone(0, 0, 300, 300), true); emettitore.addAction(deathZone);
Nello specifico, la nostra DeathZone è rettangolare, inizia nel punto 0,0
ed è di dimensioni 300x300
pixel.
Il secondo parametro (inverted
), impostato a true
, indica all'azione che tutte le particelle "non" presenti nella DeathZone di 300x300
pixel vengano eliminate. Con il valore false
(impostazione di default) sarebbero eliminate tutte le particelle all'interno nella DeathZone.
Ora il nostro esempio è completo: i fiocchi di neve cadono correttamente e una volta usciti dalla zona in cui vogliamo siano visibili vengono eliminati, evitando così calcoli inutili al player.
Utilizzo di valori casuali
Nel nostro esempio i fiocchi di neve hanno una caduta verticale fissa (5
) e nessun movimento orizzontale (0
). Stardust mette a disposizione alcune classi per la generazione di valori casuali, ad esempio per le accelerazioni.
Proviamo anche questa feature aggiungendo un'accelerazione orizzontale casuale al nostro esempio: in questo modo i fiocchi non cadranno solo in verticale ma avranno un movimento più realistico. Il codice anche in questo caso è composto da poche righe poichè le operazioni sono gestite interamente da Stardust:
// aggiungiamo un'accelerazione orizzontale casuale var drift:RandomDrift = new RandomDrift(); drift.randomX = new UniformRandom(0.1, 0); emettitore.addAction(drift);
L'oggetto RandomDrift
viene creato e viene quindi generata la sua proprietà randomX
estrando un valore casuale tra -0.1
e 0.1
(abbiamo tre possibilità: -0.1
movimento orizzontale all'indietro, 0
nessun movimento orizzontale, 0.1
movimento orizzontale in avanti) e infine l'oggetto viene associato all'emettitore come azione.
Per molti elementi Stardust mette a disposizione sia la possibilità di generare valori fissi sia casuali, è consigliabile usare la documentazione come riferimento, inoltre per questo engine, ma come per tutte le classi personalizzate, è consigliabile affidarsi ad editor che offrano il code hinting anche per librerie esterne, per evitare errori di battitura e per avere a disposizione classi, metodi e parametri in un colpo d'occhio.