I prefab possono contenere molto più che un singolo gameObject. È possibile infatti inserire più di un oggetto dentro un altro, e poi creare un prefab a partire dal parent. In questo modo, il prefab salva la gerarchia degli oggetti, che poi possono essere utilizzati tutti insieme.
Nel caso di prefab che contengono più oggetti, è importante capire dove si trova il pivot dell'oggetto parent, per non avere sorprese successivamente.
In una scena vuota, create una capsula (GameObject > Create Other > Capsule
), chiamatela Body
, e posizionatela a (0, 0, 0)
. Create una sfera, rinominatela in Head
, e tiratela su come se fosse la testa di un omino a (0, 1.5, 0)
.
Ora create un gameObject vuoto (GameObject > Create Empty
), chiamatelo Human
e posizionatelo in (1, 0, 0)
. Per farla diventare la base del nostro prefab (chiamata root
), inserite i due oggetti creati in precedenza all'interno di Human
. La struttura dovrebbe presentarsi così:
Notate che, per avere il gizmo in quella posizione, bisogna che Pivot sia selezionato in alto.
Create un prefab trascinando Human
nel Project. Come potete vedere, il prefab creato mostra una piccola anteprima di tutti i suoi contenuti, ed un triangolino per espandere l'icona e vederli uno ad uno (se non la vedete usate lo slider in basso a destra nel pannello Project per controllare la dimensione delle icone):
Come dicevamo, è importante in fase di creazione di un prefab decidere dove mettere i pivot. In questo caso, se questo prefab fosse un personaggio umano, il pivot sarebbe in una posizione molto scomoda, perché Body
e Head
sono decentrati rispetto ad esso: provate a selezionarli nella Hierarchy, e noterete che hanno rispettivamente come coordinate relative (-1, 0, 0)
e (-1, 1.5, 0)
. Questo creerebbe delle problematiche quando il personaggio si gira, perché ruoterebbe secondo un centro che non è posizionato al centro dei suoi "piedi".
Per correggere questo difetto, riposizioniamo gli oggetti interni al prefab a partire dalla sua istanza nella scena. Selezionate Head
e Body
e portateli rispettivamente a (0, 2.5, 0)
e (0, 1, 0)
. Selezionate Human
, e vedrete che ora il pivot del padre è al centro in basso del personaggio, dove si trovano i suoi piedi.
Per completare l'operazione, premete Apply
nell'Inspector. Il prefab è ora stato modificato, e tutte le nuove istanze avranno un pivot in basso al centro.
Nota: è consigliabile che la root di un prefab sia sempre un gameObject vuoto. In questo modo, per disabilitare interamente uno dei suoi oggetti (dall'editor ma anche da codice) basterà disabilitare l'oggetto figlio.
Si immagini ad esempio un prefab di una casa, in cui l'oggetto root contenga le mura ed il tetto, e i figli siano porte e finestre. Per rimuovere le mura ed il tetto temporaneamente, bisognerebbe creare uno script che cicla tutti i componenti dell'oggetto root (Renderer, Collider vari, ecc.), perché disabilitandolo verrebbero disabilitati anche i figli. Invece se l'oggetto root fosse un gameObject vuoto e mura e tetto fossero un semplice gameObject figlio, basterebbe disabilitare quello per ottenere l'effetto voluto.
Aggiunta di GameObject in un Prefab
Vediamo come Unity risponde all'aggiunta e alla rimozione di gameObject all'interno di un'istanza di un prefab.
Duplicate l'intero omino creato in precedenza, e rinominate le due istanze Human1
e Human2
. Ingrandite il Body
di Human1
con scalatura (2, 1, 2)
, in modo da renderlo grasso:
Se premessimo Apply nel pannello prefab di Human1
, vedremmo entrambi gli omini diventare "grassi". Per questo motivo, lavoreremo su Human2
che ora è l'istanza più "pulita" (ovvero non ha modifiche rispetto al prefab).
Create una nuova sfera, chiamatela Hand
, impostate la scalatura a (0.5, 0.5, 0.5)
, spostatela all'interno di Human2
, e posizionatela in (0, 1, -0.75)
. Dovreste avere una situazione simile:
Come si può notare, Hand
è scritta in nero nel pannello Hierarchy, a simboleggiare che non fa parte del prefab. Premete Apply
nell'Inspector, e vedrete tre modifiche: il nome Hand
diventa anch'esso blu, appare Hand anche in Human1
come purenel pannello Project dentro al prefab Human
.
A questo punto è possibile regolare la posizione della mano in Human1
, perché essendo il suo Body
più largo nasconde la mano. Basterà posizionarla in (0, 1, -1.25)
per ottenere un risultato simile a Human2
(non premete Apply o questa modifica verrà propagata anche al prefab originale!).
Così facendo però, ci stiamo pericolosamente allontanando dal concetto di prefab: abbiamo in scena due istanze di un prefab, ma una di esse contiene già due modifiche custom. In una situazione di lavoro, bisognerebbe stare attenti a non premere Apply su questa, compromettendo così il prefab originale.
Rimozione di GameObject da un Prefab
Rimuoviamo ora le mani dagli omini. Selezionate Hand
di Human2
, ed andate su Edit > Delete
(o con il tasto destro dalla Hierarchy, o con una shortcut...).
Unity vi presenta un avviso:
Premete Continue. Come si può vedere, Human2
ha perso la mano, ma ora non è più legato al prefab originale, ed infatti tutti i nomi degli oggetti che lo compongono (Human2, Body, Head
) sono in nero e non in blu nella Hierarchy. Qualunque modifica faremo al prefab d'ora in poi, non sarà più riportata in quest'istanza.
Per ricollegarlo al prefab, basterà premere Revert
o Apply
nell'Inspector.
- Revert rende l'istanza come il prefab, quindi
Human2
recupererà la sua mano. - Apply rende tutte le istanze ed il prefab stesso come
Human2,
quindi come risultato tutti perderanno la mano.
Cancellare o scollegare un Prefab dalle sue Istanze
Ci sono due modi di scollegare un prefab dalle istanze, rendendole così indipendenti.
Selezionate Human1
. Per renderlo un gameObject indipendente e non collegato ad alcun prefab, è sufficiente selezionare GameObject > Break Prefab Instance
. Il suo nome in Hierarchy diventa nero, ed ora tutte le modifiche che gli faremo non verranno applicate alle altre istanze né al prefab.
La stessa cosa succederebbe se cancellassimo (per sbaglio o intenzionalmente) un prefab. Cancellate Human
dal Project.
Human1
, l'istanza rimasta in scena, ora ha un nome in rosso ad indicare che il suo prefab è mancante. In pratica si può considerarlo un oggetto indipendente, quindi si può selezionare come prima dal menu GameObject > Break Prefab Instance
per renderlo davvero indipendente e rimettere il nome in nero.
La perdita di riferimenti a seguito della cancellazione di un prefab è una modifica che non si può annullare, quindi bisogna fare molta attenzione nel cancellare prefab dal progetto, perché questo porta alla perdita di tutti i riferimenti al prefab in tutte le scene.
Prefab innestati
Nella versione di Unity attuale (4.2) è ancora impossibile creare un prefab dentro un altro prefab (ovvero i cosiddetti nested Prefabs
, una caratteristica richiesta a gran voce da molti sviluppatori) senza perdere il riferimento al prefab padre.
Per spiegarlo meglio, facciamo un esempio pratico: immaginate di creare un prefab che è un edificio, completo di tanti gameObject interni (mura dell'edificio, una tenda da sole, una bandiera sul tetto, ecc.), chiamiato Building1
. Immaginate di crearne altri due diversi, Building2
, Building3
.
Ora, volete creare un prefab per un isolato, in modo da creare un blocco di costruzione per un'intera città. Inserite Building1, Building2
e Building3
in un gameObject vuoto chiamato Block
, e fin qui tutto ok: le istanze rimangono collegate ai 3 prefab in libreria.
Se però trascinate questo gameObject nel pannello Project e ne fate un prefab, verrà creato un nuovo prefab Block
ma le 3 istanze di Building1, Building2
e Building3
perderanno il collegamento ai rispettivi prefab, diventando parte di Block
. Questo vuol dire che qualunque modifica sui 3 prefab non si rispecchierà nelle copie contenute in Block
, che ormai è un prefab separato ed indipendente.
In realtà, questo comportamento si può evitare con l'uso di diversi plugin scaricabili dall'Asset Store (alcuni dei quali a pagamento), ma spesso il processo è comunque macchinoso perché richiedono delle azioni da compiere ogni volta che si vuole creare un prefab innestato.