Hoe de voortgang van uw spelers in eenheid te bewaren en te laden

Wat je gaat creëren

In deze zelfstudie leert u hoe u een eenvoudig systeem kunt implementeren om savegames te maken en te beheren voor uw Unity-spellen. We zullen het raamwerk voor een Final Fantasy-zoals hoofdmenu waarmee spelers nieuwe, unieke opslagbestanden kunnen maken en bestaande kunnen laden. De getoonde principes laten je toe om ze uit te breiden naar wat je spel nodig heeft. 

Aan het einde van de tutorial zult u geleerd hebben hoe u:

  • bewaar en laad spelgegevens binnen Unity3D met behulp van serialisatie
  • gebruik statische variabelen om gegevens over te dragen aan scènewijzigingen

Opmerking: deze aanpak voor het opslaan en laden van gamegegevens werkt op alle platforms behalve de webplayer. Raadpleeg de officiële documentatie over Unity Web Player en browsercommunicatie voor informatie over het opslaan van gamegegevens in de Web Player.

Laten we serie krijgen

Het eerste dat we gaan doen, is een code maken waarmee we onze gamegegevens kunnen serialiseren - dat wil zeggen, het converteren naar een formaat dat kan worden opgeslagen en later kan worden hersteld. Laten we hiervoor een C # -script maken en dit noemen SaveLoad. Dit script verwerkt alle opslag- en laadfunctionaliteit. 

We zullen verwijzen naar dit script vanuit andere scripts, dus laten we het een statische klasse maken door het woord toe te voegen statisch tussen openbaar en klasse. Laten we ook de : MonoBehaviour deel, omdat we het niet hoeven te koppelen aan een GameObject. En omdat het niet langer erft van MonoBehaviour, laten we het verwijderen Begin en Bijwerken functies. 

De resulterende code zou er als volgt uit moeten zien:

gebruikmakend van UnityEngine; met behulp van System.Collections; openbare statische klasse SaveLoad 

Nu gaan we wat nieuwe functionaliteit aan dit script toevoegen, dus meteen onder waar het staat met behulp van System.Collections;, voeg het volgende toe:

met behulp van System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; met behulp van System.IO;

De eerste regel stelt ons in staat om dynamische lijsten te gebruiken in C #, maar dit is niet nodig voor serialisatie. De tweede regel is wat ons in staat stelt om de serialisatiemogelijkheden van het besturingssysteem binnen het script te gebruiken. In de derde regel, IO betekent Invoer uitvoer, en dat is wat ons in staat stelt om te schrijven naar en te lezen van onze computer of mobiel apparaat. Met andere woorden, deze regel stelt ons in staat om unieke bestanden te maken en later uit die bestanden te lezen.

We zijn nu klaar om enkele gegevens te serialiseren!

Serialiseerbare klassen maken

Nu ons script de mogelijkheid heeft om te serialiseren, zullen we een aantal klassen moeten instellen om geserialiseerd te worden. Als je denkt aan een eenvoudige RPG, zoals Final Fantasy, het biedt spelers de mogelijkheid om verschillende opgeslagen spellen te maken en te laden. Dus maak een nieuw C # -script met de naam Spelen geef het een aantal variabelen om drie objecten te bevatten: a ridder, een schurk, en een tovenaar. Wijzig de code van dit script om er zo uit te zien:

gebruikmakend van UnityEngine; met behulp van System.Collections; [System.Serializable] public class Game public static Game current; openbaar karakter ridder; public Character rogue; public Character wizard; public Game () ridder = nieuw teken (); rogue = new Character (); wizard = nieuw teken (); 

De [System.Serializable] regel vertelt Unity dat dit script kan zijn geserialiseerde-met andere woorden, dat we alle variabelen in dit script kunnen opslaan. Stoer! Volgens de officiële documenten kan Unity de volgende typen serialiseren:

  • Alle basistypen van gegevens (zoals int, draad, vlotter, en bool).
  • Sommige ingebouwde typen (inclusief Vector2, Vector3, Vector4, viertal, Matrix4x4, Kleur, rect, en LayerMask).
  • Alle klassen erfenissen van UnityEngine.Object (inclusief GameObject, bestanddeel, MonoBehavior, Texture2D, en AnimationClip).
  • enums.
  • Arrays en lijsten van een serialiseerbaar type.

