【Unity】Unity Save & Load: Persisting Game Data with JSON Serialization

Created: 2025-12-07

Save functionality is essential for preserving player progress. Go beyond PlayerPrefs limitations and learn how to save and load complex game data to files using JSON format and Unity's JsonUtility class, with practical C# code examples.

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 public fields are serialized. private fields and properties are ignored.
  • JsonUtility cannot directly serialize some complex types like multidimensional arrays or Dictionary. List is 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 with File.WriteAllText().
  • Load: Read JSON string with File.ReadAllText(), restore to data class instance with JsonUtility.FromJson<T>().
  • Use Application.persistentDataPath for 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.