【Unity】JSON形式でファイルを読み書きする

こじゃら
こじゃら

スクリプトからファイルを読み書きするのって、思った以上に面倒だね~
簡単に出来る方法ってないの~?

このは
このは

そうね…Unity標準のシリアライズ機能を使えば簡単にできるけど、その中でも比較的お手軽なJsonUtilityを使ってJSON形式でデータを扱う方法を紹介するわ!

本記事では、Unity標準のシリアライザの一つであるJsonUtilityを用いて、スクリプト中のオブジェクトをファイルに書き出したり、ファイルから読み込んだりする方法をご紹介します。

これにより、僅か2、3行でデータの読み書き処理を記述できるようになります!

JsonUtilityとは

Unity5.3より追加されたUnity標準のJSONデータを扱うクラスです。

オブジェクトをJSON形式のテキストにシリアライズするメソッド、逆にJSON形式のテキストからオブジェクトにデシリアライズするメソッドがあります。

JSON形式にシリアライズする

スクリプト中で扱っているオブジェクトをJSON形式のテキストにシリアライズするには、JsonUtility.ToJson()メソッドを用います。

これは、次の形式からなるメソッドです。

public static string ToJson(object obj);
public static string ToJson(object obj, bool prettyPrint);

第1引数objには、シリアライズするオブジェクトを指定します。
第2引数prettyPrintは、trueの場合は改行やインデントありの整形されたJSON、falseの場合は最小限のデータサイズになるように改行やインデントが無いJSONになります。

1つ目のメソッドは、2つ目のメソッドの第2引数prettyPrintにfalseを指定した挙動と同じです。

JSON形式からデシリアライズする

JSON形式の文字列からオブジェクトにデシリアライズする場合は、JsonUtility.FromJson()メソッドを使います。メソッドの形式は以下の通りです。

public static T FromJson<T>(string json);

Tオブジェクト型を表すジェネリック型です。
引数jsonには、JSON形式の文字列を指定します。

戻り値には、T型にデシリアライズされたオブジェクトが返却されます。

メモリに優しいデシリアライズ

JsonUtility.FromJson()メソッドは、呼び出す毎に必ずヒープ領域を確保してしまいます。これを回避する方法として、JsonUtility.FromJsonOverwrite()メソッドが提供されています。

public static void FromJsonOverwrite(string json, object objectToOverwrite);

第1引数jsonには、JSON形式の文字列を指定します。
第2引数objectToOverwriteには、デシリアライズ結果を格納するオブジェクトを指定します。

デシリアライズが成功すると、objectToOverwriteの中身が直接デシリアライズした値で書き換えられます。
この際、新しくオブジェクトを生成しないため、ヒープ領域の確保が全く発生しません。

このは
このは

頻繁にJSON形式でデシリアライズしたい場合は、JsonUtility.FromJsonOverwrite()メソッドを使うことも視野に入れておくと良いわね!

JsonUtilityでファイルを読み書きしてみる

以上を踏まえて、実際にJSONデータとしてファイルに対して読み書きを行ってみたいと思います。
ゲームオブジェクトのワールド位置を読み書きするサンプルスクリプトです。

サンプル

JsonSerializationTest.cs

using System.IO;
using UnityEngine;

// JSON形式のデータ読み書きテスト
public class JsonSerializationTest : MonoBehaviour
{
    // 位置データ
    [System.Serializable]
    private struct PositionData
    {
        public Vector3 position;
    }

    // ファイルパス
    private string _dataPath;
    private void Awake()
    {
        // ファイルのパスを計算
        _dataPath = Path.Combine(Application.persistentDataPath, "position.json");
    }
    private void Update()
    {
        // 1キー押下で現在位置をセーブする
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            OnSave();
        }

        // 2キー押下で現在位置をロードする
        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            OnLoad();
        }

        // 方向キーで移動できるようにしておく
        transform.position = transform.position + new Vector3(
            Input.GetAxis("Horizontal"),
            Input.GetAxis("Vertical")) * Time.deltaTime;
    }

    // JSON形式にシリアライズしてセーブ
    private void OnSave()
    {
        // シリアライズするオブジェクトを作成
        var obj = new PositionData
        {
            position = transform.position
        };

        // JSON形式にシリアライズ
        var json = JsonUtility.ToJson(obj, false);

        // JSONデータをファイルに保存
        File.WriteAllText(_dataPath, json);
    }

    // JSON形式をロードしてデシリアライズ
    private void OnLoad()
    {
        // 念のためファイルの存在チェック
        if (!File.Exists(_dataPath)) return;

        // JSONデータとしてデータを読み込む
        var json = File.ReadAllText(_dataPath);

        // JSON形式からオブジェクトにデシリアライズ
        var obj = JsonUtility.FromJson<PositionData>(json);

        // Transformにオブジェクトのデータをセット
        transform.position = obj.position;
    }
}

このスクリプトを対象のゲームオブジェクトにアタッチして実行してみましょう。

実行結果

方向キーでオブジェクトを移動させ、「1」キーで現在位置をJSONファイルに保存、「2」キーでJSONファイルから現在位置を読み込んでTransformにセットする挙動になります。

なお、JSON形式で保存するファイルは、Application.persistentDataPathが指し示すディレクトリ配下にposition.jsonとして置くようになっています。
パスは各プラットフォームによって異なります。各プラットフォーム毎のパスは以下リファレンスをご覧ください。

position.jsonを開くと、以下のようにデータが格納されていることと思います。

{"position":{"x":-0.014371572993695736,"y":2.6874682903289797,"z":0.0}}

さいごに

今回は、Unity標準のJSONデータを扱うクラスであるJsonUtilityによるデータ読み書きの方法をご紹介させていただきました。

実は、Unity標準以外を視野に入れると、ほかにも様々な方法が存在します。
その中でも、比較的手軽にシリアライズ/デシリアライズが実現できるJsonUtilityは、機能に制限があるものの動作が高速な部類に入ります。

JSONというテキスト形式なので、デバッグもしやすいといったメリットも存在します。

ぜひ活用を検討してみてください!

参考サイト