In questa lezione analizzeremo i sistemi particellari, ovvero i componenti utilizzati per rappresentare oggetti complessi e spesso amorfi (come liquidi, fiamme, fumo, o altre entità fluide o gassose) per cui le mesh 3D non sono adatte.
Per sistemi particellari intendiamo dei generatori che producono una quantità di particelle, le quali non sono veri e propri gameObject ma solo degli effetti grafici. Utilizzare delle entità semplificate fa sì che i sistemi particellari possano generare, gestire ed animare molte più particelle di quanto si potrebbe fare utilizzando un oggetto per ognuna di esse. Infatti, i sistemi particellari in Unity possono generare centinaia di particelle, muoverle nello spazio, cambiarne la dimensione e il colore, ed in più legare tutti questi valori al tempo dalla'nascita' della singola particella, o renderli casuali.
In genere, le particelle sono billboard, ovvero dei quad (fatti da due triangoli) che hanno una texture (spesso con canale alpha) e guardano sempre verso la telecamera. In realtà, è possibile anche orientarle in maniera diversa (rivolte verso l'alto, o in altri modi), ma difficilmente torneranno utili altri orientamenti. Oltre alle billboard, si possono usare anche mesh 3D come particelle… ma le performance diminuiranno drasticamente rispetto al primo metodo.
Il componente Particle System
Vediamo come creare un sistema particellare da zero: basterà usare GameObject > Create Other > Particle System
. Avremo un nuovo GameObject con un solo componente (oltre al Transform) che si occuperà della generazione, del rendering, e dell'animazione delle particelle.
Nota: Unity possiede al momento due tipi di sistemi particellari, uno molto vecchio (che rimane per retrocompatibilità, detto “legacy”) ed uno nuovo (il cui nome in codice è Shuriken). Il vecchio non viene quasi mai usato, né si trovano più risorse in giro, per cui non lo tratteremo. I vecchi componenti si trovano sotto Components > Effects > Legacy Particles
.
È importante ricordare che il nuovo sistema Shuriken risiede tutto in un solo componente (chiamato appunto Particle System) e che non bisogna mischiare componenti del sistema legacy con il componente di Shuriken.
Il sistema particellare creato apparirà come una sorta di fontana di palline bianche (la texture di default) e, se selezionato, sarà animato e mostrerà una finestrella nell'angolo in basso a destra nella Scene View:
Questa finestra fornisce un controllo sul sistema particellare, ma solo in fase di editing: permette di fermarlo, riavviarlo, o di farlo andare più o meno veloce. Ricordiamo però che non sono opzioni che andranno a finire nel gioco: servono solo a debuggare eventuali errori di impostazione nel sistema particellare.
Il componente Particle System appare così nell'Inspector:
È un componente più complesso degli altri, formato da tanti moduli che possono essere abilitati o spenti a seconda della necessità. Alcuni di questi però sono fondamentali, come:
- Emission, che regola quante particelle vengono emesse nel tempo;
- Shape, che indica la forma dell'emettitore);
- Renderer, che regola come le particelle vengono disegnate a schermo;
oltre che naturalmente al primo blocco che contiene le proprietà di base del sistema particellare.
Analizzando questo blocco per primo, soffermiamoci sul tempo: i sistemi particellari possono funzionare in maniera ciclica, o essere one-shot (ovvero essere riprodotti una volta sola, come le esplosioni). Chiaramente, questi valori (come anche altri) possono essere modificati via codice a runtime, ma per ora parleremo solo di quello che si può fare regolando il sistema particellare dall'Inspector.
Duration indica la durata di “un ciclo” di emissione (di default 5 secondi). Se Looping non è spuntato, dopo 5 secondi il sistema smetterà di emettere particelle. Se lo è, vedremo un flusso continuo ma ogni 5 secondi avverranno gli eventi iniziali (vedi dopo). Prewarm è molto importante: abilitandolo, il sistema particellare verrà simulato per qualche secondo prima che la scena venga avviata, così che all'avvio del gioco avremo già una serie di particelle in movimento. Questo è molto utile per elementi come cascate, fuochi o torce, dove è importante vedere l'effetto completo del sistema sin dal primo frame in cui l'elemento appare in scena - e non vogliamo vedere il sistema particellare'avviarsi'. Non è possibile attivare Prewarm se il sistema particellare non è di tipo loop.
Start Delay indica il ritardo nell'avvio, e non è abilitabile se Prewarm è attivo… ed è ovvio, perché Prewarm è come se fosse un delay negativo (ricordiamo che l'avvio dell sistema particellare viene anticipato, non posticipato)!
Play on Awake indica se il sistema viene messo in play automaticamente all'avvio del gioco. Attenzione: se non è spuntato, servirà uno script che lo metta in play con ParticleSystem.Play()
.
Animare le particelle
Oltre alle opzioni relative al sistema, esistono una serie di opzioni che caratterizzano le singole particelle. Start Delay, Speed, Lifetime, Size, Color e Rotation sono tutte indicative della condizione di partenza delle singole particelle.
C'è da dire però che assegnare lo stesso valore ad ogni particella produrrebbe risultati monotoni. è per questo che Unity mette a disposizione alcune possibilità alternative per aggiungere varietà: oltre a valori fissi (Constant) o totalmente random (Random Between two Constants), è possibile distribuire i valori lungo una curva (Curves) o casualmente fra due curve (Random Between two Curves). In questo caso ad esempio, stiamo editando il valore della curva per Start Rotation:
Quando modifichiamo una curva, in basso appare il Curve Editor. è un tool semplice, in cui una serie di punti di controllo definiscono il profilo di una curva, completi di'maniglie' che fanno da tangenti ai punti e che permettono di modificare i punti di ingresso e di uscita della curva (un po' come quando si disegna in vettoriale in programmi come Illustrator, Photoshop, Freehand, ecc.):
Per creare un nuovo punto di controllo basta fare doppio click sulla curva, per muoverlo basta trascinarlo, e per cancellarlo basta cliccare col destro sul punto e selezionare Delete Key.
Il Curve Editor è uno strumento che ricorre spesso in Unity, non solo nei sistemi particellari ma anche in altri componenti chiave, come durante la creazione di animazioni.
Come si interpreta una curva? Come si può vedere dall'immagine di cui sopra - relativa al parametro Start Rotation (ovvero la rotazione di partenza di ogni particella), la curva risiede su un grafico a due assi. Quello orizzontale è relativo al tempo durante il loop (controllato quindi tramite il parametro Duration, che qui vale 5 secondi), mentre quello verticale è il parametro stesso (in questo caso Start Rotation, che può valere da -180 a 180, anche se possiamo modificare il valore massimo).
Insomma, interpretando la curva di cui sopra, abbiamo in pratica detto ad Unity di non ruotare le particelle generate all'inizio (tempo 0) e, man mano che il tempo passa, di generarle ruotate sempre di più fino ad un valore di circa 150 gradi al secondo 3. Da qui in poi (cioè nella parte piatta della curva), tutte le particelle saranno ruotate di 150 gradi fino alla fine del loop, per poi ricominciare da 0 gradi.
Le curve sono uno strumento semplice ma potente che, se usato bene, può dare risultati sorprendenti e molto creativi.
Altre proprietà di base
Il modulo Emission regola la quantità di particelle emessa. Se è di tipo Time, il valore Rate indica quante particelle vengono emesse ogni secondo. Se è di tipo Distance, il sistema emetterà particelle solo quando si muove. In questo caso, Rate indica quante particelle vengono emesse quando il sistema particellare si muove di un unità. è evidente che se il sistema particellare non si muove, non verranno emesse particelle… il che lo rende adatto a tubi di scappamento, o scie di oggetti in movimento.
Quando l'emissione è di tipo Time, si rendono disponibili i Bursts, ovvero delle emissioni improvvise di più particelle alla volta. Premendo sul + si possono aggiungere'esplosioni', decidendo in che momento avvengono e quante particelle vengono emesse, fino ad un massimo di 4. Chiaramente i tempi si riferiscono al loop del sistema: un sistema con Duration 5 ed un solo Burst a 0 ad esempio, partirà con un esplosione di particelle, e la ripeterà ogni 5 secondi.
Da notare che i Bursts non devono per forza emettere centinaia di nuove particelle: si possono programmare 2-3 bursts con tempi non regolari e poche particelle, per rendere meno evidente il loop all'occhio umano e quindi più naturale il sistema particellare.
Altro modulo fondamentale, Shape indica la forma dell'emitter, ovvero l'area dove le particelle vengono generate e la direzioni in cui vengono sparate (che funziona in tandem con il parametro Start Speed). Senza andare nel dettaglio, esistono 4 forme di base regolabili con diversi parameteri, più la possibilità di usare una mesh come emettitore.
In quest'ultimo caso, si consiglia di non usare mesh eccessivamente complesse come numero di triangoli, e con una distribuzione regolare di questi: se così non fosse, si noterebbero più particelle generate dove la triangolazione è più fitta, e meno altrove.
Altri Moduli del Sistema Particellare
Sotto ai moduli di base si trovano una serie di moduli opzionali che di default sono disattivi, ma che possono essere abilitati per dare più vita al sistema tramite le spunte vicino al titolo di ognuno di essi. Molti si commentano da soli, ma vediamoli a grandi linee.
Alcuni moduli sono over Lifetime. Questo vuol dire che agiscono durante il corso della vita della particella, indipendentemente da quanto questa sia lunga (le particelle possono avere vita diversa se Start Lifetime non è di tipo Constant!).
Ad esempio, Color over Lifetime permette di definire un gradient, e la particella cambierà di colore partendo dal lato sinistro (alla sua nascita) fino ad arrivare a quello destro (al momento della distruzione).
Attenzione: come per Start Color, anche Color over Lifetime funziona solo se il materiale in uso per il sistema particellare è un tipo Particle. Questo perché queste proprietà non agiscono sul parametro _Color dello shader ma su quello _Tint, che è tipico degli shader della famiglia Particle.
Nell'editare il gradient si aprirà il Gradient Editor:
Il Gradient Editor presenta una fascia in alto, dove è possibile inserire una serie di marker. Nell'immagine in alto ne abbiamo 6: 3 in alto, che regolano la trasparenza del gradient, e tre in basso che ne regolano il colore. Come per le maniglie nelle curve, per aggiungere marker basta fare doppio click sul bordo alto o basso della fascia di colore, e trascinarli via (in alto o in basso) per rimuoverli.
Il gradient in figura ha una sfumatura che va dal nero passando per il rosso (quello selezionato, al 50%), fino al bianco. Come alpha invece, abbiamo messo 3 marker: gli estremi sono al 100% di opacità, mentre quello centrale è semitrasparente, come viene evidenziato dalla trama a scacchiera che compare nel mezzo. In questo caso quindi, la particella nasce nero pieno, dopo un po' diventa semitrasparente e rossa, per poi andare a morire bianca in piena opacità.
Possiamo definire quanti marker vogliamo, ma più ne avremo più sarà difficile regolare l'effetto finale. Di solito, basta avere un paio di marker ad opacità zero sugli estremi per avere un effetto di fade sulla nascita e sull'eliminazione delle particelle, per evitare una brusca apparizione o scomparsa delle stesse (utile per sistemi particellari come il fumo).
Altri moduli sono by Speed: come dice il nome, influenzano i valori di una particella in base alla sua velocità, la quale può dipendere non solo da Start Speed ma anche da moduli come Velocity over Lifetime o Force over Lifetime, che influenzano la velocità delle singole parrticelle.
È facile esagerare attivando tutti i moduli… ma gli effetti cumulativi sono spesso difficilmente decifrabili. Il consiglio è attivare e regolare un modulo per volta, prima di passare all'effetto finale.
Altri moduli specifici: Wind Zones e collisioni
Il modulo External Forces, se attivo, dice al sistema particellare di farsi influenzare dalle Wind Zones. Le Wind Zones sono gameObject che rappresentano dei campi di forza di due tipi: direzionale (utile per simulare il vento) o sferico (utile per creare esplosioni). Una Wind Zone direzionale appare così in scena:
Una volta create (GameObject > Create Other > Wind Zone
), basta regolarle tramite i parametri di forza, turbolenza e impulso per ottenere il risultato ottimale, e poi abilitare sul sistema particellare il modulo External Forces e decidere quanto (Multiplier) le particelle vengono influenzate da tutte le Wind Zone.
Il modulo Collision permette di far urtare le particelle contro due tipi di oggetti: dei piani “virtuali”, o il mondo di gioco. Nel primo caso, Planes (molto più leggero dal punto di vista computazionale) si possono creare gameObject vuoti mediante il tasto + oppure assegnarne di esistenti, e Unity farà collidere le particelle contro dei piani allineati con questi gameObject e orientati verso la Y up di questi.
Nel secondo caso World (più pesante da calcolare) le particelle urteranno contro qualunque Collider incontreranno. Va da sé che se è possibile approssimare l'urto con un piano (come nel caso di un pavimento), è inutile utilizzare la modalità World, sprecando risorse. In entrambi i casi si potranno regolare le proprietà dell'urto mediante le differenti proprietà di questo modulo.
Il modulo Sub Emitters permette di creare altri sistemi particellari in risposta ad alcuni eventi, come la nascita di una particella, una collisione, o la sua morte. I sistemi da creare possono essere in scena, o dei prefab nel Project. Questo modulo permette di creare effetti molto elaborati (come particelle che sono pezzetti di vetro e che si rompono in tante piccole scheggie quando urtano), ma è ovvio che il numero di particelle potrebbe diventare facilmente spropositato… da usare con cautela, soprattutto su piattaforme mobile!
Ancora, Texture Sheet Animation permette di creare particelle “animate a mano”, usando la texture della particella come un foglio di animazione su cui sono disegnati i vari frame. Nel fare ciò, si può regolare la dimensione di questi frame con Tiles e la loro durata con Frame over Time e Cycles. In più, scegliendo il valore Single Row nel parametro Animation ed abilitando Random Row, è possibile avere un foglio di animazione con più file di frame, con leggere variazioni, per avere dei sistemi particellari molto randomici e naturali.
Renderizzare le particelle
Infine, il modulo Renderer (già attivo di default) definisce come le particelle vengono renderizzate. Come già detto, di default le particelle sono billboard - ovvero guardano verso la camera. Ecco perché il parametro di Start Rotation non è un quaternione, ma un singolo valore: la rotazione delle particelle si intende sull'asse che va dalla particella alla camera, ovvero il suo asse Y locale (per cui non potremo mai vedere il retro di una particella, perché non può ruotare sul suo asse X).
Come tipo quindi possiamo usare Billboard, o dei tipi particolari di Billboard: Stretched deforma la particella in base alla sua velocità (per creare un “effetto raggi”), mentre nel caso di Horizontal e Vertical le particelle vengono orientate non verso la camera ma verso (rispettivamente) l'alto, e il davanti. Ad esempio nell'immagine sotto, nel caso di Horizontal Billboard le particelle ad un certo punto si vedranno di taglio:
In quest'altro esempio invece, usiamo la modalità Stretched Billboard:
Le particelle assumono la forma di raggi. Il problema che nasce qui è che visto che sono stirate in entrambi i sensi, quando nascono dal punto d'origine sono già lunghe e creano quell'effetto “mazzo di fiori” che si vede in basso. Per rimediare, potremmo ricorrere a diversi metodi: farle nascere invisibili mediante Color over Lifetime, modificare l'emitter in modo che nascano da un punto più in alto, ecc.
Non resta che decidere che materiale usare per il sistema particellare tramite Material, e regolarlo mediante Cast Shadows e Receive Shadows.
In ultimo, Max Particle Size serve, in caso di particelle troppo grandi appartenenti ad un sistema particellare troppo vicino alla camera, ad impedire che queste coprano completamente lo schermo: ad esempio, il valore iniziale (0.5
) indica che una particella non può mai essere più grande di metà dello schermo.