Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Prefab, istanziare molti oggetti a runtime

Istanziare nuovi oggetti da codice, sia utilizzando le variabili public, sia acquisendo gli oggetti da file system con la cartella Resources.
Istanziare nuovi oggetti da codice, sia utilizzando le variabili public, sia acquisendo gli oggetti da file system con la cartella Resources.
Link copiato negli appunti

Dopo aver visto come aggiungere e rimuovere componenti a runtime, in questa lezione vedremo come fare per creare e distruggere interi oggetti.

La cartella Resources

Come per i componenti, in Unity si può decidere di creare oggetti nell'editor (trascinandoli dal pannello project nella scena, o in Hierarchy), oppure di farlo in programmazione. Tante volte questa risulta l'unica opzione, come nel caso di proiettili, esplosioni, ed in generale di tutti quegli oggetti che vengono creati in maniera ripetuta.

Per istanziare un oggetto, la prima cosa da fare è crearlo come prefab.

Definire un oggetto come prefab

È molto importante capire questo meccanismo ma c'è anche una maniera più diretta di istanziare molti oggetti, come nemici o proiettili, senza ricorrere al meccanismo della cartella Resources:

1. Si dichiara una variabile public di tipo GameObject (es. public GameObject prefab;) all'interno dello script che utilizziamo come controller (nella Main Camera ad esempio):

