Imparare UnityPrincipianti

I Prefabs

I prefabs (abbreviazione che sta per ‘prefabbricati‘) sono dei gameObject prefabbricati .
Essi sono dei gameObjects “salvati” all’interno delle cartelle del progetto.
I prefabs mantengono tutte le caratteristiche di un gameObject, scripts, modelli, materiali, valori degli scripts ecc… e si potranno inserire nella scena a nostro piacere, sia da codice che trascinandoli dalla cartella in cui si trovano (nel pannello Project) fin dentro alla Hierarchy o direttamente nel pannello della scena.

A cosa possono serivere i Prefabs?

Mettiamo per esempio di  voler far sparare il giocatore.
Dovremmo “istanziare” l’oggetto “proiettile” che vorremmo far apparire nel punto di fuoco del giocatore.
Istanziare significa “creare una copia dell’originale” all’interno della nostra scena, tramite codice.
Nel momento dello sparo creeremo dunque un’istanza dell’oggetto “proiettile”.
L’oggetto proiettile è il nostro oggetto prefabbricato, un prefab.

Per creare un prefab basta trascinare un qualunque gameObject dalla nostra Hierarchy fin dentro una cartella del progetto (Project).

I prefabs vengono quindi creati (prefabbricati) all’interno di Unity e poi potranno essere istanziati anche più e più volte nel momento del bisogno. Sono essenzialmente dei gameObject che vengono salvati nella cartella del progetto per poi essere riutilizzati in diverse situazioni anche semplicemente trascinandoli nella scena, oppure, come nel caso dei proiettili, tramite codice.

I prefabs sono utili ogni volta che un oggetto deve comparire più di una volta, e deve avere caratteristiche uguali o simili a quella di un altro, ma non per questo sempre identiche. Ogni volta che un prefab viene messo in scena, si dice che quella è un’istanza (una specie di clone) del prefab stesso.

Modificando un prefab padre che abbiamo già all’interno del pannello Project, tali modifiche si ripercuoteranno su ogni istanza nella scena.
In questo modo, se nella scena o in più scene avessimo tanti oggetti che sono istanze di un prefab e volessimo apportare delle modifiche a tutti loro, non dovremmo andare andare a cercare ogni singolo oggetto nelle scene per fare le stesse modifiche su ognuno di essi mabasterà apportare le modifiche al prefab padre nella cartella dove l’abbiamo creato, nel pannello Project.

Ovviamente gli unici parametri che non saranno mai in comune tra le istanze e non verranno ereditati saranno le coordinate del transform perché le istanze saranno sempre posizionate in punti diversi delle scene.

Quando nella scena selezioneremo un gameObject che è un’istanza di un prefab, vedremo che apparirà questo piccolo menu aggiuntivo nell’inspector, con tre tasti.

  • Select seleziona il prefab padre nel pannello Project mostrandoci quindi di quale prefab è quell’istanza.
  • Revert fa sì che l’istanza selezionata diventi di nuovo come il prefab padre in tutto e per tutto, perdendo tutte le modifiche locali a qualunque proprietà.
  • Apply serve a modificare il prefab base sulla base delle modifiche effettuate a quella specifica istanza.

 

Se per esempio avete un gioco con un determinato tipo di nemico che si dovrà ripetere nella scena o anche in altre scene, potreste creare un prefab di esso ed utilizzare le sue istanze semplicemente trascinandole all’interno delle scene.
Se poi vorrete cambiare solo un particolare di un’istanza, potrete semplicemente farlo modificando il gameObject nella scena.
Quando modificare una caratteristica ad un’istanza nella scena, quella caratteristica verrà posta in grassetto per evidenziare che è stata modificata rispetto al suo valore di base presente sul prefab da cui proviene.
A quel punto però, state attenti a non premere “Apply” perché se lo farete, le modifiche effettuate su quell’istanza saranno applicate anche al prefab padre nella cartella del progetto.

Essendo un prefab un gameObject a tutti gli effetti, essi potranno essere composti da scripts e componenti di ogni genere e potranno avere una loro gerarchia, con childrens e quant’altro si può avere su un normale gameObject.

