Imparare C#Imparare UnityPrincipianti

Muovere un gameObject da script

La posizione degli oggetti gestita da codice
(senza l’ausilio di un RigidBody)

Facciamo muovere un oggetto presente nella scena di Unity semplicemente gestendo la sua posizione nella scena, ovvero modificando le sue coordinate di posizione direttamente da script.

Poniamo di avere una serie di gameObject in una scena. Cubi, cilindri, sfere ecc..
Creiamo uno script (NuovoScript.cs) ed applichiamolo ad uno solo dei gameObject nella scena, nel nostro esempio, alla sfera. Modifichiamo lo script con questo semplice codice:

using UnityEngine;

public class NuovoScript : MonoBehaviour
{
    public Vector3 movimento;

    void Start()
    {
    
    }

     void Update()
    {
        movimento.x+=0.1f;

        transform.position = movimento;
    }

}

Alla riga 5 abbiamo dichiarato un Vector3 chiamato movimento.
Abbiamo lasciato vuoto il metodo Start, che in questo caso potremmo anche cancellare.
All’interno del metodo Update (che ricordiamo, essere eseguito decine di volte al secondo durante l’esecuzione del gioco) abbiamo lavorato con uno dei tre sotto-elementi del vector3, la sua parte X, semplicemente scrivendo il nome della variabile di tipo Vector3 movimento seguito da .x.
Alla riga 14 infatti abbiamo detto allo script di aumentare di 0.1f la sua componente X, dunque, ad ogni aggiornamento la X del vector3 aumenterà di 0.1.
A questo punto del codice il vector3 movimento è ancora un punto nello spazio a se stante, che non ha nulla a che fare con nessun gameObject nella scena.
Dobbiamo dunque dire allo script di assegnare  il vector3 movimento come posizione di un gameObject, attraverso il suo Transform.
E’ quello che abbiamo fatto alla riga 16. Infatti abbiamo assegnato il vector3 movimento alla posizione dell’oggetto con  l’istruzione transform.position=movimento;. Ovvero diciamo via codice: la posizione del transform deve essere sempre uguale a movimento. Questa impostazione verrà ripetuta costantemente, ad ogni frame (fotogramma) del gioco.

A questo punto la position del nostro trasform sarà sempre posta uguale a movimento, ad ogni fotogramma.

Ma perché parliamo de“il nostro transform“? Perché non di un altro qualsiasi transform presente nella scena? Perché è bastato scrivere transform sullo script per intendere il transform della sfera e non quello di un altro gameObject nella scena?
Chi ha deciso a quale specifico transform applicare il vector3 movimento?

Una domanda più che lecita, e questa è la risposta:
quando scriveremo la parola transform all’interno di uno script, sarà sempre sottinteso che si intende il trasform su cui è posizionato questo script. Quando uno script incontrerà la parola transform andrà infatti alla ricerca del transform dell’oggetto sui cui lo script è attaccato ed eseguirà le istruzioni su di esso.

Basterà scrivere la parola transform all’interno di uno script per lavorare automaticamente con il transform dell’oggetto su cui è posto lo script.

Sappiate sin da ora che vale la stessa cosa per la parola gameObject.
Mentre per gli altri componenti dovremmo esplicitamente richiedere di lavorare su di essi con il codice :
GetComponent<tipoComponente>(); (cioè, prendi il componente di tipo topoComponente presente su questo gameObject)
per lavorare sul gameObject e sul transform dell’oggetto su cui è posto lo script, non sarà necessario usare la funzione “GetComponent”.

Sarà comunque possibile operare anche su un trasform o gameObject diversi da quelli a cui è attaccato lo script semplicemente andandoli a specificare.Se per esempio avessimo voluto far muovere il gameObject “cubo”, avremmo dovuto specificare una cosa del genere:
cubo.transform.position = movimento;
dove cubo è il gameObject del cubo.
Vedremo un esempio pratico poco sotto, prima però dobbiamo fare una doverosa:

Precisazione sul codice poco sopra.
In realtà non è corretto scrivere il movimento di un oggetto nel modo in cui lo abbiamo visto poco sopra perché manca qualcosa che andrà sempre inserito negli incrementi, che siano di scala o di movimento: Time.deltaTime.