De eerste variabele, stroom, is een statische verwijzing naar a Spel aanleg. Wanneer we een game maken of laden, gaan we deze statische variabele instellen op die specifieke game-instantie, zodat we overal in het project naar de 'huidige game' kunnen verwijzen. Door statische variabelen en functies te gebruiken, hoeven we geen a te gebruiken gameObject's GetComponent () functie. Handig! 

Merk op dat het verwijst naar iets dat a heet Karakter? We hebben dat nog niet, dus laten we een nieuw script maken om deze klasse te huisvesten en het te noemen Karakter:

gebruikmakend van UnityEngine; met behulp van System.Collections; [System.Serializable] public class Character public string name; public Character () this.name = ""; 

Je vraagt ​​je misschien af ​​waarom we een hele nieuwe klasse nodig hadden als we alleen een stringvariabele opsloegen. Inderdaad, we kunnen het gewoon vervangen Karakter in de Spel script om te gebruiken draad in plaats daarvan. Maar ik wil je laten zien hoe diep dit konijnenhol kan gaan: je kunt klassen opslaan en laden die naar andere klassen verwijzen, enzovoort, zolang elke klasse is serialiseerbaar

Nu onze klassen zijn ingesteld om te worden opgeslagen en geladen, laten we teruggaan naar onze SaveLoad script en voeg de mogelijkheid toe om games op te slaan.

De staat van een spel opslaan

Een "Load Game" -menu toont meestal een lijst met opgeslagen spellen, dus laten we een Lijst van type Spel en noem het savedGames. Maak het een statische lijst, zodat er maar één lijst met opgeslagen spellen in ons project is. De code zou er als volgt uit moeten zien:

gebruikmakend van UnityEngine; met behulp van System.Collections; met behulp van System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; met behulp van System.IO; openbare statische klasse SaveLoad public static List savedGames = nieuwe lijst(); 

Laten we vervolgens een nieuwe statische functie maken om een ​​game op te slaan:

 public static void Save () savedGames.Add (Game.current); BinaryFormatter bf = new BinaryFormatter (); FileStream-bestand = File.Create (Application.persistentDataPath + "/savedGames.gd"); bf.Serialize (bestand, SaveLoad.savedGames); file.Close ();  

Regel 2 voegt ons huidige spel toe aan onze lijst met opgeslagen spellen. Die lijst is wat we gaan serialiseren. Om dit te doen, moeten we eerst een nieuw maken BinaryFormatter, die het serialisatiewerk zal afhandelen. Dit is wat Line 3 doet.

In regel 4 maken we een Bestandsstroom, wat in wezen een pad is naar een nieuw bestand dat we ook gegevens kunnen sturen, zoals vissen die stroomafwaarts in een rivier zwemmen. We gebruiken File.Create () om een ​​nieuw bestand aan te maken op de locatie die we als parameter doorgeven. Handig is dat Unity een ingebouwde locatie heeft om onze spelbestanden op te slaan (die automatisch worden bijgewerkt op basis van het platform waarop je spel is gebouwd) waarop we kunnen verwijzen Application.persistentDataPath

Omdat we een nieuw bestand maken, kunnen we niet alleen zeggen waar het bestand zich bevindt, we moeten ook dit pad afdekken met de naam van het daadwerkelijke bestand zelf. Er zijn twee delen in dit bestand:

  1. de bestandsnaam
  2. het bestandstype

We zullen gebruiken savedGames voor de bestandsnaam en we gebruiken een aangepast gegevenstype gd (voor "gamegegevens") voor het bestandstype. Ons resultaat is een spelbestand genaamd savedGames.gd op de locatie ingesteld door Application.persistentDataPath. (In de toekomst kunt u andere soorten dingen opslaan in dit gegevenstype, u kunt bijvoorbeeld de instellingen van de gebruikersopties opslaan als options.gd.)

