Load and Defaults
Part of the Game Architecture in Unity section of Coddy's Unity journey. Lesson 32 of 64.
Loading has to cope with saves that are not there or not complete. A racing game saves a RaceSave; version 2 of the game added musicVolume:
[System.Serializable]
public class RaceSave
{
public int trophies;
public string car = "Rookie Kart";
public float musicVolume = 0.8f;
}RaceSave save;
if (File.Exists(path))
{
save = JsonUtility.FromJson<RaceSave>(File.ReadAllText(path));
}
else
{
save = new RaceSave();
}A new player has no file, and File.ReadAllText on a missing file throws FileNotFoundException, so check File.Exists first and start from new RaceSave(): 0 trophies, the Rookie Kart and volume 0.8, the field initializers. An old player's version 1 file holds {"trophies":3,"car":"Red Racer"}. FromJson makes a new RaceSave, which runs the field initializers, and then sets only the keys the JSON has, so musicVolume is 0.8. A key the class has no field for is ignored. A new version of the game can therefore read old saves, as long as every new field has an initializer with a sensible value.
JsonUtility.FromJsonOverwrite(json, obj) fills an object that already exists instead of making one, and leaves every field the JSON lacks as it was. FromJson cannot make a MonoBehaviour or a ScriptableObject (Unity throws Cannot deserialize JSON to new instances of type 'RaceSettings.'), so a settings asset is loaded by overwriting it: JsonUtility.FromJsonOverwrite(json, settings);. Overwriting also keeps the values an object already holds, such as the ones the Inspector gave it, where FromJson would bring back the class's initializers.
Challenge
MediumLoad pet.json safely for new players and old saves.
- In
Start(), replace the FromJson line withif (File.Exists(path))and anelse. - With a file, fill the Inspector's pet:
JsonUtility.FromJsonOverwrite(File.ReadAllText(path), pet);, then logWelcome back, Mochifrompet.petName. - Without one, log
A new pet: Biscuitthe same way.
Press Play, feed the pet, then Stop and Play: it says welcome back. Then Check.
Try it yourself
using System.IO;
using UnityEngine;
using UnityEngine.InputSystem;
using TMPro;
public class PetCare : MonoBehaviour
{
public PetSave pet;
public TMP_Text petText;
private string path;
void Start()
{
path = Path.Combine(Application.persistentDataPath, "pet.json");
pet = JsonUtility.FromJson<PetSave>(File.ReadAllText(path));
Show();
}
void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
pet.hunger = Mathf.Max(pet.hunger - 10, 0);
pet.happiness = Mathf.Min(pet.happiness + 5, 100);
File.WriteAllText(path, JsonUtility.ToJson(pet));
Show();
}
}
void Show()
{
petText.text = pet.petName + ": hunger " + pet.hunger + ", happiness " + pet.happiness;
}
}
All lessons in Game Architecture in Unity
1Components, Not Hierarchies
Small ComponentsA Base Enemy ScriptAbstract ComponentsComponents Working TogetherRecap: Modular TurretRecap: Twin Cannon4Serialized Data
Serializable ClassesLists in the InspectorEnums in the InspectorRecap: Loot TableRecap: Roll the Loot2Interfaces in Unity
IDamageableInterfaces and TryGetComponentGetComponentsRecap: Interact KeyRecap: Nearest in ReachPractice on your own: Unity playground