Unity 3D Italia

Approfondimento sulle Variabili

Approfondiamo un po’ la questione sulle variabili e sulla loro dichiarazione.
Come già detto, C# è un linguaggio fortemente tipizzato. Ogni variabile è di un certo tipo. Ovvero ogni variabile appartiene è di una certa tipologia, numero intero, numero con virgola, stringa di testo ecc…

Il compilatore usa le informazioni sul tipo anche per assicurarsi che tutte le operazioni eseguite nel codice siano fattibili. Se ad esempio si dichiara una variabile di tipo int (numero intero), il compilatore consente di usare questa variabile in operazioni di addizione e sottrazione. Se si provasse ad eseguire le stesse operazioni su una variabile di tipo bool (valore true o false), il compilatore genererebbe un errore, come illustrato nell’esempio seguente:

int a = 5; //Numero interno             
int b = 2;  //Numero intero
bool c = true; //Variabile bool

 //OK
int sum= a + b;

// Errore, non si può sommare un valore di tipo int e un valore di tipo bool
int sum2 = a + c;

Non volgio tediarvi troppo su questioni che al momento potrebbero solo confondervi le idee, dunque parleremo solo lo stretto necessario dei:

I modificatori d’accesso

Quando dichiariamo una variabile possiamo stabilire il suo “livello di accessibilità”, che controlla se potrà essere usata da altro codice. Per il momento a noi interessano solo i modificatori d’accesso public e private.
In realtà private non lo useremo mai, perché già nel momento in cui non specificheremo nessun modificatore d’accesso, avremmo sottinteso che esso è private e dunque sarà visibile e accessibile solo nella sua classe di appartenenza.

Concentriamoci dunque sul modificatore public.

Se provassimo a dichiarare due variabili così:

int test1;
public int test2;

avremmo dichiarato due normalissime variabili del tipo int, solo che test sarà private, cioè sarà accessibile solo all’interno della classe in cui è stata dichiarata, mentre test2, volendo, si potrà leggere, scrivere e modificare a piacimento anche da altri scripts.

Una cosa molto importante per noi che lavoriamo su Unity, sta nel fatto che una variabile pubblica sarà visibile anche nell’inspector in Unity. A differenza di una variabile non pubblica.

Il modificatore static.

Questo modificatore sarà molto importante in futuro. Siamo in questa lezione proprio per approfondire e spiegare l’uso di questa parolina! Perché è leggermente più complicato capire il suo uso rispetto ai modificatori d’accesso che abbiamo trattato in poche righe qui sopra.
static non è un modificatore di accesso, infatti esso può essere specificato anche insieme ad un modificatore d’accesso, tipo:

pulic static int miaVariabile;

In questo modo abbiamo dichiarato una variabile che è pubblica, statica e di tipo int.

A cosa serve static?

static è un modificatore per dichiarare una variabile che appartenga alla classe invece che a un oggetto specifico. Ovvero, una variabile che non cambierà tra un oggetto e un altro ma sarà sempre lo stessa, con un valore unico, in comune a tutti gli oggetti di quella classe. E che dunque, quando cambierà, cambierà per tutti gli oggetti di quella classe.

Possiamo dire che una variabile statica è una variabile di classe e non di oggetto.


Facciamo chiarezza e approfondiamo sulla situazione.
Ogni volta che dichiariamo una variabile sappiamo che stiamo creando “una certa proprietà” di quella classe che sarà poi modificabile a piacimento in modo differente per ogni oggetto di quella classe.

Un po’ come creare una classe di tipo “Automobile” e dichiararne le variabili:
“velocità massima”, “accelerazione”, “numero ruote”.
Ogni oggetto “Automobile” potrà avere una velocità massima e una accelerazione differente da un’altro oggetto “Automobile“.
Su Automobile “Ferrari” potremmo impostare velMassima = 350 e accelerazione = 10.
In Automobile “Fiat Punto” potremmo inserire velMassima = 150, accelerazione = 2.
Ma una cosa non cambierà mai per tutti gli oggetti di tipo Automobile, ovvero “numero ruote” che sarà sempre = 4.

public class Automobile: MonoBehaviour  {

public int velMassima ; //Valore modificabile a piacimento per ogni oggetto di tipo Automobile
public int accelerazione;  //Valore modificabile a piacimento per ogni oggetto di tipo Automobile
static int numeroRuote=4;  //Volore modificabile, ma sempre uguale per ogni oggetto di tipo Automobile
…..



Facciamo un altro esempio riprendendo lo script della palla usato nella lezione sulle classi e sui metodi.
Avevamo dichiarato due variabili così che  oggetto della classe palla che avremmo creato, avrebbe potuto avere un determinato valore di peso e di attrito.
Dunque, ogni palla potrà avere un suo peso e un suo valore di attrito unici.
Rivediamo quell’esempio:

Questa volta però creiamo due sfere, con lo stesso script Palla attaccato.

public class Palla: MonoBehaviour  {