Notitie: U kunt het bestandstype maken wat u maar wilt. De reeks Elder Scrolls bijvoorbeeld gebruikt .esm als het bestandstype. Je had net zo gemakkelijk kunnen zeggen savedGames.baconAndGravy.

In regel 5 noemen we het serialize functionaliteit van de BinaryFormatter om onze te redden savedGames lijst naar ons nieuwe bestand. Daarna hebben we het bestand dat we hebben gemaakt in regel 6 gesloten. 

Badda bing, badda boom. Onze spellen worden opgeslagen.

De staat van een spel laden

In de Opslaan functie hebben we onze lijst met opgeslagen spellen op een specifieke locatie in serie gezet. Omgekeerd, zou de code om onze spellen te laden er als volgt uit moeten zien:

 public static void Load () if (File.Exists (Application.persistentDataPath + "/savedGames.gd")) BinaryFormatter bf = new BinaryFormatter (); FileStream-bestand = File.Open (Application.persistentDataPath + "/savedGames.gd", FileMode.Open); SaveLoad.savedGames = (lijst) Bf.Deserialize (file); file.Close (); 

In regel 2 controleren we of een opgeslagen spelbestand bestaat. (Als dat niet het geval is, zal er natuurlijk niets te laden zijn.) In regel 3 maken we een BinaryFormatter op dezelfde manier als in de Opslaan functie. In regel 4 maken we een Bestandsstroom-maar deze keer zwemmen onze vissen stroomopwaarts van het bestand. Dus gebruiken we het dossier.Open, en wijs naar waar onze savedGames.gd bestaat hetzelfde Application.persistentDataPath draad. 

Regel 5 is een beetje compact, dus laten we het uitpakken: 

  • De bf.Deserialize (file) call vindt het bestand op de locatie die we hierboven hebben gespecificeerd en deserialiseert het. 
  • We kunnen niet zomaar Binary spugen bij Unity en verwachten dat het werkt, maar wij ook ons gedeserialiseerde bestand converteren (of casten) naar het gegevenstype dat we willen dat het is, wat in dit geval een is Lijst van het type Game. 
  • Vervolgens stellen we die lijst in als onze lijst met opgeslagen spellen. 

Tenslotte sluiten we in regel 6 dat bestand op dezelfde manier als in de Opslaan functie. 

Notitie: Het gegevenstype waarnaar u de gedeserialiseerde gegevens cast, kan veranderen afhankelijk van wat u nodig hebt. Bijvoorbeeld, Player.lives = (int) bf.Deserialize (bestand);.

Conclusie

Onze SaveLoad script is nu voltooid en ziet er als volgt uit:

gebruikmakend van UnityEngine; met behulp van System.Collections; met behulp van System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; met behulp van System.IO; openbare statische klasse SaveLoad public static List savedGames = nieuwe lijst(); // het is statisch, zodat we het vanaf elke plek kunnen oproepen openbare statische leegte Save () SaveLoad.savedGames.Add (Game.current); BinaryFormatter bf = new BinaryFormatter (); //Application.persistendDataPath is een tekenreeks, dus als u dat wilt, kunt u dat in debug.log plaatsen als u wilt weten waar de opslagspellen zich bevinden. FileStream-bestand = File.Create (Application.persistentDataPath + "/savedGames.gd"); // je kunt het alles noemen dat je wilt bf.Serialize (file, SaveLoad.savedGames); file.Close ();  public static void Load () if (File.Exists (Application.persistentDataPath + "/savedGames.gd")) BinaryFormatter bf = new BinaryFormatter (); FileStream-bestand = File.Open (Application.persistentDataPath + "/savedGames.gd", FileMode.Open); SaveLoad.savedGames = (lijst) Bf.Deserialize (file); file.Close ();  

Dat zijn de basisprincipes van opslaan en laden in Unity. In het bijgevoegde projectbestand vindt u enkele andere scripts die laten zien hoe ik deze functies aanroep en hoe ik de gegevens weergeef met behulp van de GUI van Unity.

Als je een voorsprong nodig hebt bij het ontwikkelen van je spel, probeer dan de Unity3D-sjablonen die beschikbaar zijn op Envato Market.