In questa lezione vediamo come stabilire l'ordine di rendering degli elementi grafici 2D in un progetto Unity, e come questi si relazionano agli oggetti 3D in scena.
Layer e SortingLayer
Proprio come nei programmi di disegno più diffusi, Unity possiede un sistema di livelli (layer) che ci permette di renderizzare forzatamente alcuni oggetti di fronte ad altri. Questo è molto utile soprattutto quando abbiamo situazioni come la GUI di un gioco, in cui una grafica deve apparire davanti a tutto anche se nello spazio 3D l'oggetto si trova dietro ad un altro.
Abbiamo già parlato dei Layer per il 3D nella lezione sui Raycast della guida di base di Unity. Brevemente, i Layer in Unity permettono di fare molte operazioni oltre al semplice ordinamento di oggetti: permettono di dividere gli oggetti in veri e propri gruppi, su cui (come nell'esempio prima) è possibile ignorare il Raycast, oppure fra i quali si possono disabilitare le collisioni.
Per il 2D, viene introdotto un nuovo concetto che un po' si sovrappone a quello di Layer: i SortingLayer ("livelli di ordinamento"). Diversamente dai Layer però, i SortingLayer non sono una proprietà del GameObject bensì dello Sprite Renderer (vedi lezione precedente). Difatti, un GameObject che sia uno Sprite 2D fa parte allo stesso tempo di un Layer, ma anche di un SortingLayer. Vediamo come.
Uno Sprite Renderer appena creato appartiene al SortingLayer "Default". Clicchiamo su di esso, e dal menu a tendina scegliamo Add Sorting Layer. L'Inspector mostrerà questo pannello:
Iltre ai Tag ed ai Layer di cui prima, abbiamo un manager dei SortingLayer. Premendo +
possiamo aggiungerne uno, chiamiamolo "Background
", ed aggiungiamone un altro chiamato "Character
".
I due SortingLayer sono pronti all'uso. Ora riprendiamo la tilemap della lezione precedente e creiamo una collinetta. Diamo ai tile in alto un Color grigio, in modo che sembrino in secondo piano. Mettiamo un personaggio in scena (va bene una grafica qualunque!), così:
Il personaggio si trova ora dietro la collinetta, ma vogliamo che venga disegnato davanti. Per ottenere questo effetto, è sufficiente assegnare al personaggio il SortingLayer "Character
" ed ai tre tile della collina (si può effettuare una selezione multipla dalla Hierarchy) il SortingLayer "Background
".
Infine, dobbiamo tornare al Layer Manager e cambiare l'ordine dei layer, perché Unity - stranamente - funziona al contrario rispetto ai software di grafica: un layer più in alto nella lista viene disegnato dietro!
In scena vedremo finalmente l'effetto voluto:
Notiamo che oltre all'organizzazione in SortingLayer, Unity supporta anche l'ordinamento all'interno del singolo layer. Nel componente Sprite Renderer infatti, possiamo decidere il parametro Order in Layer. Questi numeri funzionano come ci si aspetterebbe: più il numero è alto, più lo Sprite viene renderizzato davanti agli altri. Difatti, avremmo potuto ottenere l'effetto voluto anche usando solo questo numero: bastava assegnare 1 al personaggio, e per i tile del terreno lasciare lo 0.
L'organizzazione con i SortingLayer però è più pulita e lascia meno spazio all'errore, soprattutto in caso di modifiche successive. Ad esempio, per inserire una decorazione fra il personaggio e lo sfondo basterà semplicemente metterla su un nuovo SortingLayer e poi riordinarli nel Layer Manager.
Nota: Due Sprite che hanno lo stesso SortingLayer e lo stesso Order in Layer saranno ordinati secondo criteri su cui non abbiamo il controllo (casuale? a seconda della distanza dalla camera?), quindi meglio evitare questa situazione. Questa situazione va bene però quando i due Sprite non si sovrapporranno mai (come nel nostro caso i tile, e più in generale elementi statici di un background o di un terreno), in questo caso è perfettamente normale che gli Sprite condividano lo stesso Order in Layer.
Interazione fra 2D e 3D
Come interagiscono Sprite ed i normali GameObject 3D che abbiamo visto fin'ora a livello di rendering? Semplice, rispettano l'ordine nella scena 3D. Ecco un rapido esempio:
Abbiamo inserito nella scena una capsula rossa ed un piano con un Toon Shader. Come si può vedere, il piano interseca lo scenario 2D proprio come ci si aspetterebbe, e lo stesso vale per la capsula (che viene tagliata).
Chiaramente è stato necessario inserire una luce per illuminare questi oggetti 3D, e come si può notare la capsula proietta un'ombra che viene ricevuta dal piano, ma non dallo scenario 2D. È possibile far sì che Sprite ricevano le ombre, ma per fare ciò sarà necessario scrivere uno Shader apposito.
Comporre una scena complessa
Come si relazionano quindi i SortingLayer con i Layer? Semplice: le due cose interagiscono e si sommano fra loro, con i Layer che possono funzionare da "macro gruppi", ed i SortingLayer che definiscono l'ordine all'interno di una singola Camera di rendering.
Ad esempio, supponiamo di avere due Layer (UI
, già presente di default, e GameScene
) e due SortingLayer (Characters
e Background
).
Possiamo ordinare la nostra scena così: creiamo due oggetti Camera, uno lo useremo per la GUI (layer: UI) ed uno per il gameplay (layer: GameScene). Nei componenti Camera dovremo selezionare per ognuno nella Culling Mask di renderizzare il rispettivo Layer, e dare alla Depth il giusto valore (0 per la Camera di gameplay, 1 per quella della GUI).
Possiamo quindi inserire i GameObject della GUI in scena, assegnandogli il Layer UI. Infine, possiamo comporre la nostra scena come abbiamo fatto fin'ora, dividendo gli Sprite nei due SortingLayer Background e Characters, ricordando di assegnare a questi il Layer GameScene.
Il risultato è che gli oggetti 2D verranno renderizzati tutti da una Camera, ed ordinati fra loro secondo i SortingLayer (e poi secondo l'Order in Layer, se l'abbiamo assegnato). Su tutto poi verrà renderizzata la GUI per effetto della seconda Camera, come stabilito dalle Culling Mask e dalle Depth.
In realtà, se la GUI fosse totalmente in 2D (ovvero con soli Sprite) si potrebbe pensare a scartare i Layer del tutto ed utilizzare una sola Camera, creando semplicemente un SortingLayer per la GUI. Potrebbero però nascere successivamente problemi con l'introduzione del testo, quindi in generale è meglio tenere la GUI su una Camera separata.