In questo caso:   0.1f * Time.deltaTime.

using UnityEngine;

public class NuovoScript : MonoBehaviour
{
    public Vector3 movimento;



     void Update()
    {
        movimento.x+=0.1f *Time.deltaTime;

        transform.position = movimento;
    }

}

Il Time.deltaTime

deltaTime è una variabile statica della classe Time. Ovvero, è una variabile che sarà sempre la stessa in qualunque punto del codice la andremo a richiamare, a prescindere dall’oggetto su cui è posta perché appunto, statica. Noi dovremmo solo ricordarci di inserirla sempre negli incrementi che faremo nel metodo Update().

Ma perché aggiungere questa moltiplicazione apparentemente inutile?
Non è inutile, anzi, sarà sempre necessaria. Ecco il perché:
il metodo Update è afflitto dalle prestazioni della macchina su cui si sta eseguendo il gioco. Su una macchina performante il metodo Update potrà essere eseguito anche centinaia di volte al secondo, mentre su macchine meno potenti potrà arrivare anche a scendere sotto i 30 aggiornamenti al secondo.
Questo fa si che ciò che viene “mosso” dentro al metodo Update dovrà essere regolamentato e “normalizzato” in modo che sia uguale a prescindere dal numero di fotogrammi al secondo che a cui sta girando il gioco in quel momento, altrimenti su alcune macchine un oggetto andrà più veloce e su altre meno.  Anche considerando il fatto che l’aggiornamento non è mai stabile su un numero fisso e potrebbe essere altalenante, potrebbe capitare di vedere muovere un oggetto a velocità non costanti.
Per ovviare questo problema basta moltiplicare per Time.deltaTime ad ogni incremento che faremo all’interno del metodo Update.
Potremmo approfondire la questione puramente matematica in un altro ambito, per ora provate ad intuire da soli il perché questa moltiplicazione “aggiusti le cose” sapendo il solo fatto che Time.deltaTime è il tempo che intercorre tra un frame e l’altro. Moltiplicando un incremento o una sottrazione costante per questo valore rende il cambiamento della variabile indipendente dal framerate.

Nota a margine:
Quando moltiplicherete lo spostamento per Time.deltaTime esso risulterà molto più lento rispetto a prima che lo faceste perché Time.deltaTime è sempre inferiore a 1. Nulla di eccezionale, è tutto nella norma. Per questo è sempre meglio farlo sin da subito per regolarsi sulla quantità di movimento che si vuole ottenere.

Muoviamo un gameObject su cui non è stato attaccato uno script.

Riprendiamo il discorso lasciato in sospeso poco sopra. Muoviamo un oggetto qualsiasi su cui non è stato necessariamente attaccato uno script. Praticamente muoviamo un gameObject tramite uno script attaccato su un altro gameObject .
Vediamo un esempio funzionale modificando lo script appena sopra:

using UnityEngine;

public class NuovoScript : MonoBehaviour
{
    public Vector3 movimento;
    public GameObject oggettoDaMuovere;



     void Update()
    {
        movimento.x+=1f * Time.deltaTime;

        oggettoDaMuovere.transform.position = movimento;
    }

}

Alla riga 5 abbiamo sempre il Vector3 movimento. Lo teniamo pubblico solo per vederlo nell’inspector.
Alla riga 6 abbiamo aggiunto una nuova variabile di tipo GameObject che abbiamo chiamato fantasiosamente “oggettoDaMuovere”. Sarà ovviamente qui che andremo ad impostare il gameObject che vorremo far muovere al posto della sfera.
Ricordiamoci che questo script si trova sulla sfera, non c’è bisogno di spostarlo ne sul gameObject da muovere ne da nessun’altra parte. Basta che sia presente nella scena, su qualsiasi gameObject.
Alla riga 12 abbiamo effettuato il solito calcolo, aggiungiamo 1 alla x della variabile di tipo vector3 chiamata movimento. (invece che 0.1f ora aggiungiamo 1f per avere un movimento più tangibile visto che il Time.deltaTime attenuerà il movimento).
Alla riga 14 abbiamo il cambiamento più evidente. Invece che riferirci al normale transform del gameObject di questo script, andiamo ad agire sul transform del gameObject che abbiamo scelto come oggettoDaMuovere.
Per impostare il gameObject  oggettoDaMuovere ci basterà fare un dra&Drop, ovvero trascinare l’oggetto, dalla Hierarchy di Unity fin dentro allo script nell’inspector.
Come evidenziato da questo breve video, potremmo far muovere qualsiasi oggetto:

