Unity(C#)初心者・入門者向けチュートリアル ひよこのたまご

AndroidやiOS向けアプリを簡単に作れるゲーム開発環境Unity(ユニティ)の使い方を、チュートリアル方式で一緒に学びましょう!

【Unity】JsonUtilityでJsonへシリアライズしたデータをローカルに保存する

Unity 2019.2.0f1 Personal(2019年8月)
今回はJsonテキストをローカルに保存していきます〜
JsonUtilityでクラスをJsonテキストに変換しローカルに保存したり、
逆に読み出して使用したりします。
※iOSやAndroidではなくUnity Editor環境での保存です。

Save機能

まずはデータの保存、Save機能を作成していきます〜
使いやすいstaticクラスで作成していきます。

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


public static class SaveDataManager
{
    const string SAVE_FILE = "save_data.json";
    const string DATA_DIR = "Assets/StreamingAssets/data/";
    static string saveDataPath;

    public static SaveData saveData;


    public static void Init()
    {
        saveDataPath = Path.Combine(DATA_DIR + SAVE_FILE);
    }


    public static void Save()
    {
        if (!Directory.Exists(DATA_DIR))
        {
            Directory.CreateDirectory(DATA_DIR);
        }

        var json = JsonUtility.ToJson(saveData);

        StreamWriter writer = new StreamWriter(saveDataPath, false);
        writer.WriteLine(json);
        writer.Flush();
        writer.Close();
    }


    [Serializable]
    public class SaveData
    {
        [SerializeField] private string name;
        public string Name { get { return name; } private set { name = value; } }
        [SerializeField] private int level;
        public int Level { get { return level; } private set { level = value; } }
        [SerializeField] private long money;
        public long Money { get { return money; } private set { money = value; } }

        public void SetName(string name) { Name = name; }
        public void SetLevel(int level) { Level = level; }
        public void SetMoney(long money) { Money = money; }

        public void Init()
        {
            Name = "名無し";
            Level = 1;
            Money = 500;
        }
    }
}

SaveDataManager.cs

InitメソッドのPath.Combineは、データ保存先のPathとファイル名を結合してくれます。
saveDataPathの中身は"Assets/StreamingAssets/data/save_data.json"となります。

一番下にあるSaveDataクラスがJsonにシリアライズされ保存されます。
[SerializeField]のついた変数name, level, moneyがJsonとなり、
プロパティName, Level, MoneyはJsonにはなりません。
プロパティのsetアクセサはprivateとなっているため
データ設定にはSet系のメソッドを経由する必要があります。

最後にSaveメソッドです。
DATA_DIR("Assets/StreamingAssets/data/")のディレクトリが存在しない場合、CreateDirectoryで作成してくれます。
JsonUtility.ToJsonで、メンバ変数にあるSaveDataクラスのデータをJsonに変換します。
データが用意されたので保存していきます。
StreamWriterオブジェクトはテキストの内容を保存する時に使われます。
第一引数に保存先のPathをテキスト名まで入れて指定します。
第二引数(bool)はappend(上書き)するかで、trueだと追記、falseだと上書きとなります。
セーブするたびに次の行へ追記されていっても仕方がないので、falseにします。
続いてStreamWriterのWriteLineメソッドで実際にデータの書き出しを行います。
WriteLineの時点ではデータをバッファに移しただけで、それを順次ファイルに書き出していきます。
StreamWriterのFlushメソッドを実行することで、バッファに残っているデータを全て書き出します。
最後にCloseメソッドでStreamWriterを閉じます。
Stream系のオブジェクトはちゃんとCloseしないとファイルが正常に保存されなかったりするので注意。

Load機能

続いて保存したデータを呼び出すLoad機能です。

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


public static class SaveDataManager
{
    const string SAVE_FILE = "save_data.json";
    const string DATA_DIR = "Assets/StreamingAssets/data/";
    static string saveDataPath;

    public static SaveData saveData;


    public static void Init()
    {
        saveDataPath = Path.Combine(DATA_DIR + SAVE_FILE);
// ***** 開始 *****
        Load();
// ***** 終了 *****
    }

// ***** 開始 *****
    public static void Load()
    {
        if (File.Exists(saveDataPath))
        {
            FileStream stream = File.Open(saveDataPath, FileMode.Open);
            StreamReader reader = new StreamReader(stream);
            var json = reader.ReadToEnd();
            reader.Close();
            stream.Close();

            saveData = JsonUtility.FromJson<SaveData>(json);
        }
        else
        {
            saveData = new SaveData();
            saveData.Init();
            Save();
        }
    }
// ***** 終了 *****

    public static void Save()
    {
        if (!Directory.Exists(DATA_DIR))
        {
            Directory.CreateDirectory(DATA_DIR);
        }

        var json = JsonUtility.ToJson(saveData);

        StreamWriter writer = new StreamWriter(saveDataPath, false);
        writer.WriteLine(json);
        writer.Flush();
        writer.Close();
    }


    [Serializable]
    public class SaveData
    {
        [SerializeField] private string name;
        public string Name { get { return name; } private set { name = value; } }
        [SerializeField] private int level;
        public int Level { get { return level; } private set { level = value; } }
        [SerializeField] private long money;
        public long Money { get { return money; } private set { money = value; } }

        public void SetName(string name) { Name = name; }
        public void SetLevel(int level) { Level = level; }
        public void SetMoney(long money) { Money = money; }

        public void Init()
        {
            Name = "名無し";
            Level = 1;
            Money = 500;
        }
    }
}

SaveDataManager.cs

Loadメソッドの最初ではsaveDataPathにファイルが存在するか判定しています。
存在していたらファイルを読み出しし、
存在していなければSaveDataクラスをnewし初期値を設定し、先ほどのSaveメソッドで保存します。

FileStreamはファイルを読み込む際に使うオブジェクトです。
FileStreamはFile.Openメソッドで作成します。
読み込みファイルのPathを第一引数に、FileModeを第二引数にとります。
FileMode.Openは既存のファイルを開く際に指定します。他にはファイルを新規作成して開くFileMode.Createなどあります。
続いて作成したFileStreamを引数にとりStreamReaderオブジェクトを作成し、
StreamReaderのReadToEndメソッドによりsaveDataPathにあるファイルのデータを全て読み出します。
使用したストリーム系オブジェクト(FileStreamとStreamReader)はCloseします。
最後にJsonUtility.FromJsonにより、Jsonテキストからクラスへ変換します。
ジェネリクスを指定します。

Initメソッドに今回作成したLoadメソッドを追記しておきます。

実行

別オブジェクトを作成しSaveDataManagerのInitメソッドを実行することで、
実際にsave_data.jsonファイルを作成しデータのセーブ/ロードを行います。

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

public class Manager : MonoBehaviour
{

    void Start()
    {
        SaveDataManager.Init();
        CheckStatus();
        SaveDataManager.saveData.SetName("たろう");
        SaveDataManager.saveData.SetLevel(100);
        SaveDataManager.saveData.SetMoney(10000000);
        SaveDataManager.Save();
        CheckStatus();
    }

    void CheckStatus()
    {
        Debug.Log("name:" + SaveDataManager.saveData.Name + " level:" + SaveDataManager.saveData.Level + " money:" + SaveDataManager.saveData.Money);
    }
}

Manager.cs

SaveDataManagerのInitメソッドを実行するとLoadメソッドが実行されます。
まだsave_data.jsonファイルは存在しないので、
SaveDataクラスを生成しSaveメソッドを実行します。
Saveメソッドを通してディレクトリが作成され、ローカルに保存されます。

セーブデータの更新はSaveDataManager.saveData(SaveDataクラス)を通して行います。
具体的にはSaveDataクラスのSet系メソッドです。
更新する値のセットが完了っしたらSaveメソッドを実行し、データを保存します。
f:id:hiyotama:20190829224449p:plain

ゲームモードを終了してもjsonファイルが残っていることが確認できます。
f:id:hiyotama:20190829224602p:plain

以上、JsonUtilityを使ったデータの保存についてでした。
ありがとうございました〜