Unityで永続させたいデータを取り扱う(ファイルの読み書き)
Unityで永続させたいデータがあり、どう取り扱えば良いのか調べた時のメモ。
UnityにはPlayerPrefsという便利なクラスがあり、こいつを使えば整数だとか文字列だとか真偽値だとか簡単なデータを読み/書きすることができます。
しかも、データは保存されて再起動しても残ってるし、データの読み書き簡単な操作で行えるのでとても便利。
だが、どうせならもうちょっと大きいデータ(それこそゲーム内で使うデータをクラス丸ごと、など)保存したいところ。
さらに、クラスに依存せずに汎用的に取り扱えるメソッドが良かったので、そういうのを実際に作ってみました。
どこに保存するか
Unityのドキュメントをしらべたりしたところ、下記のプロパティがあるようでした。
Application.persistentDataPath
これはアプリケーションが永続させたいデータの置き場のディレクトリを絶対パスで教えてくれるもの。
しかもプラットフォーム間で処理を変更する必要は無く、iOSで実行すればiOS用の、Androidで実行すればAndroid用のディレクトリを教えてくれるようです。
とても便利っぽそうなので、これを使えばうまくいきそうです。
なにを保存するか
次に、データをどうやって保存するか、です。
クラスを丸ごと保存するならxmlかjsonにして保存しちゃうのが手っ取り早いかな?ということで、もともと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
と固定でしたが、保存するクラスによって分けると良さそうです。