Documentation
Features
πŸ’Ύ SaveData

πŸ’Ύ SaveData


What is SaveData

  • A container that contains all saved player data
  • A list of ISaveData objects that gets serialized and saved

Advantages of SaveData

  • Uniform way of accessing and saving any player data
  • No more manual PlayerPref name mapping for every single value
  • Supports multiple SaveSlots
  • View all the SaveData through the Debug Menu
  • Versioning and migration support

How to create SaveData

Implement ISaveData to make it automatically accessible and configurable through the inspector, as simple as that:

PlayerSaveData.cs
using GameSuite;
 
namespace Game
{
	public class PlayerSaveData : ISaveData
	{
		public int Lives;
 
		public int Version => 1;
 
		public void Migrate(int toVersion, JToken jsonToken)
		{
 
		}
	}
}

How to access SaveData

Use the SaveDataService or the shortcut SaveData to access the player SaveData.

int lives = SaveData.Get<PlayerSaveData>().Lives;

SaveDataObject

This contains a list with all ISaveData objects inside and can be retrieved from the SaveDataService like so:

SaveDataObject saveDataObject = Services.Get<SaveDataService>().PlayerSaveData;

Observables

It is highly advised to use ObservableValue, ObservableList, ObservableDictionary to store the data in SaveData as it allows any script to subscribe to changes being made. For example when receiving coins, animate the coins flying up to the text and animate the old number to the new value. Read the Observables chapter to learn more.

InitialSaveData

Add the InitialSaveConfig to the project in order to set any default SaveData.


Initial Save Data
ℹ️

New SaveData
Adding any new SaveData to the InitialSaveConfig, which does not yet exist in the current saved player SaveData on device, will get added to the existing player SaveData with the values set in the InitialSaveConfig.

ℹ️

AB Testing
AB Testing can be used to have multiple InitialSaveConfigs and have different inventory for different players.

ℹ️

Remote Configs
Remote Configs can be used to change specific InitialSaveConfig variables.

Save Slots

There are an infinite amount of SaveSlots available for the players to use.

int saveSlot = 3;
Services.Get<SaveDateService>().Load(saveSlot);
ℹ️

Slot lifetime
When a slot is set, the game will keep using that slot to save and load until told otherwise.

Data Migration

When making changes to the format of SaveData format, changes to the saved JSON might be necessary as well to not break deserialization.

Example time: let's have a SaveData file with a string variable.

PlayerSaveData.cs
using Newtonsoft.Json.Linq;
using GameSuite;
 
namespace Game
{
	public class PlayerSaveData : ISaveData
	{
		public ObservableValue<string> WrongVariableName = new ObservableValue<string>();
 
		public int Version => 1;
 
		public void Migrate(int toVersion, JToken jsonToken)
		{
            
        }
	}

Now in order to rename the WrongVariableName to the CorrectVariableName, all we need to do is bump the version by 1 in this particular SaveData file and add the migration on the Json Token like so:

PlayerSaveData.cs
using Newtonsoft.Json.Linq;
using GameSuite;
 
namespace Game
{
	public class PlayerSaveData : ISaveData
	{
		public ObservableValue<string> CorrectVariableName = new ObservableValue<string>();
 
		public int Version => 2;
 
		public void Migrate(int toVersion, JToken jsonToken)
		{
            switch (toVersion)
            {
				case 2:
					jsonToken["CorrectVariableName"] = jsonToken["WrongVariableName"];
					break;
            }
        }
	}

This is of course a simple example to manipulate the data, but it is possible to do almost anything on the jsonToken as it is the JSON itself and it is possible to do a complete overhaul or transformation of any data in there.

When upgrading multiple versions, the method gets called for every intermediate version. So if the saved version is 2 and the new version is 5, Migrate() will get called for 3, 4 and 5 back to back.

ℹ️

Migration per file
Migration only has to be done on a specific SaveData class level, as the version and migration is handled per SaveData class, each file has its own version number. Meaning bumping PlayerSaveData does not require any action or manipulation on any other SaveData files.

ℹ️

Adding new values
Adding any new data with a default value set will work without needing to add a new Version and data Manipulation. The new data will get the default assigned value. So there is no need to add a Migrate data just to set the default value of a variable. Only when changing/renaming existing SaveData variables it is required to add Manipulation and bump the Version number of the file.

⚠️

Backwards compatibility
When adding SaveData Migration, leave the switch case in the SaveData script for all time to support any older version to be upgraded to the new standard. It is ok to have 100 manipulations to the data, as long as the new version number always increments and never goes down between builds.

Of course this matters the most once a build has been released to the public, just be warned that the SaveData Json deserialization might not work and break the build if not properly migrated. In this case a fresh install should to be made for the SaveData format to be correct. Thats why it's a good practice to always bump the version and migrate the old data to the new supported format.

Manual Manipulation

To manually save the entire SaveData to a json, use this code:

SaveDataService saveDataService = Services.Get<SaveDataService>();
SaveDataObject playerSaveData = saveDataService.PlayerSaveData;
string json = saveDataService.Serialize(playerSaveData);

When writing the json into the SaveData:

SaveDataService saveDataService = Services.Get<SaveDataService>();
SaveDataObject playerSaveData = saveDataService.Deserialize(json);
saveDataService.Sync(playerSaveData);

It is also possible to sync only a single piece of the SaveData collection:

PlayerSaveData playerSaveData;
saveDateService.Sync(playerSaveData);

Manual Data Migration

When needing to do a manual data migration of a JSON, like in the case of receiving or caching multiplayer SaveDataObjects which is are not yet upgraded to the latest version, use the SaveDataMigrator. This will return a JSON that can be safely deserialized to a SaveDataObject.

string migratedJson = SaveDataMigrator.Migrate(json);

Debug Menu

Go to Debug Menu > Game State > SaveData to view all the SaveData of the current session.


Debug Menu SaveData

Save Slots can also be created and switched from the Debug Menu.


Debug Menu Save Slots