Overview
Player level, inventory items, cleared stages, various settings—save & load functionality for preserving game progress and restoring it in future sessions is essential for most games. Unity has PlayerPrefs for easily saving simple data, but it's designed for small key-value pair data (player names, volume settings) and isn't suitable for saving complex game states (item lists, character stats).
For building more flexible and powerful save systems, JSON (JavaScript Object Notation) format data serialization is widely used.
- Serialization: Converting in-memory data like C# classes and objects into a format that can be saved to files or transmitted over networks (in this case, JSON strings).
- Deserialization: Restoring JSON strings back to original C# classes and objects.
Unity provides the built-in JsonUtility API for easy JSON serialization. This article covers the basics of saving and loading game data as JSON files using JsonUtility.
Step 1: Define the Data Class to Save
First, create a C# class defining the data structure you want to save. The crucial point is adding the [System.Serializable] attribute to this class. Classes without this attribute cannot be serialized with JsonUtility.
using System.Collections.Generic;
// This attribute is required!
[System.Serializable]
public class GameData
{
public int level;
public float currentHealth;
public Vector3 playerPosition;
public List<string> inventoryItems;
// Constructor sets initial values
public GameData()
{
this.level = 1;
this.currentHealth = 100f;
this.playerPosition = Vector3.zero;
this.inventoryItems = new List<string>();
}
}
Important notes:
- Only
publicfields are serialized.privatefields and properties are ignored. JsonUtilitycannot directly serialize some complex types like multidimensional arrays orDictionary.Listis supported.
Step 2: Serialize Data to JSON and Save
Next, store current game state in a GameData instance, convert it to a JSON string with JsonUtility.ToJson(), and write it to a file.
using UnityEngine;
using System.IO; // Required for file operations
public class SaveLoadManager : MonoBehaviour
{
private string saveFilePath;
private GameData gameData;
void Awake()
{
// Determine save file path
// Application.persistentDataPath points to a platform-safe persistent directory with write permission
saveFilePath = Path.Combine(Application.persistentDataPath, "gamedata.json");
gameData = new GameData();
}
public void SaveGame()
{
// --- Reflect current game state into gameData object here ---
// Example:
// gameData.level = FindObjectOfType<GameManager>().currentLevel;
// gameData.playerPosition = FindObjectOfType<PlayerController>().transform.position;
// ----------------------------------------------------------
// Convert GameData object to JSON string
// Setting second argument to true formats for human readability (pretty print)
string json = JsonUtility.ToJson(gameData, true);
// Write JSON string to file
File.WriteAllText(saveFilePath, json);
Debug.Log("Save successful! Path: " + saveFilePath);
}
}
Application.persistentDataPath points to a safe location (like the user's documents folder) that persists even after uninstallation—ideal for save data.
Step 3: Load and Deserialize JSON File
When the game starts or a load button is pressed, read the saved JSON file and restore it to a GameData object with JsonUtility.FromJson().
// Continuation of SaveLoadManager class
public void LoadGame()
{
// Check if save file exists
if (File.Exists(saveFilePath))
{
// Read JSON string from file
string json = File.ReadAllText(saveFilePath);
// Restore from JSON string to GameData object
gameData = JsonUtility.FromJson<GameData>(json);
// --- Apply restored data to game here ---
// Example:
// FindObjectOfType<GameManager>().currentLevel = gameData.level;
// FindObjectOfType<PlayerController>().transform.position = gameData.playerPosition;
// --------------------------------------------------
Debug.Log("Load successful!");
}
else
{
Debug.LogWarning("Save file not found. Starting new game.");
// Process for starting a new game
gameData = new GameData();
}
}
The FromJson<T>() method creates an object of type T and overwrites its fields based on the JSON data.
Summary
Save & load systems using JsonUtility enable far more flexible and extensible data management than PlayerPrefs.
- Create a class bundling data to save and add the
[System.Serializable]attribute. - Save: Convert data class instance to JSON string with
JsonUtility.ToJson(), save to file withFile.WriteAllText(). - Load: Read JSON string with
File.ReadAllText(), restore to data class instance withJsonUtility.FromJson<T>(). - Use
Application.persistentDataPathfor save file location—it's safe and reliable.
Building on these basics, you can implement advanced features like managing multiple save slots or encrypting save data to prevent cheating.