【Unity】Unityセーブ&ロード実装:JSONとSerializationでゲームデータを永続化する

作成: 2025-12-07

プレイヤーの進捗を保存するセーブ機能はゲームに不可欠。PlayerPrefsの限界を超え、JSON形式とUnityのJsonUtilityクラスを使って、複雑なゲームデータをファイルに保存・読込する方法を、具体的なC#コード例と共に解説します。

概要

プレイヤーのレベル、所持アイテム、クリアしたステージ、各種設定など、ゲームの進行状況を保存し、次回プレイ時に復元するセーブ&ロード機能は、多くのゲームにとって不可欠な要素です。Unityには、簡単なデータを手軽に保存するためのPlayerPrefsという機能がありますが、これはキーと値のペアで少量のデータ(プレイヤー名や音量設定など)を保存するのには向いていますが、複雑なゲーム状態(アイテムリストやキャラクターのステータスなど)を丸ごと保存するには不向きです。

より柔軟で強力なセーブシステムを構築するために広く使われているのが、JSON (JavaScript Object Notation) 形式を利用したデータのシリアライズ (Serialization) です。

  • シリアライズ: C#のクラスやオブジェクトといったメモリ上のデータを、ファイルに保存したりネットワークで送信したりできる形式(この場合はJSON文字列)に変換すること。
  • デシリアライズ: JSON文字列を、元のC#クラスやオブジェクトの形に復元すること。

Unityには、このJSONシリアライズを簡単に行うための組み込みAPI JsonUtility が用意されています。この記事では、JsonUtilityを使ってゲームデータをJSONファイルとして保存・ロードする基本的な方法を解説します。

Step 1: 保存するデータクラスを定義する

まず、セーブしたいデータ構造を定義したC#のクラスを作成します。重要なのは、このクラスに [System.Serializable] 属性を付けることです。この属性がないクラスは、JsonUtilityでシリアライズすることができません。

using System.Collections.Generic;

// この属性が必須!
[System.Serializable]
public class GameData
{
    public int level;
    public float currentHealth;
    public Vector3 playerPosition;
    public List<string> inventoryItems;

    // コンストラクタで初期値を設定
    public GameData()
    {
        this.level = 1;
        this.currentHealth = 100f;
        this.playerPosition = Vector3.zero;
        this.inventoryItems = new List<string>();
    }
}

注意点:

  • シリアライズ対象となるのは、publicなフィールドのみです。privateなフィールドやプロパティは無視されます。
  • JsonUtilityは、多次元配列やDictionaryなど、一部の複雑な型を直接シリアライズできないという制限があります。Listはサポートされています。

Step 2: データをJSONにシリアライズして保存する

次に、ゲーム内の現在の状態をGameDataクラスのインスタンスに格納し、それをJsonUtility.ToJson()でJSON文字列に変換してファイルに書き出します。

using UnityEngine;
using System.IO; // ファイル操作のために必要

public class SaveLoadManager : MonoBehaviour
{
    private string saveFilePath;
    private GameData gameData;

    void Awake()
    {
        // セーブファイルのパスを決定
        // Application.persistentDataPathは、各プラットフォームで安全に書き込みが許可されている永続的なディレクトリを指す
        saveFilePath = Path.Combine(Application.persistentDataPath, "gamedata.json");

        gameData = new GameData();
    }

    public void SaveGame()
    {
        // --- ここで現在のゲーム状態をgameDataオブジェクトに反映させる ---
        // 例:
        // gameData.level = FindObjectOfType<GameManager>().currentLevel;
        // gameData.playerPosition = FindObjectOfType<PlayerController>().transform.position;
        // ----------------------------------------------------------

        // GameDataオブジェクトをJSON文字列に変換
        // 第2引数をtrueにすると、人間が読みやすいように整形(pretty print)される
        string json = JsonUtility.ToJson(gameData, true);

        // JSON文字列をファイルに書き込む
        File.WriteAllText(saveFilePath, json);

        Debug.Log("Save successful! Path: " + saveFilePath);
    }
}

Application.persistentDataPathは、ユーザーのドキュメントフォルダ内など、アンインストールしても消えない安全な場所を指すため、セーブデータの保存場所として最適です。

Step 3: JSONファイルを読み込んでデシリアライズする

ゲーム開始時やロードボタンが押された時に、保存したJSONファイルを読み込み、JsonUtility.FromJson()GameDataオブジェクトに復元します。

// SaveLoadManagerクラスの続き

public void LoadGame()
{
    // セーブファイルが存在するか確認
    if (File.Exists(saveFilePath))
    {
        // ファイルからJSON文字列を読み込む
        string json = File.ReadAllText(saveFilePath);

        // JSON文字列からGameDataオブジェクトに復元
        gameData = JsonUtility.FromJson<GameData>(json);

        // --- ここで復元したデータをゲームに反映させる ---
        // 例:
        // 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.");
        // 新しいゲームを開始するための処理
        gameData = new GameData();
    }
}

FromJson<T>()メソッドは、指定した型Tのオブジェクトを生成し、JSONデータに基づいてそのフィールドを上書きします。

まとめ

JsonUtilityを使ったセーブ&ロードシステムは、PlayerPrefsよりもはるかに柔軟で拡張性の高いデータ管理を可能にします。

  • 保存したいデータをまとめたクラスを作成し、[System.Serializable]属性を付ける。
  • セーブ時: データクラスのインスタンスをJsonUtility.ToJson()でJSON文字列に変換し、File.WriteAllText()でファイルに保存する。
  • ロード時: File.ReadAllText()でJSON文字列を読み込み、JsonUtility.FromJson<T>()でデータクラスのインスタンスに復元する。
  • セーブファイルの保存場所には、Application.persistentDataPathを使用するのが安全で確実。

この基本的な仕組みを応用すれば、複数のセーブスロットを管理したり、セーブデータを暗号化してチートを防いだりといった、より高度な機能も実装していくことができます。