public class controller : MonoBehaviour {
	public GameObject prefab;
// ...

2. Vi si correla da pannello Inspector il prefab che vogliamo replicare:

3. Poi da codice (es. nella funzione Start) possiamo istanziare l'oggetto, come vedremo anche più avanti:

public class controller : MonoBehaviour {
	public GameObject prefab;
	// Use this for initialization
	void Start () {
		newCube = (GameObject) Instantiate(prefab);
// ...

Troviamo un esempio in questo articolo.

L'uso di Resources

In questa lezione vogliamo poter definire dinamicamente un oggetto come prefab, per questo avremo bisogno di inserire i nostri prefab all'interno della cartella Resources(o in una sua sottocartella). Si tratta di una cartella speciale in cui Unity, grazie all'oggetto Resources, può andare alla ricerca di oggetti da trattare come prefab a runtime.

Questo metodo può essere vantaggioso se vogliamo controllare meglio l'allocazione e il rilascio della memoria, quindi vediamo come si procede a definire un prefab direttamente da codice con questa tecnica.

Creare un prefab

Creiamo un semplicissimo prefab partendo da un cubo. Andiamo su Game Object > Create Other > Cube. Una volta in scena, aggiungiamo un componente Rigidbody. Nel pannello Project, creiamo una nuova cartella di nome Resources (attenti a dare questo nome esatto).

Ora trasciniamo il cubo dalla Hierarchy al pannello Project per creare un prefab. Cancelliamo l'oggetto in scena, e rinominiamo il prefab in SimpleCube.

Ora creiamo un nuovo script (con un qualunque nome), ed assegnamolo come componente ad un qualunque oggetto presente in scena (magari alla Camera di default) in modo che venga eseguito. Nello script scriveremo, dentro il metodo Start:

GameObject prefab = Resources.Load<GameObject>("SimpleCube");

Ciò che abbiamo appena fatto è di caricare un prefab di nome SimpleCube dalla cartella Resources, mediante la funzione statica Resources.Load.

Questa funzione accetta un parametro, ovvero il path del prefab da caricare. In questo caso, poiché SimpleCube non è in una sottocartella, possiamo passare semplicemente il suo nome.

Inoltre, Resources.Load è una funzione template, quindi possiamo passare un tipo in modo che lo script sappia già di che classe è l'asset caricato (in questo caso GameObject, ma poteva essere Texture2D, Material, AudioClip, etc.).

Per sapere di più sull'uso di Resources ci si può riferire alle references ufficiali

Istanziare di oggetti, la funzione Instantiate

Una volta caricato, possiamo istanziare il prefab. Utilizziamo la funzione Instantiate, che è una funzione statica della classe Object e che crea una nuova istanza in scena a partire da un prefab che gli passiamo (nel nostro caso, il GameObject caricato prima).

GameObject prefab = Resources.Load<GameObject>("SimpleCube");
GameObject newCube = (GameObject) Instantiate(prefab);

All'esecuzione di queste due righe, vedremo comparire in scena un oggetto cubo, nella stessa posizione in cui era quello da cui abbiamo fatto il prefab. Inoltre, poiché come il prefab ha un Rigidbody attaccato, lo vedremo cadere secondo la fisica.

Come è possibile notare, Instantiate non è una funzione template, quindi è necessario eseguire un cast (ovvero quello che facciamo con (GameObject)).

È logico che su questo nuovo gameObject appena creato, potremo operare come abbiamo fatto fin'ora: ad esempio, se vogliamo operare sui suoi componenti rigidbody ed un altro script creato da noi (che qui chiamiamo OtherComponent), potremo normalmente fare qualcosa del genere:

newCube.rigidbody.mass = 10f;
newCube.GetComponent<OtherComponent>().enabled = false;

Non ci preoccupiamo per ora di posizionare il nuovo oggetto nello spazio 3D, lo vedremo nella prossima lezione.

Trovare un oggetto nella scena

Prima di vedere come rimuovere un oggetto, dobbiamo avere il modo di trovarlo in scena. Per cercare un oggetto presente in scena a partire da un altro script, possiamo usare diversi metodi. Il più banale è usare la funzione statica GameObject.Find:

GameObject otherCube = (GameObject) GameObject.Find("newCube");

Come si può notare, la funzione Find accetta un parametro di tipo string che è il nome dell'oggetto da cercare. Ovviamente, in caso ci fosse più di un oggetto in scena con lo stesso nome, Unity non avrebbe modo di distinguere fra i due, quindi passerebbe un riferimento al primo trovato.

Inoltre, GameObject.Find è una funzione estremamente lenta: è consigliabile infatti non utilizzarla nell'Update perché per come è strutturata, deve scorrere tutta la gerarchia della scena e leggere i nomi di tutti i GameObject incontrati.

Nel caso in cui un riferimento ad un oggetto serva ad ogni frame, è consigliabile utilizzare GameObject.Find solo una volta nello Start e salvare un riferimento da usare nell'Update. Ad esempio:

private GameObject otherObject;
void Start () {
	otherObject = (GameObject)GameObject.Find("AnotherCube");
}
void Update () {
	otherObject.GetComponent<OtherComponent>().counter++;
}

In questo caso, per accedere al componente OtherComponent abbiamo salvato un riferimento all'altro oggetto in una variabile privata di nome otherObject.

Rimuovere gli oggetti

In ultimo, è possibile rimuovere oggetti dalla scena utilizzando la funzione statica Destroy (l'abbiamo usata nella lezione precedente per eliminare componenti). Ad esempio:

otherObject = (GameObject) GameObject.Find("AnotherCube");
Destroy(otherObject);

In questo caso, viene cercato un oggetto a partire dal suo nome e poi viene rimosso dalla scena, e con esso tutti i suoi eventuali oggetti figli e tutti i loro componenti.

Attenzione però: Destroy non ha effetto istantaneo. La rimozione dell'oggetto viene messa in programma, e portata a termine solo alla fine del frame corrente. Questo perché una rimozione immediata porterebbe a problemi in diversi casi, come ad esempio nella risoluzione della fisica.

Esiste anche la versione immediata di Destroy, di nome DestroyImmediate, ma va usata solo quando si sta programmando un plugin per Unity da utilizzare quando il gioco non è in Play. Usare DestroyImmediate mentre in un normale flusso di gioco può solo dare problemi, e può portare alla distruzione permanente di asset (ovvero gli oggetti vengono rimossi dal pannello Project, e dalle cartelle in cui si trovano!).

Ti consigliamo anche