Per fer un sistema de guardat en un videojoc s'han de tenir clar aquests aspectes:
- Quina informació que es vol guardar (per veure amb quins scripts s'haurà de comunicar el sistema).
- A on s'emmagatzema de manera temporal aquesta informació mentre el joc està en funcionament.
- A on s'emmagatzema aquesta informació de manera permanent (Save).
- En quin moment i en quin ordre es carrega aquesta informació permanent a la partida (Load).
Tot això influeix molt a l'hora de dissenyar un sistema de guardat, ja que com aquest s'ha de comunicar constantment amb altres scripts (com els d'estats del jugador, els d'estats dels pickups, la interfície d'usuari…) s'ha de vigilar molt l'ordre en què les funcions de tots aquests scripts afecten al joc.
Quines dades es volen guardar?
En primer lloc, es crea una nova classe [SaveData], que contindrà les variables en què guardarem la informació.
En el nostre videojoc són les següents:
A on s'emmagatzema de manera permanent aquesta informació?
Per determinar a on i com es guarda la informació de [SaveData], es crea un mànager per al sistema de guardat [SaveManager]. Diguem que aquest mànager és l'intermediari entre el llenguatge de Unity i el llenguatge de l'ordinador.
Algunes de les funcions claus d'aquest script són:
- Application.persistentDataPath ⇾ per localitzar la carpeta de data persistent de l'aplicació a l'explorador de l'ordinador (i poder guardar allí la partida).
- JsonUtility.ToJson // JsonUtility.FromJson ⇾ per passar la informació de Unity a JSON i viceversa.
- File.WriteAllText // File.ReadAllText ⇾ per escriure i llegir el text JSON.
- Altres funcions menors com Directory.CreateDirectory, File.Exists, File.Delete…
A on s'emmagatzema de manera temporal aquesta informació?
Depèn. Es podria haver fet fer un script dedicat només a aquesta tasca, però com en el nostre joc l'script [PlayerStats] ja tenia algunes funcions que s'encarreguen de controlar aquestes dades (com per exemple GetVida()), ha acabat sent el centre neuràlgic del sistema de guardat.
Algunes dades no requereixen ser guardades temporalment, ja que es poden extreure directament amb eines de Unity, com per exemple el nom de les escenes amb SceneManagement o el temps actual amb System.DateTime. En canvi, altres dades són més complicades de guardar, com els pickups.
En primer lloc, per poder saber quins objectes han estat recollits de l'escena i quins no, tots ells necessiten tenir un ID únic. Com que tots els pickups (estrelles, vides i papirs) tenen adjunt un script a l'inspector (EstrellaPickUp, VidaPickUp i PapirPickUp respectivament), s'han fet servir aquests mateixos: amb la línia de codi [SerializeField] private string papirId; es pot assignar l'ID L1_Papir_01 a l'objecte Papir_01 mitjançant l'inspector de Unity.
Un cop fet això, ja es pot crear un llistat a on emmagatzemar temporalment quins pickups han estat recollits a l'escena.
Com i quan es guarda la partida?
És a dir, com i quan es passa la informació temporal emmagatzemada a les variables de [PlayerStats] a un estat permanent amb l'ajuda de [SaveManager] i [SaveData]?
En el nostre joc es va contemplar un sistema d'autoguardat, tot i que al final va ser descartat: es pensava fer servir per donar l'opció de reintentar la partida quan el jugador morís, però com al final cada nivell és independent (no s'acumulen les estrelles recollides del nivell 1 al 2), amb tornar a carregar cada nivell des del principi és suficient.
Per tant, les partides es guardaran (i carregaran) exclusivament a través de la interfície d'usuari, simplificant la seva integració: el joc es guardarà només en seleccionar-se una ranura de guardat, així que directament vam escriure el codi a l'script [SaveGameUI].
Primer es busca l'script a on és la informació temporal [PlayerStats], després es fa servir el constructor de [SaveData] i es registren quines estrelles, papirs i vides han estat recollides, quins enemics han estat matats i quines palanques han estat activades. Per últim, es passa tot això a l'ordinador mitjançant [SaveManager] i s'actualitza l'estat de la ranura per mostrar que ja no és buida.
Com i quan es carrega la partida?
El com ja l'hem decidit en el sistema de guardat: de manera manual a través de la interfície d'usuari. En prémer una ranura, es passa la informació permanent a un estat temporal fins que és carregada a l'escena.
Aquest és el punt crucial. Si aquesta informació es carrega massa aviat a l'escena, pot ser sobreescrita per una altra funció si no es va amb compte, i si es carrega massa tard, l'escena pot interpretar aquesta informació com a buida o inexistent. A més, també s'ha de tenir en consideració què més s'ha d'activar o fer en carregar aquesta informació: si al personatge li falta vida ha d'actualitzar els seus paràmetres, si un pickup ha estat recollit s'ha de destruir de l'escena i actualitzar el HUD, i si una palanca ja ha estat activada i obert una porta, aquesta porta s'ha d'obrir (sense reproduir sons o efectes).
Per exemple, a l'script [PlayerController] s'actualitza constantment la posició del jugador a Update(), i per això es va haver de bloquejar aquesta funció amb el boolean IsLoadingFromSave fins que es carreguessin correctament les dades guardades.




Cap comentari:
Publica un comentari a l'entrada