Imparare C#Imparare UnityPrincipianti

Il PlayerPrefs

Il PlayerPrefs è la classe di Unity che ci permetterà di effettuare i salvataggi permanenti, ovvero, tramite esso potremmo salvare lo stato del gioco nel momento della chiusura e ricaricare i dati alla riapertura dello stesso.
Esistono diversi modi per salvare permanentemente i dati su disco, il PlayerPrefs è sicuramente quello più immediato, anche se limitato ad una serie ristretta di tipi di dati.
Con il PlayerPrefs potremmo infatti salvare i dati di tipo float, int e string. Vedremo in seguito che esiste uno script non proprietario di Unity che permetterà di ampliare questo limite.
La classe PlayerPrefs non usa il salvataggio su file. La locazione dei salvataggi varia a seconda del dispositivo per cui si è sviluppato il gioco ma il funzionamento a livello di codice è il medesimo.

In seguito vedremo come salvare i dati con un metodo differente dal PlayerPref, più potente e veloce.
Per ora impariamo ad usare il PlayerPref che già di per se può bastare in progetti di piccola/media complessità.

 

Vediamo il suo funzionamento in pochi brevi passaggi.

Appena scriveremo la parola PlayerPrefs , ci verranno elencati i metodi statici disponibili su questa classe. Prendiamo in esame i tre metodi che iniziano con la parola Set e i i tre corrispettivi metodi Get.
Set
sta per “setta“(salva) mentre Get sta per “prendi“(carica).
Come si può facilmente intuire, con SetFloat andremo a salvare un numero di tipo float mentre con GetFloat lo andremo a caricare.

Se peresempio volessimo salvare lo stato dell’energia del giocatore, potremmo scrivere:

 PlayerPrefs.SetFloat("EnergiaPlayer", playerEnergy);

Abbiamo usato il metodo SetFloat della classe PlayerPrefs che richiede due parametri, il nome con i quale andremo a salvare il dato (di tipo string) e il dato in questione (in questo caso, di tipo float).
In questo modo avremo salvato il dato di tipo float playerEnergy su disco, sotto il nome di EnergiaPlayer. Per convenzione, i nomi con cui salviamo le variabili su PlayerPrefs vengono chiamate keys (chiavi).
Notare che il nome scelto (la key) per il salvataggio è (e dovrà sempre essere) una stringa.

Per caricare lo stesso dato salvato su disco dovremmo usare l’istruzione GetFloat:

 playerEnergy = PlayerPrefs.GetFloat("EnergiaPlayer");

In questo modo abbiamo impostato la variabile playerEnergy uguale al dato presente su disco sotto il nome EnergiaPlayer.

Potremmo creare una semplice classe per il salvataggio dei dati chiamata SaveAndLoad con questi due metodi, uno per il salvataggio e uno per il caricamento:

using UnityEngine;

public class SaveAndLoad : MonoBehaviour {


    public float playerEnergy;

	void SavePlayerState () 
	{
        PlayerPrefs.SetFloat("EnergiaPlayer", playerEnergy);
	}


    void LoadPlayerState()
    {
        playerEnergy = PlayerPrefs.GetFloat("EnergiaPlayer");
    }
    

}

 

In questo modo avremo salvato solo un dato, ovvero l’energia del giocatore.
Sappiamo però che in un gioco complesso sarà necessario salvare molti più dati.
Per esempio, potremmo aggiungere il nome del giocatore e il suo punteggio attuale. Due variabili rispettivamente di tipo string e int.

using UnityEngine;

public class SaveAndLoad : MonoBehaviour {


    public float playerEnergy;
    public int playerPoints;
    public string playerName;

	void SavePlayerState () 
	{
        PlayerPrefs.SetFloat("EnergiaPlayer", playerEnergy);
        PlayerPrefs.SetInt("PuntiPlayer", playerPoints);
        PlayerPrefs.SetString("NomePlayer", playerName);
	}


    void LoadPlayerState()
    {
        playerEnergy = PlayerPrefs.GetFloat("EnergiaPlayer");
        playerPoints = PlayerPrefs.GetInt("PuntiPlayer");
        playerName = PlayerPrefs.GetString("NomePlayer");
    }
    

}

 

In questo modo abbiamo visto tutt e tre i tipi di variabili salvabili tramite PlayerPrefs, float, int e string.

 

Salvare un Vector3 su PlayerPrefs

E se volessimo salvare l’attuale posizione del player nella scena?
Come abbiamo visto non ci sono i Vector3 tra le variabili salvabili. Dovremmo quinti salvare tre variabili float corrispondenti alle tre componenti del Vector3, X,Y,Z.
Per caricarle dovremo in seguito “ricomporre” il Vector3 position del player.