Come avrete notato dal video, allo start della scena il cubo appare subito sulle coordinate 0,0,0 e non “parte” dal punto in cui si trova nella scena.
Questo perché non abbiamo impostato il punto di partenza, ovvero, la variabile movimento non è stata inizializzata e dunque risulterà uguale a (0,0,0).

Se volessimo fare le cose per bene e far inziare a muovere l’oggetto dal punto in cui si trova, potremmo semplicemente aggiungere questa riga nel metodo Start:

void Start(){
    movimento=oggettoDaMuovere.transform.position;
}

Ovvero diremo che la variabile movimento dovrà iniziare il suo incremento partendo dal Vector3 position in cui si trova l’oggetto allo start della scena.

Conclusioni finali

Questo sistema per muovere un oggetto sulla scena è solo uno dei tanti disponibili e lo abbiamo esaminato sopratutto per prendere confidenza con il trasform e le coordinate di posizione.
Avremmo anche potuto usare il metodo Translate del Transform e altri sistemi .
In seguito impareremo ad usare i Rigidbody di Unity, ovvero quegli oggetti che risentono della fisica.
Per muovere gli oggetti, invece che lavorare sulle loro coordinare di posizione, andremo ad imprimere delle forze che spingeranno letteralmente gli oggetti.
Sarà questo il metodo più preciso e realistico per muovere gli oggetti e fare in modo che si muovano anche a causa della gravità o a causa dell’urto con altri oggetti.

9 pensieri su “Muovere un gameObject da script

  1. Nel primo script di esempio, viene dichiarata una variabile di tipo Vector ma non viene mai istanziata, dunque nel metodo di update quando si accede alla sua x non dovrebbe dare tipo un NullPointerException? O è corretto così?

    1. NullPointerException sono eccezioni che si verificano quando si tenta di utilizzare un riferimento che punta a nessuna posizione in memoria (null).
      Quando si crea un Vector3, anche se non lo inizializza in realtà è come si fosse creato un Vector3.Zero, ovvero un un Vector3 con X,Y e Z uguale a 0. Ma non null.
      Così come se si dichiarasse una variabile di tipo string senza inizializzarla, cioè senza “riempirla di caratteri”, essa non sarà mai null ma sarà semplicemente senza caratteri, vuota.
      Essendo il C# un linguaggio “managed” gli errori NullPointerException sono assai rari.

  2. I’m not that muϲh of a online reader to be honest but your sites гeally nice,
    keep it up! I’ll go aheaⅾ and bookmark your ѕite to come back
    down the road. Cheers

  3. Come da tua richiesta, segnalo che il video nella pagina con EDGE non funziona mentre con Chrome funziona .
    informazione che potrebbe tornare utile ad altri utenti .
    Grazie per l’ottimo lavoro

    1. Grazie per la segnalazione. Purtroppo Edge non è il top dei browsers e a volte crea qualche problema. Non avendo questo browser non posso fare dei test, ma sinceramente penso e spero che gli sviluppatori abbiano la tendenza di usare Firefox, Chrome o qualsiasi altro browser che non sia Microsoft. 😉

  4. Ciao! Non credo tu abbia trattato l’argomento NavMesh ma avrei una domanda su quello.
    Ho letto un paio di settimane fa la tua guida e mi ha spinto a leggere l intera documentazione di unity e adesso sto studiando direttamente quella. Ora stavo sperimentando con la navmesh e tutto funziona perfettamente. Ma solo se uso humanoids. Appena provo a cambiare e settare un nuovo agent non riesco a muovere nulla. Il mio obiettivo sarebbe quello di creare un agent auto.
    Riusciresti a spiegarmi cosa non va? ho cercato su internet ma trovo risposte poco valide.
    E quando hai tempo pubblica update di starshift!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.