ゲーム中のデータをJsonとして簡単に扱う方法を知りたいの。
JsonUtilityクラスを使えばこの辺が楽に実現できるわ。
JsonUtilityクラスはUnity標準のJSONデータ操作用のユーティリティクラスです。
プログラム上のオブジェクトをJSON形式の文字列にシリアライズしたり、逆にJSON形式の文字列からオブジェクトにデシリアライズする機能を有します。
参考:JsonUtility – Unity スクリプトリファレンス
これにより、例えば次のようなことが手軽に実現できるようになります。
- JSON形式のセーブデータとして読み書きする
- JSON形式でサーバーとデータのやり取りをする
特に後者は、様々なサービスのAPIでもよく採用される方法と言えるでしょう。
また、JsonUtilityクラスのシリアライズ・デシリアライズでは、内部的にUnityのシリアライザが用いられており、シリアライズ可能な条件や挙動などもこれに準拠します。
本記事では、このようなJsonUtilityクラスの機能の使い方を解説するとともに、内部挙動や使用上の注意点についても触れます。
- Unity 2022.1.7f1
目次 非表示
JSON形式にシリアライズする
JsonUtility.ToJsonメソッドを用います。
2種類のオーバーロードされたメソッドが用意されています。
第1引数objは共通で、シリアライズ対象のオブジェクトを指定します。
2つ目のメソッドの第2引数prettyPrintは、trueなら改行やインデントが有効、falseなら無効になります。
第2引数が指定されない場合、prettyPrintにfalseが指定されたのと同じ挙動となります。
参考:JsonUtility-ToJson – Unity スクリプトリファレンス
サンプルスクリプト
以下、自分自身のクラスをJSON文字列に変換してログ出力するサンプルスクリプトです。
上記スクリプトをToJsonExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチすると機能するようになります。
例では以下のようにインスペクターに適当な値を設定することとします。
実行結果
以下のようにスクリプトのフィールドの内容がJSON出力されます。
上記のrotationフィールドはインスペクター上ではオイラー角で指定するようになっていましたが、JSONにシリアライズされるとクォータニオンの要素として出力されていることが分かります。
スクリプトの解説
以下部分で自分自身のインスタンスをJSON形式にシリアライズした文字列を取得しています。
例では第2引数にtrueを指定することでインデントと改行されたJSON文字列を取得していますが、ここをfalseにすると以下のようにインデントと改行の無いJSON文字列が得られます。
データ容量の削減ができるため、ファイル保存やサーバー通信など用途で活用すると良いでしょう。
JSON文字列をオブジェクトにデシリアライズする
2つのメソッドが提供されています。
第1引数にはデシリアライズされるJSON形式の文字列を指定します。
デシリアライズされたオブジェクトは、FromJsonメソッドは新しいインスタンスを返し、FromJsonOverwriteメソッドは第2引数に指定されたオブジェクトに上書きする形で返します。
後者のFromJsonOverwriteメソッドは、MonoBehaviour継承クラスなど直接インスタンス化できないものや、インスタンス化されることによって発生するGC.Allocを抑えたい場面などで役立ちます。
参考:JsonUtility-FromJson – Unity スクリプトリファレンス
参考:JsonUtility-FromJsonOverwrite – Unity スクリプトリファレンス
サンプルスクリプト
以下、JSON文字列をデシリアライズしてログ出力するサンプルスクリプトです。
スクリプトの内容をFromJsonExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチすると機能します。
実行結果
フィールドにデシリアライズされた値が格納されていることが確認できました。
スクリプトの解説
以下部分でJSON形式の文字列をクラス型に変換しています。
シリアライズ・デシリアライズ時の内部挙動
JsonUtilityクラスは内部ではUnityのシリアライザが用いられています。
そのため、シリアライズされるフィールドはUnityシリアライザのシリアライズ条件に準拠します。
フィールドがシリアライズ条件は以下の通りです。
- publicか[SerializeField]属性が付加されている
- staticでない
- constでない
- readonlyでない
- シリアライズ可能な型である
最後の条件であるシリアライズ可能な型は以下の通りです。
- int、float、stringなどのプリミティブ型
- Enum型
- Vector2、AnimationCurveなどの特定のUnityビルトイン型
- [Serializable]属性の付いた独自classやstruct
- 配列やリスト(List<T>)など
詳細な条件は以下リファレンスのページをご覧ください。
具体例など含めたUnityシリアライザの挙動については以下記事でも解説していますので、必要ならご覧ください。
使用上の注意点
いくつか嵌ると思われる落とし穴について紹介しておきます。
structなどの値型はボックス化される
JsonUtility.ToJsonメソッドやJsonUtility.FromJsonなどでやり取りする際は、シリアライズ対象のオブジェクトをobject型にキャストする必要があります。
そのため、structなどの値型に対してシリアライズ・デシリアライズするとボックス化が発生し、GC.Allocが生じます。
頻繁な呼び出しを行わなければ問題ないと思われますが、留意しておくと安心かもしれません。
MonoBehaviourをデシリアライズするときはFromJsonOverwriteメソッドを使う
MonoBehaviourインスタンスをシリアライズするときはJsonUtility.ToJsonメソッドで問題なく行えますが、デシリアライズするときはJsonUtility.FromJsonOverwriteメソッドを使う必要があります。
JsonUtility.FromJsonメソッドを使ってデシリアライズしようとした場合、MonoBehaviourクラスのインスタンスを直接作成しようとしてしまうために次のようなエラーが発生します。
エラーとなるコード例
出力結果
そのため、MonoBehaviourインスタンスをデシリアライズしたい場合、GameObject.AddComponentメソッドなどで一度インスタンス化してから、JsonUtility.FromJsonOverwriteメソッドで上書きでデシリアライズする必要があります。
正しい例
パフォーマンスについて
JsonUtilityクラスはUnityシリアライザを通じてシリアライズ・デシリアライズが行われますが、パフォーマンスに関しては.NETが提供するものより大幅に高速です。
参考:https://docs.unity3d.com/ja/current/Manual/JSONSerialization.html
また、GC.Allocが最小限に抑えられるような設計になっていることも特徴です。
例えばJsonUtility.ToJsonメソッドなら、戻り値として返す文字列生成に対してのみGC.Allocが発生します。
JsonUtility.FromJsonOverwriteメソッドは、上書きされるstring型や配列型フィールドに対してGC.Allocが発生してしまいますが、完全な値型への上書きでは全くGC.Allocが発生しません。
さいごに
JsonUtilityクラスを使うことで、手軽にJSON形式のシリアライズ・デシリアライズ操作ができます。
機能が少ない分、パフォーマンスが最適化されているメリットがあり、通常のJSONを扱う用途であればJsonUtilityクラスを使えば良いでしょう。