Cosa succede se si cancella un prefab dalla cartella del Project? Tutte le istanze verranno cancellate?
La risposta è no, non succede nulla di particolare. Le vostre istanze non verranno ne cancellate ne modificate. Semplicemente, appariranno in rosso sulla Hierarchy perché verrà a mancare il collegamento con il prefab.
Ma quel punto le istanze non saranno più collegate tra loro e saranno come dei normali gameObjects nella scena. Sul piccolo menu, al posto dei pulsanti apparirà la scritta in giallo Missing.
Per scollegare completamente un’istanza dal suo prefab, dovrete andare nel menu GameObject e scegliere Break Prefab Instance.

Istanziare un prefab da codice

Proviamo a creare un metodo che istanzi un prefab nel momento in cui il giocatore preme un tasto. Un classico esempio di come emettere un proiettile dal giocatore (o volendo anche da un nemico).

Per creare un’istanza si usa il metodo Instantiate che appartiene ad ogni oggetto ed ha molte variabili a seconda di quali parametri vogliamo impostare nel momento della creazione dell’istanza.

using UnityEngine;

public class Player : MonoBehaviour {

    public GameObject PrefabOriginale;




	void Spara () {
        //Istanzia l'oggetto proiettile, creando una copia del PrefabOriginale
        GameObject proiettile = GameObject.Instantiate(PrefabOriginale);
	}


    void Update()
    {
        // Alla pressione di Fire1, esegui il metodo Spara()
        if (Input.GetButtonDown("Fire1"))
        {
            Spara();
        }




    }



}

 

In questo modo, ad ogni pressione del tasto Fire1 (che di default è il tasto Ctrl della tastiera) verrà creato un nuovo oggetto, istanza del prefab che abbiamo scelto.

La funzione
if(Input.GetButtonDown(“Fire1”))
controlla se è premuto il tasto Fire1.

In questo modo, ad ogni pressione del tasto Fire1 (che di default è il tasto Ctrl della tastiera) verrà creato un nuovo oggetto, istanza del prefab che abbiamo scelto.

La riga
GameObject.Istantiate(PrefabOriginale);
è però ancora molto vaga. Non abbiamo specificato il punto in cui vorremo che venga creata l’istanza, la sua rotazione e altri parametri che potremmo specificare usando le diverse versioni del metodo Instantiate().

using UnityEngine;

public class Player : MonoBehaviour {

    public GameObject PrefabOriginale;




	void Spara () {
        //Istanzia l'oggetto proiettile, creando una copia del PrefabOriginale impostando la posizione e la rotazione
        GameObject proiettile = GameObject.Instantiate(PrefabOriginale,transform.position,Quaternion.identity);
	}


    void Update()
    {
        // Alla pressione di Fire1, esegui il metodo Spara()
        if (Input.GetButtonDown("Fire1"))
        {
            Spara();
        }




    }



}

 

Ora abbiamo aggiunto sia la posizione che la rotazione iniziale che l’istanza dovrà avere nel momento in cui verrà creata.

GameObject.Instantiate(PrefabOriginale,transform.position,Quaternion.identity);

Con transform.position gli abbiamo detto di creare l’istanza nel punto di posizione dove si trova il trasform su cui è questo script, ovvero sul punto di posizione del Player.
Con Quaternion.identity gli abbiamo detto di mantenere la rotazione di default che possiede il prefab padre del proiettile.

In questo modo avremo i proiettili creati sempre nella posizione del Player. Basterà dunque assegnare al prefab del proiettile uno script che lo faccia muovere in avanti sin dal momento della sua creazione.

 

NOTA:
Forse avrete notato la presenza di due funzioni dal nome molto simile per rilevare la pressione di un tasto:
GetButton e GetButtonDown

Sappiate che a parola Down identifica quelle funzioni che necessitano di rilevare solo la prima pressione di un tasto/comando e vengono eseguite una volta sola, nel momento in cui si preme il tasto. Anche tenendo il tasto premuto essa non verrà più eseguita fino a quando il tasto non sarà rilasciato lo si premerà di nuovo.

Così come la parole Up identifica quelle funzioni che intercettano solo il frame in cui viene rilasciato un tasto.

Vedremo in un altro articolo la funzionalità di questi metodi, in particolare vedremo come far sparare la nostra arma (istanziare un proiettile) ogni tot secondi, anche mantenendo un tasto premuto.

Conclusioni finali

