Imparare C#Principianti

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! :mrgreen: 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();

 

 

 

16 pensieri su “Approfondimento sulle Variabili

    1. Ciao Eden,
      non so quale siano le tue necessità nello specifico, ma sappi che una costante (const) è sempre statica e non è necessario definirla come static.
      Essendo una costante non modificabile essa sarà sempre fissa e uguale per ogni oggetto della classe.

      1. “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 allo stesso valore, unico per tutto il gioco.”
        Il punteggio, l’energia, se in pausa o no ecc. cambiano il loro valore nel corso del tempo quindi perché dici stesso valore?

        1. Forse mi sono espresso male. Dovrò correggere qualcosa nell’articolo. In effetti ho usato la parola “valore” al posto di “variabile”. 😉
          Con “lo stesso valore” intendevo dire che quella variabile sarà sempre unica e non ci saranno altre versioni essa. Ovvero non ci saranno due o più variabili “energiaGiocatore”. Ci sarà sempre solo un valore che identifica il punteggio del giocatore, la sua energia ecc…” Ovviamente i valori di queste variabili… sono appunto variabili, cioè si modificano durante lo svolgimento del gioco.
          Ma sono variabili uniche, che cioè esistono in una sola istanza per tutto il gioco a prescindere dal livello che si sta giocando o di qualsiasi altro fattore. Variabili che si dovranno portare sempre “con noi” durante lo svolgimento del gioco. Ma pur sempre variabili.

  1. io non ho capito l’ultima parte dell’articolo(“l’importanza delle variabili statiche in unity”). Visto che tutti i gameObjects vengono distrutti al cambio di scena , che senso ha fare il gameObject “GameManager”? lui non viene distrutto ?
    Poi non riesco a capire la differenza dei 2 esempi alla fine. Non riesco a capire cosa intendi con “E potrai leggerle e scriverle semplicemente con la riga GameManager.miaVariabile.”. Potresti rispiegare questi ultimi punti ?
    Grazie in anticipo e scusa il disturbo (e scusa se ti do del tu).

    1. Ciao Simone,
      in effetti dovrei aggiungere alcune considerazioni a questa parte dell’articolo. Considerazioni che ho comunque fatto nell’articolo struttura-e-organizzazione-iniziale/
      Il gameObject GameManager non verrà mai distrutto perché sarà “marcato” come oggetto “DontDestroyOnLoad” ovvero un gameObject persistente che non sarà cancellato al cambio di scena. Un po’ come se fosse un oggetto “sospeso tra le scene”.
      Il gameObject con lo script GameManager verrà dunque creato al caricamento della prima scena e rimarrà invariato per tutta la durata del gioco, a prescindere dai caricamenti di altre scene. Tutti i gameObjects che saranno “marcati” come DontDestroyOnLoad non verrano mai distrutti automaticamente al cambio di scena.

  2. credo ci sia un errore nel primo script, nell esempio dell errore sulla somma di dati int e bool…cè scritto a+b ma non dovrebbe restituire errore in questo modo, credo volesse scrivere a+c…non ho compreso anche la seconda parte “int sum2 = a + test”

    1. Ciao Carlos,
      il primo esempio va bene, infatti è quello preceduto dal il commento “OK”.

      //OK
      int sum= a + b;

      Il secondo esempio (a+test) aveva un errore che ho corretto, grazie per la segnalazione 😉

  3. “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.”
    Ho notato in altri tutorial che alcuni per farlo usano serialize field.
    Puoi spiegarmi la differenza e quali dei due è più conveniente da utilizzare per scrivere un codice più pulito?
    Se dovesse essere spiegato più avanti allora my bad, puoi anche solo indirizzarmi all’argomento.
    Approfitto dell’occasione per farti i complimenti sulla guida, semplice,concisa e molto esaustiva!
    Buona fortuna con il gioco in via di sviluppo, promette veramente bene!

    1. Ciao Gabriele, grazie per i complimenti.
      SerialiseField e pubblic sono due cose differenti.
      Usando SerialiseField una variabile sarà visualizzata nell’inspector anche se non pubblica. Essa non sarà accessibile da altri script perché appunto, non pubblica.

      Ecco le quattro diverse combinazioni possibili (ma ce ne sarebbero anche altre).

      pubblic – Mostra in inspector e accessibile da altri script
      [SerialiseField] private – Mostra in inspector, non accessibile da altri script
      [HideInInspector] pubblic – non viene visualizzato in Inspector, accessibile da altri script
      private – Non mostra in inspector e non accessibile da altri script

      Parole come pubblic, private e protect sono chiamate modificatori di accesso e cambiano quello che viene chiamato livello di accessibilità di una classe o di una variabile. Puoi leggere ulteriori informazioni sui modificatori di accesso nella sessione “approfondimento sulle variabili” https://www.unity3dtutorials.it/2018/01/28/le-variabili/
      È possibile accedere e modificare le variabili pubbliche mediante codice esterno alla classe. In generale, dovresti sempre provare a creare variabili con il livello di accessibilità più basso necessario; cioè privato. Ci sono molte ragioni per questo, ma una delle ragioni è che quando lavori in una grande squadra, o anche da solo, può essere facile scrivere codice che modifica accidentalmente variabili in altre classi che non dovrebbero essere modificate. La maggior parte delle variabili create in realtà sono necessarie solo all’interno della classe in cui sono dichiarate e dovrebbero essere private.

      Mentre può essere allettante rendere pubbliche le variabili perché Unity le fa apparire automaticamente come un campo in Inspector, è molto meglio rendere una variabile privata (per i motivi sopra menzionati) e quindi utilizzare l’attributo [SerializeField] solo se è necessario accedervi tramite l’inspector. Saranno così modificabili solo da inspector.

      Dunque, riassumendo, si dovrebbe usare pubblic solo per quelle variabili che sappiamo che dovranno essere lette e modificate da altri scripts, oltre che da inspector.
      Si dovrebbe usare [SerializeField] nel caso in cui sarà necessario avere queste variabili diponibili solo nell’inspector.

  4. Ciao WILEz,
    grazie innanzitutto per il lavoro che fai che per chi come me è alle prime armi è un faro nel buio perchè riesci a spiegare concetti nuovi in maniera semplice e mi permetti di familiarizzare fin da subito con questi elementi.
    Ti scrivo perchè, nel provare a replicare il tuo percorso transform.localScale *= scalaPalla ho riscontrato un errore. Le due sfere che ho posto su un piano inclinato, allo start cambiano di scala (ho assegnato anch’io un valore scalaPalla=2) ma non nello stesso modo, una delle due sfere assume valore scala=2 (come dovrebbe essere) l’altra valore scala=4 . Cosa ho sbagliato? Grazie dell’attenzione e di tutto quello che fai!

  5. Ciao, e grazie ancora per queste guide che per chi come me è a digiuno di programmazione sono fantastiche.
    Avrei una domanda, se ho capito bene la spiegazione del modificato d’accesso static, questo esempio potrebbe essere corretto?
    Creo la classe “nemiciBoss” e dichiaro le varibili:
    public static int energiaBoss=300;
    public string armaInUso;
    Dunque, tutti i nemici della classe “nemiciBoss”
    avranno armi diverse ma tutti avranno la stessa energia
    E’ corretto?

    1. Si è corretto Bartowski, ma non pratico. Cioè non lo fare per l’energia dei nemici. 😉

      Se facessi in quel modo, la variabile energiaBoss sarà in comune a tutti i “nemiciBoss”. Ma non solo all’inizio, verrà sempre aggiornata. Questo vuol dire che quando un nemico verrà colpito e la sua energia scenderà, la stessa cosa accadrà per un altro nemico con quello script.
      Ogni nemico dovrebbe avere la sua variabile di energia.

      Se vuoi che per tutti i nemici l’energia inziale sia uguale a 300, puoi usare una variabile statica ed assegnarla al valore di energia unico di ogni nemico.
      Così:


      static int energiaBossIniziale=300;
      public int energiaQuestoBoss;

      Start(){
      energiaQuestoBoss=energiaBossIniziale;
      }

  6. Se attivi le rewards di Brave, do il mio contributo assolutamente. Mi hai chiarito le idee su molte cose e ne ho imparate altre. Ma da un po’ di tempo ho notato che è da aggiornare e, con questo contributo, spero di farlo aggiornare

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.