    public int pesoPalla;
    public int attritoPalla;
    

	void Rimbalzo () {
		//dai una spinta verso l'alto di 100 - pesoPalla
	}
	
	void Rotolamento () {
		//dai una spinta di rotazione alla palla di 10 - attritoPalla
	}
}

Lasciamo da parte i due metodi che avevamo inserito per fare l’esempio sui metodi e concentriamoci sulle variabili e la loro dichiarazione.

Notiamo che su ogni Palla abbiamo le due variabili visibili sull’inspector, perché appunto sono due variabili pubbliche.
Dall’inspector possiamo impostare i valori di rimbalzo e attrito come vogliamo.
Su una sfera possiamo mettere per esempio

peso = 2 e attrito = 10

e su l’altra

peso = 5 e attrito = 15


Ottimo, abbiamo due sfere differenti, in pratica due gameObject con delle caratteristiche differenti.

Proviamo ora ad aggiungere una variabile statica.
Pensiamo ad una caratteristica da assegnare ad una palla, per esempio la sua scala, di tipo float. Rendiamola sia pubblica che statica.

public class Palla: MonoBehaviour  {

    public int pesoPalla;
    public int attritoPalla;
    public static float scalaPalla;

	void Rimbalzo () {
		//dai una spinta verso l'alto di 100 - pesoPalla
	}
	
	void Rotolamento () {
		//dai una spinta di rotazione alla palla di 10 - attritoPalla
	}
}


Dov’è la variabile della scala? Eppure è pubblica! Perché non è visibile nell’inspector?

Tutto, nella norma, è giusto così.
Per capire il perché di questa situazione, assegniamo davvero la scala alle sfere in base alla variabile scala che avviamo creato.
Per farlo dobbiamo moltiplicare la scala attuale del transform per la variabile scala.
Così:

transform.localScale *= scalaPalla;

In questo modo tutte e tre le componenti X,Y,Z saranno moltiplicate per il float scala.
Lo faremo dentro il metodo Start, così che l’operazione avvenga una sola volta e all’inizio dell’esecuzione della scena.

using UnityEngine;

public class Palla : MonoBehaviour
{

    public int pesoPalla;
    public int attritoPalla;
    public static float scalaPalla=2;

    void Start() {

        //Moltiplico la scala dell'oggetto per la variabile scala
        //con l'operatore *= è come se avessi scritto 
        //transform.localScale = transform.localScale * scala;
        transform.localScale *= scalaPalla; 

    }

    void Rimbalzo()
    {
        //dai una spinta verso l'alto di 100 - pesoPalla
    }

