Extending PlayerPrefs with JSON

PlayerPrefs is one of the most convenient tools included in Unity. A single line of code: PlayerPrefs.SetInt("player_level", 6); will successfully write a piece of data to disk on any platform you happen to find your code running on. The devs at Unity have done the device-specific work required to read and write bits for us. Neat.

But the out-of-the-box tools available to us in PlayerPrefs are extremely limited. Just three data types (int, string, and float) are allowed, meaning even such an obligatory datatype as Vector3 requires some sort of workaround. In the case of Vector3, either we have to clutter PlayerPref’s key-space with three SetFloat()s for a single Vector3 object’s xyz values, or we can use SetString() and define a string format that we must convert to and from each time we load that Vector3. Neither is particularly clean or manageable when applied to a game that requires the persistence of positions for many GameObjects.

A solution is to use the JSON format. If not for JSON’s inherent simplicity and human-readability then for the ubiquity of the format across programming languages, and therefore the availability of many free and powerful tools for reading and writing data into a JSON string. In fact, the entirety of your persistent data can be formatted into a single string (or split among a few strings if reading/writing starts to take up too much time on a device’s hard drive).

Having just three data types to work with in PlayerPrefs had seemed like a limitation. Using JSON, anything more than string now seems superfluous.

To begin, first download Json.NET, which is available on the project’s official webpage. If you chose direct download, you will be taken to a release page. Download the latest release and wait for a .zip file to appear on your computer.

Depending on which version of Unity you’re running and what version of .NET you’re using, you’ll want to select the appropriate .dll from inside the unzipped folder you just downloaded. As of writing this, Unity 2017.2 only allows .NET 4.6 as an “experimental” feature, and should be used with caution. It’s very likely you’ll just want to select the .dll for .NET 3.5. Drag it into your Unity project’s Plugins folder. You’re ready to go!

Any object you make in code can be serialized by marking it with the [Serializable] attribute above the class definition. This will allow all public and private fields in an object to be serialized, meaning that when you call a method from Json.NET like JsonConvert.SerializeObject(_data); it will be converted into a JSON string that saves all the values for fields in your _data object.

The object you serialize can be anything. I like to make a single struct called DataObject or whatever, and it might look like this:

public struct DataObject
public List<ScoreData> BestScoresClassic;
public List<ScoreData> BestScoresTimeAttack;
public GameMode CurrentGameMode;
public bool TimeAttackUnlocked;
public bool InvertY;
public int TotalObstaclesCleared;
public int TotalBalloonsPopped;
public int TotalTimesFlipped;
public Dictionary<string, int> Inventory;
public string SelectedBlueprintId;
public string SelectedPaperId;
public int CurrentTutorialStep;
public ScoreData NewestClassicScore;
public ScoreData NewestTimeAttackScore;
public string NewestItemId;

Notice that in addition to strings and ints, I’m also keeping track of enums, bools, Dictionaries, and even types I created myself, though keep in mind that in the example above, ScoreData must also be marked with [Serializable] for this to work as intended.

You can see an entire class called PlayerPrefsManager that I wrote for AntiFreemium’s first game, Airplane, on PasteBin. Hopefully it’s easy to follow, although I don’t recommend calling Save() every time a value is set if the amount of data being saved is any larger than this. Notice the InitializeData() method, it’s extremely useful if you’re finding yourself clearing your PlayerPrefs frequently during development.

The example PlayerPrefsManager I’ve provided is very specific to one game and yours will look very different, especially if your game is large. Some ideas you might want to consider for your project:

  • Break up your data into multiple JSON strings. If your data is very large, or if it doesn’t make sense to save/load all data at the same time, this could save resources and avoid frame drops by only serializing specific chunks of data a time. For instance, I also like to use JSON for configuration, and I’ll save the most recent config with its own key in PlayerPrefs.
  • Version your save data. Once you’ve been working on a project for a while, you might find that old data simply breaks your game, requiring you to wipe PlayerPrefs with each major change to what is being saved. Once your game is out in the world, you’ll want to implement a way to convert old versions to the latest version, so that players don’t have to start from scratch if they haven’t opened your app in a while.

Thanks for reading! I hope this has given you some inspiration for how to approach persistent data in your game. You can follow @antifreemium on Twitter or visit www.antifreemium.com for updates on future articles like this one, as well as news about free resources for game developers and projects from AntiFreemium.