using UnityEngine;

public class SaveAndLoad : MonoBehaviour {


    public float playerEnergy;
    public int playerPoints;
    public string playerName;
    public Transform Player; //Il Transform del player

	void SavePlayerState () 
	{
        PlayerPrefs.SetFloat("EnergiaPlayer", playerEnergy);
        PlayerPrefs.SetInt("PuntiPlayer", playerPoints);
        PlayerPrefs.SetString("NomePlayer", playerName);
        
        //Salvo individualmente le tre componenti della position del player
        PlayerPrefs.SetFloat("posizionePlayerX", Player.position.x);
        PlayerPrefs.SetFloat("posizionePlayerY", Player.position.y);
        PlayerPrefs.SetFloat("posizionePlayerZ", Player.position.z);
	}


    void LoadPlayerState()
    {
        playerEnergy = PlayerPrefs.GetFloat("EnergiaPlayer");
        playerPoints = PlayerPrefs.GetInt("PuntiPlayer");
        playerName = PlayerPrefs.GetString("NomePlayer");
        
        //Carico i valori su tre variabili temporanee
       float Xpos = PlayerPrefs.GetFloat("posizionePlayerX");
       float Ypos =  PlayerPrefs.GetFloat("posizionePlayerY");
       float Zpos =  PlayerPrefs.GetFloat("posizionePlayerZ");
       
       //Assegno i tre valori alla position del player
        Player.position = new Vector3(Xpos,Ypos,Zpos);
        
    }
    

}

 

Abbiamo salvato in modo separato le tre componenti della posizione del giocatore, rispettivamente con i keys:

  • posizionePlayerX
  • posizionePlayerY
  • posizionePlayerZ

Per caricarli dovremmo quindi “ricomporre” il Vector3 posizionePlayer caricandoci le tre variabili salvate su disco.
Questa operazione sembra un po’ troppo macchinosa, seppur necessaria. Considerando che sarà probabile che dovremmo salvare un sacco di posizioni di oggetti durante un gioco, dovremmo fare questa operazione per ogni Vector3.
A questo proposito, vi invito a dare un’occhiata allo script aggiuntivo presente in questo articolo. che ci permetterà di salvare i Vector3 con una sola riga di codice.

Se tentiamo di caricare qualche key che non esiste?

Prima di fare un caricamento potremmo controllare che sul PlayerPrefs esista una determinata key.
Con questa riga:
PlayerPrefs.HasKey(“nomeKey”)
che restituisce un valore booleano, vero se nomeKey esiste e falso se nomeKey non esiste.

if(PlayerPrefs.HasKey("EnergiaPlayer"))
{    
    //se esiste un dato salvato con il nome NomePlayer, assegnalo alla variabile playerName 
    playerName = PlayerPrefs.GetString("NomePlayer");
}
else
{
    //Se non esiste nessun dato salvato con la key NomePlayer, assegnagli "Pippo" di default 
    playerName = "Pippo";
    
}

 

Senza fare un controllo, nel caso la key che si tenta di caricare non esista, non avremo un errore, ma alla variabile sarà assegnato un valore di default (stringa vuota in caso di string, e 0 in caso di float o int).

 

Atre tecniche di salvataggio

Come detto, usare il PlayerPref per gestire i salvataggi in un videogioco di piccole/medie dimensioni può essere la scelta più corretta.
Come abbiamo visto possiamo salvare il valore di una variabile e in seguito ricaricare il valore per reimpostarlo sulla valriabile.
Va detto però che il PlayerPref ha delle limitazioni nelle dimensioni/quantità di dati salvabili e una gestione macchinosa dei caricamenti dove bisogna lavorare singolarmente su ogni valore per riassegnare lo stato di ogni variabile. Pensate se doveste salvare degli array o una serie di dati molto grande, con molti nemici di cui salvare lo stato ecc…
Quanto sarebbe più bello poter salvare lo stato di un’intera classe, a prescindere dalle variabili che ha al suo interno, per poter poi ripristinare lo stato della classe in tutte le sue variabili, con un semplice comando?
E’ quello che andremo ad imparare usando la serializzazione per i salvataggi su files, con ben tre metodi differenti tra loro: Json, XML e Binary Serialization.