    void Rotolamento()
    {
        //dai una spinta di rotazione alla palla di 10 - attritoPalla
    }
}

Come abbiamo visto, per semplicità abbiamo usato l’operatore *=
Come descritto nel commento, con l’operatore *= è come se avessimo scritto:
” poni la scala attuale dell’oggetto uguale alla scala attuale dell’oggetto moltiplicato per la variabile scala”.

Ma torniamo alla questione del modificatore static.
Mandiamo in esecuzione la scena.

E si, a questo punto avremo due sfere belle grosse! Come avevamo previsto, entrambe le sfere ora hanno la scala moltiplicata per 2f. Questo ci chiarisce l’utilità della parolina static.
La variabile statica scala è una proprietà della classe in cui l’abbiamo scritta, non degli oggetti specifici che poi andremo a creare. In questo caso tutti gli oggetti della classe palla, avranno la scala che gli abbiamo impostato.Per questo motivo non può apparire nell’inspector di un singolo oggetto!

 

Che ce ne facciamo?

Di solito, quando si impara una cosa nuova, il pensiero va subito a “dove poter usare” la nuova nozione appresa. Non so a voi ma a me succede così. 😉
E dove possiamo usare questa nozione nello sviluppo di un videogioco? A prima vista sembra quasi inutile.

Ma soffermatevi un attimo a pensare alla logica d’esecuzione di un gioco nel suo insieme.
Ci saranno gli oggetti, personaggi, edifici, ambiente e quant’altro. Tutti oggetti con le loro caratteristiche che renderemo uniche proprio per renderli differenti gli uni dagli altri.
Un nemico, per esempio, avrà un valore di energia differente da un’altro nemico, a seconda se ha subito colpi ecc.. Dunque ogni classe “Nemico” dovrà avere la sua variabile “energia“.
Ma questo non vale per il Player, perché esso è uno solo, dunque il suo valore “energia” sarà sempre leggibile e modificabile dalla stessa variabile.

Riassumendo:

Ci saranno delle variabili che dovranno essere uniche e persistenti per tutta la durata del gioco, dei valori che ci dovremmo portare tra le scene e che non dipenderanno da un oggetto specifico.
Prendiamo per esempio il punteggio del giocatore, oppure la sua energia, lo stato del gioco, se in pausa o no… e millemila altre cose. Queste variabili le potremmo impostare su static, perchè punteremo sempre alla stessa variabile, unica per tutto il gioco.
Tale variabile sarà sempre unica e non ci saranno altre versioni essa. Ovvero non ci saranno due o più variabili “energiaGiocatore”. Ci sarà sempre solo una variabile con il suo valore che identifica il punteggio del giocatore, la sua energia ecc…” Ovviamente il valore, essendo appunto, variabil, si modificherà durante lo svolgimento del gioco.
Ma essa sarà comunque una variabile unica, che cioè esiste in una sola istanza per tutto il gioco a prescindere dal livello che si sta giocando o di qualsiasi altro fattore.

Queste sono variabili che si dovranno portare sempre “con noi” (cioè dovranno essere sempre accessibili da qualunque script, in qualunque momento del gioco) durante tutto lo svolgimento del programma.

L’importanza delle variabili statiche in Unity

Impereremo presto che, nel momento in cui ci sarà un passaggio di scena (per esempio tra un livello e l’altro), tutti i gameObjects nella scena verranno letteralmente distrutti. Cancellati per sempre. Pufff…Non ne avremo più traccia nella scena appena caricata. Ed insieme a loro, saranno distrutte tutte le caratteristiche che avevano assunto durante la scena! 🙁
Ma le variabili statiche rimarranno inalterate perché appunto, non sono elementi applicati ai gameObjects che andranno distrutti al cambio di scena. Evviva! 😆
Vedremo in seguito che in gioco sarà sempre buona prassi creare un GameManager. Ovvero un normale gameObject che chiameremo GameManager su cui applicheremo uno script chiamato allo stesso modo, GameManager.cs.
Questo gameObject sarà sempre presente dall’inizio alla fine dell’esecuzione del gioco, sarà sempre lo stesso, con lo stesso script attaccato, pieno di variabili statiche. Anche al cambio di scena, esso rimarrà invariato e non verrà mai distrutto.

A questo proposito potrete trovare una spiegazione più dettagliata nell’articolo struttura-e-organizzazione-iniziale dove verrà spiegata nel dettaglio la funzione DontDestroyOnLoad ovvero la funzione che imposta un gameObject come permanente e che dunque non dovrà essere distrutto al cambio di una scena.

Dentro allo script GameManager.cs potrai inserire tutte quelle variabili (statiche) che ti potranno servire in ogni momento del gioco, a prescindere dalla scena in cui ti trovi. In pratica avrai dei dati sempre disponibili che non varieranno al caricamento di un’altra scena ma che potrai incrementare, variare, leggere ecc… come qualsiasi altra variabile.
E potrai leggerle e scriverle semplicemente con la riga GameManager.miaVariabile.

Per leggere una variabile statica non avremo bisogno dunque di “cercarla” in un singolo oggetto, ma la troveremo direttamente nella classe.

Esempio:

public class Giocatore: MonoBehaviour
{

public int miaVariabile1;

public static int miaVariabile2;

….

Abbiamo due variabili nella classe Giocatore, una statica e una no.
Per impostare per esempio un punteggio uguale a miaVariabile1, dovremmo andare su un oggetto della classe Giocatore.
Tipo:

public Giocatore oggetto = new Giocatore();

punteggio=oggetto.miaVariabile1;

dunque avremmo dovuto prima cercare un oggetto di classe Giocatore e di conseguenza andremo a leggere la variabile di quello specifico oggetto.
Per la variabile statica invece dovremmo semplicemente richiamarla così:

punteggio=Giocatore.miaVariabile2;

cioè richiamarla direttamente da Giocatore perché la variabile è parte della classe.

Modificatori d’accesso sui metodi

Come forse avrete già notato, le parole static, pubblic, private sono usate sia per le variabili che i metodi ed hanno lo stesso identica funzione. Ovvero stabilire il “livello di accessibilità” del metodo.

Così come per le variabili, un metodo pubblic sarà accessibile da qualunque altro script, mentre uno private sarà accessibile solo dall’interno della classe a cui appartiene.

Un metodo denominato come static sarà una funzione specifica della classe e non di un oggetto specifico.
Infatti per richiamare un metodo static dovremmo semplicemente usare una sintassi tipo:

MiaClasse.EseguiFunzione():

a differenza di un metodo non static che per essere eseguito è necessario anche la specificare l’oggetto in questione:

MiaClasse.quellOggetto.EseguiFunzione();

 

 

 

Exit mobile version