この記事でのバージョン
Unity 2017.3.1f1
はじめに
今回は
「Unityで静的なデータを扱う際にScriptableObjectとJSONのどっちが優れてるの?!」
という、貴方(私)の疑問を解決出来るかもしれない記事です!
比較対象
まず比較対象についてです。
以下のようにfloat, int, bool, stringのListを持つ
Json用のクラスとScriptableObject用のクラスを作成、
[SerializeField] public class JsonClass { public List<float> FloatList = new List<float>(); public List<int> IntList = new List<int>(); public List<bool> BoolList = new List<bool>(); public List<string> StringList = new List<string>(); }
public class ScriptableObjectClass : ScriptableObject { public List<float> FloatList = new List<float>(); public List<int> IntList = new List<int>(); public List<bool> BoolList = new List<bool>(); public List<string> StringList = new List<string>(); }
同じ値をそれぞれのListに100万件ずつ設定し、比較対象にしました。
なお、JSONは無圧縮のものと圧縮(BASE64)したものの二つを用意しました。
//両データクラス作成 ScriptableObjectClass scriptableObjectClass = ScriptableObject.CreateInstance<ScriptableObjectClass>(); JsonClass jsonClass = new JsonClass(); //両データに同じ値を設定 for (int i = 0; i< 1000000; i++) { float floatValue = Random.Range(float.MinValue, float.MaxValue); scriptableObjectClass.FloatList.Add (floatValue); scriptableObjectClass.IntList.Add ((int) floatValue); scriptableObjectClass.BoolList.Add (floatValue > 0); scriptableObjectClass.StringList.Add(floatValue.ToString()); jsonClass.FloatList.Add (scriptableObjectClass.FloatList [i]); jsonClass.IntList.Add (scriptableObjectClass.IntList [i]); jsonClass.BoolList.Add (scriptableObjectClass.BoolList [i]); jsonClass.StringList.Add(scriptableObjectClass.StringList[i]); } //Json書き出し string json = JsonUtility.ToJson(jsonClass); string jsonPath = "Assets/Resources/JsonData.json"; File.WriteAllText(jsonPath, json); AssetDatabase.ImportAsset(jsonPath); //圧縮したJson書き出し string compressedJson = CompressString(json); string compressedJsonPath = "Assets/Resources/CompressedJsonData.json"; File.WriteAllText(compressedJsonPath, compressedJson); AssetDatabase.ImportAsset(compressedJsonPath); //ScriptableObject書き出し string scriptableObjectClassPath = "Assets/Resources/ScriptableObjectData.asset"; AssetDatabase.DeleteAsset(scriptableObjectClassPath); AssetDatabase.CreateAsset(scriptableObjectClass, scriptableObjectClassPath);
データサイズ
比較対象を作った所で、最初に比較するのはデータサイズです。
上記の通り、同じデータを設定して出力すると各データのサイズは以下の通りになりました。
データサイズ[MB] | |
---|---|
JSON(無圧縮) | 54.9 |
JSON(圧縮) | 19.4 |
ScriptableObject | 45.6 |
まずはJSON(圧縮) < ScriptableObject < JSON(無圧縮)という優劣です。
次にビルド後(Macのapp)のサイズの比較です。
どれか一つをResourcesディレクトリに入れてビルドしたものと、
比較用に全て入れずにビルドしたアプリのサイズは以下の通りでした。
アプリサイズ[MB] | |
---|---|
全てなし | 51 |
JSON(無圧縮) だけ | 105.9 |
JSON(圧縮) だけ | 70.4 |
ScriptableObjectだけ | 77.8 |
ビルドする際、JSONはそのままのサイズで追加されているのに対し、
ScriptableObjectは圧縮されて追加されているので、JSON(圧縮)との差は縮まりましたが、
それでもJSON(圧縮) < ScriptableObject < JSON(無圧縮)という優劣は変わりませんでした。
ロード時間
次に比較するのはロード時間です。
実際に使えるようになるまでの時間を比較しないと意味がないと思ったので、
ScriptableObjectはロード時間のみですが、JSON(無圧縮)はロードして変換するまでの時間、
JSON(圧縮)はロードして解凍し、変換するまでの時間で比較しています。
なお、処理時間の計測には以前紹介したProcessTimerを使いました。
//Jsonをロード、変換 ProcessTimer.Restart(); TextAsset json = Resources.Load<TextAsset>("JsonData"); JsonClass jsonClass = JsonUtility.FromJson<JsonClass>(json.text); Debug.Log(ProcessTimer.Stop()); //圧縮したJsonをロード、解凍、変換 ProcessTimer.Restart(); TextAsset compressedJsonDataJson = Resources.Load<TextAsset>("CompressedJsonData"); string decompressedJson = DecompressString(compressedJsonDataJson.text); JsonClass compressedJsonDataJsonClass = JsonUtility.FromJson<JsonClass>(decompressedJson); Debug.Log(ProcessTimer.Stop()); //ScriptableObjectをロード ProcessTimer.Restart(); ScriptableObjectClass scriptableObjectClass = Resources.Load<ScriptableObjectClass>("ScriptableObjectData"); Debug.Log(ProcessTimer.Stop());
一応Listに設定されてる全データの参照する時間も測ったのですが、
ほぼ同じだったので割愛します。
実際にUnityエディタ上で比較してみた結果が以下の通りです。
ロード時間[秒] | |
---|---|
JSON(無圧縮) | 0.69 |
JSON(圧縮) | 1.81 |
ScriptableObject | 3.03 |
まずはJSON(無圧縮) < JSON(圧縮) < ScriptableObjectという優劣です。
次にMac用にビルドしたアプリで比較してみた結果が以下の通りです。
ロード時間[秒] | |
---|---|
JSON(無圧縮) | 0.75 |
JSON(圧縮) | 1.01 |
ScriptableObject | 0.15 |
なんとエディタ上と実機上では優劣が逆転しています。
(と言うよりScriptableObjectのロード時間が減っている。)
他のプラットフォームでは試していないのですが、少なくともMac実機上では
ScriptableObject < JSON(無圧縮) < JSON(圧縮)という優劣でした。
メモリ使用量
最後に比較するのはメモリ使用量です。
同じ値を設定してるので、ロード後のメモリ使用量は何を使っても同じです。
ですよね……?(違ったら教えてください!)
ただしJSONの場合、一旦TextAssetを読み込んで変換する必要があるので、
TextAssetとJsonClassで二重にメモリを消費しているタイミングがあります。
//TextAssetの分、メモリ使用 TextAsset json = Resources.Load<TextAsset>("JsonData"); //TextAsset + JsonClassの分、メモリ使用 JsonClass jsonClass = JsonUtility.FromJson<JsonClass>(json.text); //TextAssetで使ってたメモリ解放(※たしかすぐには開放されなかったような……?) Resources.UnloadAsset(json);
おわりに
以上をざっくりまとめると以下のようになります。
データサイズ | ロード時間 | メモリ使用量 | |
---|---|---|---|
JSON(非圧縮) | ✕ | △ | △ |
JSON(圧縮) | ◯ | ✕ | △ |
ScriptableObject | △ | ◯ | ◯ |
よって、データサイズをとにかく小さくしたい場合は圧縮したJSONを使い、
それ以外の場合はScriptableObjectを使うと良いという結果になりました。
なお、あくまで自分の試した環境での話という事に注意が必要です。
Unityのバージョンやデータの内容、プラットフォームの違い等で結果は変わってくるかもしれないので、
厳密にこだわる必要がある場合は、実際に確かめてみる事をオススメします。