ことばアルバム

にわかエンジニアのにわか備忘録

Unityで永続させたいデータを取り扱う(ファイルの読み書き)

Unityで永続させたいデータがあり、どう取り扱えば良いのか調べた時のメモ。
 
UnityにはPlayerPrefsという便利なクラスがあり、こいつを使えば整数だとか文字列だとか真偽値だとか簡単なデータを読み/書きすることができます。
しかも、データは保存されて再起動しても残ってるし、データの読み書き簡単な操作で行えるのでとても便利。
 
だが、どうせならもうちょっと大きいデータ(それこそゲーム内で使うデータをクラス丸ごと、など)保存したいところ。
さらに、クラスに依存せずに汎用的に取り扱えるメソッドが良かったので、そういうのを実際に作ってみました。


どこに保存するか

Unityのドキュメントをしらべたりしたところ、下記のプロパティがあるようでした。

Application.persistentDataPath

これはアプリケーションが永続させたいデータの置き場のディレクトリを絶対パスで教えてくれるもの。
しかもプラットフォーム間で処理を変更する必要は無く、iOSで実行すればiOS用の、Androidで実行すればAndroid用のディレクトリを教えてくれるようです。
とても便利っぽそうなので、これを使えばうまくいきそうです。
 

なにを保存するか

次に、データをどうやって保存するか、です。
クラスを丸ごと保存するならxmljsonにして保存しちゃうのが手っ取り早いかな?ということで、もともとJavaScriptを仕事で使ってる事もありjson形式にすることにしました。
 
つぎに、何を使ってjsonを取り扱うかなのですが、軽く調べたところ、MiniJSONってやつかJsonFxの2択っぽい感じだったので、とくに理由はないですがJsonFxを使う事にしました。

JsonFx.Json.JsonWriter.Serialize(data);

↑こんな感じでシリアライズできて、

JsonFx.Json.JsonReader.Deserialize<T>(json);

↑こんな感じにすればjsonをクラスにしてくれるらしい。

 

実装

ここまででなんとか実装できそうだったので、実際に汎用的な入出力をサポートするスクリプトを実装しました。
 
まずは、今回試しに使うデータのクラス

/**
 * データ保存用のクラス
 */
public class OriginalData
{
    public int id;
    public string name;
}

 
つぎに、書き込み用のメソッド

/**
 * 書き込みます
 */
void Write<T>(T data)where T:class
{
    
    if (!Directory.Exists(storagePath))
    {
        Directory.CreateDirectory(storagePath);
    }

    string path = storagePath + "/original_data";
    string json = JsonFx.Json.JsonWriter.Serialize(data);

    File.WriteAllText(path, json, System.Text.Encoding.UTF8);
}

コードの解説をすると、
まずWrite()メソッドを呼ぶ際には特定のクラスでなく、どのクラスのデータも書き込めるようにします。
次に保存用のディレクトリが存在するかを調べ、なければディレクトリを作成します。
続いて保存するファイルを指定し、データをjsonにしてファイルに書き込みます。
補足する事として、保存するファイルをExists()で調べていないのは、ファイルが無ければFile.WriteAllText()が勝手に作ってくれるので気にせずに大丈夫だからです。
説明用でtry-catchを省きましたが、本来はするべきです。
 
次に、読み取り用のメソッド。

/**
 * 読み取ります
 */
T Read<T>()where T:class
{
    string path = storagePath + "/original_data";
    string json = File.ReadAllText(path, System.Text.Encoding.UTF8);
    return JsonFx.Json.JsonReader.Deserialize<T>(json);
}

こちらもファイルのパスを取得し、ReadAllText()でデータを読み込み、JsonFxを使ってクラスのデータに戻しています。
これもtry-catchは本来すべきです。
 
ここまでで読み/書きをサポートしてくれるメソッドが出来上がりましたので、実際にこれを利用する処理を書いていきます。

storagePath = Application.persistentDataPath + "/storage";

// サンプルデータ
OriginalData data = new OriginalData();
data.id = 10;
data.name = "sample-text";

// セーブ
Write(data);

// ロード
OriginalData result = Read<OriginalData>();
Debug.Log(result.name); // 出力

これを実際に実行すると、ちゃんとコンソールにsample-textが出力されました。
一個こういうのを作っとけば、後から保存したいデータをどんどん増やせしていけるので便利ですね。
今回、ファイル名はoriginal_dataと固定でしたが、保存するクラスによって分けると良さそうです。