14 pensieri su “Il PlayerPrefs

  1. Ciao, non capisco una cosa.
    Grazie intanto per il tuo lavoro.
    Quando salvi, nell’esempio la posizione (x,y,z) del GameObject, non vedo, il nome del oggetto.
    Nem tuo esempio quelle coordinate a chi fanno riferimento, al player a un nemico a una sfera, non vedo un riferimento. Chi va in quella posizione?
    Spero di essere stato chiaro.
    Inoltre dove creo o “attacco” questa classe?
    Grazie

    1. Ma c’è scritto chiaramente: PlayerPrefs.SetFloat(“posizionePlayerX”, Player.position.x);
      PLAYER.position… poi più che altro che utilità ha sapere a chi fanno riferimento se è solo un esempio? XD
      In base alle tue esigenze faranno riferimento a ciò che vuoi no?

  2. Scusami, non capisco una cosa, se voglio salvare per esempio la quantità di monete raccolte in game, nel menu principale, come potrei fare? (Sono due scene differenti)

    1. Per quel tipo di dati, relativamente semplici, il PlayerPref è la soluzione ideale.

      Se il tuo numero sulle monete è per esempio int moneteRaccolte, per salvare questo numero (che sarà accessibile da qualunque altra scena), puoi usare un’istruzione tipo:
      PlayerPrefs.SetFloat(“moneteRaccolte”, moneteRaccolte);
      Dove il primo “moneteRaccolte” è una stringa, cioè il nome che identifica i dati che stai salvando e il secondo moneteRaccolte è la tua variabile, cioè un numero intero.

      Poi, anche in un’altra scena, potrai riprendere il dato con int moneteRaccolte = PlayerPrefs.GetFloat(“moneteRaccolte”);

  3. Salve, lo script che verrà creato, dovrà essere attaccato al player, o va anche benissimo un Empty?

    la seconda cosa che volevo chiedere riguarda invece il salvataggio da una scena all’altra. è necessario l’uso del don’t Destroy on load, o le prefs sono sovrascritte senza dover ripetere il processo in ciascuna scena?

    grazie mille in anticipo, complimenti per il lavoro, mi ha aiutato parecchio a capire la base da cui partire con le prefs.

    1. Ciao Giuliano, lo script può essere messo su un oggetto qualsiasi, anche Empty. L’importante è che dallo script si possa accedere alle variabili che si vogliono salvare.

      Per rispondere alla seconda domanda, dipende cosa ti serve fare.
      don’t Destroy on load non salva nulla, semplicemente non distrugge gli oggetti e gli scripts (con annesse variabili) al passaggio di scena.
      Le prefs sono come dei valori su file, che se salvati rimangono invariati per sempre, fino a che non si sovrascrivano.
      Se ho capito bene il tuo dubbio (ma non ne sono sicuro), se metti questo script su un oggetto Empty e gli dici di essere don’t Destroy on load, non ti servirà fare altro, ovvero lo script rimarrà anche al passaggio di scena e i suoi metodi potranno sempre essere eseguiti in qualunque scena ti trovi.

  4. Salve a tutti !

    Scopro solo ora questo utilissimo portale dedicato a Unity e lo trovo utilissimo ed estremamente ben strutturato !
    Approfitto delle vostre conoscenze per chiedervi un piccolo sugerimento su come risolvere un problema che mi sta mandando fuori di testa :

    Sto realizzando un Frontend in 3D per Retroarch, una sorta di Salagiochi anni 90 virtuale dove , avvicinandoti ad un cabinato , premendo il tasto start del pad si gioca a quel determinato titolo associato al cabinato .
    Ogni cabinato ha un suo script dove ho inserito come public string il percorso delle roms e il percorso dell’eseguibile di Retroarch.

    E qui c’è il mio problema: se reinstallo Retroarch o porto il mio frontend buildato su un’altro pc, l’emulatore non parte perche’ non trova il percorso di origine . Ho inserito quindi un input field in cui è possibile inserire il percorso di Retroarch ed il tutto funziona , finche’ non quitto l’applicazione . Nel momento in cui lancio di nuovo l’applicazione devo ogni volta reinserire il percorso di Retroarch .

    La domanda è : Come faccio con “playerprefs” a salvare il path di Retroarch in modo che non venga perso ad ogni riavvio ??

    grazie mille a tutti e complimenti per il lavoro !

    1. Ciao Marco.
      Poniamo che hai il tuo path su una stringa:

      string path= “D:\MioPercorso\Retroarch”;

      Alla chiusura dell’applicazione lo salvi con questo comando:
      PlayerPrefs.SetString(“path”, path);

      Poi all’avvio dell’applicazione lo carichi automaticamente:

      void Awake(){
      path= PlayerPrefs.GetString(“path”);
      }

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.