パハットノート

※当サイトではGoogleアドセンス広告を利用しています

Unity Jsonファイルを利用したセーブ機能の実装



はじめに

  • Unityでのデータのセーブ方法で代表的なものとしてPlayerPrefsがあります。
  • これは、簡単に使えますが複雑なデータの保存にはあまり向いていません。
  • 本記事では、複雑なデータの保存にも適したJson形式で保存する方法を紹介します。





用意したスクリプト

JsonSavemanager(Jsonファイルの入出力を行うクラス)

  • ジェネリッククラスにして汎用性が高めました。
  • Application.persistentDataPathはセーブするデータのフォルダ名を示しています(処理系により異なる)。
  • Save関数でセーブします。
  • Load関数でセーブしたデータをロードできますが、セーブデータが存在しない場合はdefaultを返却します。

using UnityEngine;
using System.IO;

public static class JsonSaveManager<T>
{
    static string SavePath(string path)
        => $"{Application.persistentDataPath}/{path}.json";

    public static void Save(T data, string path)
    {
        using (StreamWriter sw = new StreamWriter(SavePath(path), false))
        {
            string jsonstr = JsonUtility.ToJson(data, true);
            sw.Write(jsonstr);
            sw.Flush();
        }
    }

    public static T Load(string path)
    {
        if (File.Exists(SavePath(path)))//データが存在する場合は返す
        {
            using (StreamReader sr = new StreamReader(SavePath(path)))
            {
                string datastr = sr.ReadToEnd();
                return JsonUtility.FromJson<T>(datastr);
            }
        }
        //存在しない場合はdefaultを返却
        return default;
    }
}



SaveControllerTemplate(呼び出し用クラス例)

  • 先ほど作成したクラス「JsonSaveManager」をゲーム実行中の適切なタイミングで呼び出すためのクラス例です。
  • ゲーム終了と同時にセーブするために、OnApplicationPause(bool isPaused)OnApplicationQuit()関数を使用しています。両方とも詳細はよくわかりませんが、前者はアンドロイドスマホ、後者はエディタ上での動作の時に必要でした。

using UnityEngine;

public class SaveControllerTemplate : MonoBehaviour
{
    public static SaveControllerTemplate I { get; private set; }

    string saveDataPath = "save-data";

    private void Awake()
    {
        I = this;
    }

    private void Start()
    {
        //セーブデータの読み込み
        T saveData = JsonSaveManager<T>.Load(saveDataPath);

        if (saveData == null)
        {
            //セーブデータが存在しない場合の処理
        }
        else
        {
            //セーブデータが存在する場合の処理
        }
    }

    private void OnApplicationPause(bool isPaused)//アンドロイドスマホではこっちが必要だった
    {
        if (isPaused)
        {
            OverWriteSaveData();
        }
    }

    private void OnApplicationQuit()//エディタ上で確認するときはこっちが必要だった
    {
        OverWriteSaveData();
    }

    //セーブデータの上書き
    void OverWriteSaveData()
    {
        //新たなセーブデータを作成処理
        T saveData = new T();

        //既存のセーブデータを上書き
        JsonSaveManager<TestSaveData>.Save(T, saveDataPath);
    }
}





使用例

用意するスクリプト

用意したスクリプトはシーン上の任意のゲームオブジェクトにアタッチします。

セーブしたいクラス(TestClass)

  • セーブしたいデータを格納したクラスがTestSaveDataです。
  • Json形式でデータを保存する変数はインスペクター上に表示されている必要があるため、変数notSaveは保存されません。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

[Serializable]
public class TestSaveData
{
    public int num;
    public string str;
    public Vector3 vec;
    string notSave = "セーブされない";
}


public class TestClass : MonoBehaviour
{
    public int num;
    public string str;
    public Vector3 vec;

    public void SetValue(TestSaveData saveData)
    {
        //値TestSaveDataからセット
        num = saveData.num;
        str = saveData.str;
        vec = saveData.vec;
    }

    public static TestClass I { get; private set; }
    private void Awake()
    {
        I = this;
    }
}


「SaveControllerTemplate」を書き換えたクラス(SaveController)

using UnityEngine;

public class SaveController : MonoBehaviour
{
    public static SaveController I { get; private set; }

    string testSaveDataPath = "test-save-data";

    private void Awake()
    {
        I = this;
    }

    private void Start()
    {
        TestSaveData saveData = JsonSaveManager<TestSaveData>.Load(testSaveDataPath);

        if (saveData == null)//セーブデータが存在しない場合は任意の値で初期化
        {
            //新たなセーブデータを作成
            saveData = new TestSaveData()
            {
                num = 3,
                str = "テスト",
                vec = Vector3.one
            };
        }

        TestClass.I.SetValue(saveData);
    }

    private void OnApplicationPause(bool isPaused)
    {
        if (isPaused)
        {
            OverWriteSaveData();
        }
    }

    private void OnApplicationQuit()//アプリケーション終了時に呼び出す
    {
        OverWriteSaveData();
    }

    //セーブデータの上書き
    void OverWriteSaveData()
    {
        TestSaveData testSaveData = new TestSaveData()
        {
            num = TestClass.I.num,
            str = TestClass.I.str,
            vec = TestClass.I.vec,
        };
        JsonSaveManager<TestSaveData>.Save(testSaveData, testSaveDataPath);
    }
}





実行結果の確認

まず、セーブデータがない状態で実行すると「TestClass」は次のようになる。
TestClassの初期状態

ゲームを中断するとJsonファイルが生成されており、中身を確認すると次のようになっている。確かに、変数notSaveは保存されていないことが確認できる。

{
    "num": 3,
    "str": "テスト",
    "vec": {
        "x": 1.0,
        "y": 1.0,
        "z": 1.0
    }
}


次にこの状態からゲームを再実行して「TestClass」を次のように書き換えると、jsonファイルの中身も変化することが確認できる。
書き換え

{
    "num": -1,
    "str": "書き換え",
    "vec": {
        "x": 2.0,
        "y": -1.0,
        "z": 5.0
    }
}


再実行すると「TestClass」に先ほどと同じ値が格納される。
再実行後




関連記事(Editor Windowの保存)

kiironomidori.hatenablog.com