Ovviamente questo script è ben lontano dall’essere un sistema completo e funzionale per l’azione di fuoco del giocatore. Dovremmo impostare il punto esatto di fuoco rispetto al player (praticamente il punto sull’arma che spara), la sua direzione, in alcuni casi anche la velocità del proiettile in base alla velocità del giocatore…
In questa lezione abbiamo solo visto una delle tante funzionalità dei prefabs gestiti da codice.
Inoltre, vedremo in seguito che esiste un modo più performante (a livello di performance hardware) di far “sparare” il giocatore o i nemici, perché istanziare e distruggere gli oggetti sono azioni che necessitano di risorse che potrebbero concepire un calo di framerate se effettuate su molti oggetti a run-time, sopratutto necessitano di un uso di memoria che potremmo evitare usando un sistema di Pooling ovvero non distruggendo gli oggetti per poi farli ricreare, ma riutilizzando sempre gli stessi.

9 pensieri su “I Prefabs

  1. Grazie per il tutorial, io sono alle prime armi con gli scripts mi sto esercitando in cose semplici per capire il significato dei termini negli scripts e questo tutorial è proprio utile in tal senso anche se mi sono incartato lo stesso 🙂 Lo script va assegnato all’arma e deve essere presente il FPC immagino, perchè a me clona nella hierarchy ma non vedo fisicamente il proiettile. Sia I’arma(in questo caso un cannone) che la main camera sono all’interno nel FPC nella hierarchy.

    1. Ciao Albert,
      si, lo script dovrebbe essere posto sull’arma, ma può essere posto anche sul player o altrove. Per comodità lo si mette sull’arma.
      Quando un oggetto (in questo caso il proiettile) viene istanziato, esso appare anche nella hierarchy. Se appare nella hierarchy puoi star sicuro che si trova nella scena. Bisogna vedere, dove, di che dimensioni (potrebbe essere così minuscolo che non lo vedi) e se si trova “sotto” ad un oggetto padre che lo copre.
      Se vedi l’oggetto nella hierarchy ti basta cliccare due volte sul suo nome in hierarchy per far si che ti venga evidenziato anche nella scena.
      Un proiettile dovrà essere sempre “libero” ovvero non dovrà trovarsi sotto a nessun oggetto genitore, (almeno non alla telecamera o il player) altrimenti non sarà libero di proseguire la sua traiettoria come dovrebbe.
      Per essere sicuro che il proiettile non si trovi “sotto” nessun ‘altro gameObject puoi usare la funzione che “slega” un oggetto da qualsiasi parent:
      mioProiettile.SetParent(null);
      dove mioProiettile è il gameObject del proiettile.

  2. Ottimo tutorial ,
    volevo farti una domanda a proposito dei cloni creati.
    Quando premo il tasto mi crea almeno 20 cloni (penso sia dovuto al numero di volte che viene eseguito l’update in un secondo) , come posso limitare la creazione dei cloni , tipo un clone al secondo ?
    Ancora grazie 😀

    1. Cioa Mimmo,
      strano che ti instanzi tanti prefabs, con GetButtonDown dovrebbe farlo una sola volta alla pressione del tasto perchè GetButtonDown viene eseguita unicamente nel frame in cui avviene la pressione del tasto, poi mai più finchè non viene rilasciato e premuto di nuovo il tasto.
      A meno che tu non abbia usato GetButton invece che GetButtonDown.

      Come avrai notato ci sono molte funzioni che rilevano la pressione di un tasto che si differenziano dalla parola “Down”, ovvero ci sono due versioni della stessa funzione che dal nome si assomigliano molto, tipo GetButton e GetButtonDown .
      “Down” identifica il tipo di funzionamento ad un solo frame (GetButtonDown ,GetKeyDown…) mentre le funzioni dove manca la parola “Down”, (GetButton, GetKey…) eseguno ripetutamente la funzione per tutto il tempo in cui si tiene premuto il tasto (ad ogni frame).

      A parte questa precisazione, per fare in modo che lo sparo sia cadenziato secondo un tempo stabilito (il classico fire-rate di un’arma) bisogna usare GetButton ma creare anche una funzione apposita che non esegua la funzione ad ogni ciclo di Update (cioè ogni frame) ma attenda un tempo stabilito (un secondo o comunque un breve lasso di tempo) prima di eseguire di nuovo la funzione, anche se il tasto è rimasto premuto per tutto il tempo.

      Visto che hai sollevato la questione, vado a scrivere un articolo sull’argomento. 😉

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *