箱庭ハーブblog

7年目プログラマの趣味の小部屋

ScriptableObjectとInstantiateとシリアライゼーションの使い方

scriptableobject、instantiate



2017-03-03
シリアライズルールに関して、追記しました。

ども、お疲れ様です。
知ってる人は知っている、ScriptableObjectのInstantiateのお話。

全体を俯瞰するには、下記の公式サイトをひとまず読むと良いでしょう。

スクリプトシリアライゼーション
https://docs.unity3d.com/ja/current/Manual/script-Serialization.html


ブログ内の記事では、こちらでも扱っています。

完全攻略、Unityシリアライゼーション
http://hakoniwaherb.blog.shinobi.jp/Entry/534/

ScriptableObject、便利ですね。
継承とポリモーフィズムが使えて、Prefabのようにpublicフィールドにリンク出来て、
エディタ上で編集できる。

最高です。

Serializable付加クラスと適切に使い分けることで、
大半の機能をエディタだけで処理することが可能になります。

で、今回はScriptableObjectとInstantiate。

1、Instantiateは何をしているのか

Prefabでおなじみ、Instantiateさん。複製してくれます。
Prefab(ひな型)を作ってリンクしておき、それをInstantiateするだけ。
なんとなくならすぐわかる。

しかし
  • どうしてPrefabの型はGameObjectなのか
  • どうしてInstantiateを調べるとシリアライズの話が出てくるのか
  • ScriptableObjectをInstantiateするとは、そもそもどういうことなのか

これ、仕組みを知ればすべて解決します。

単純。

Instantiateは、
シリアライズ可能なオブジェクトをシリアライズしてデシリアライズしているだけです。

うん、用語がわからない人は良くわからんですね。

シリアライズは、テキスト化です。
現在のメモリ上にあるオブジェクト(インスタンス)を、テキストに写し取ることです。

デシリアライズは、Deシリアライズです。
テキストから、オブジェクト(インスタンス)を作ることです。

「シリアライズ対象」「シリアライズされる」という単語は、
値が復元するかどうか、つまりコピーされるかどうかを表します。

このシリアライズのルールは、デフォルトでは次のようなルールになっています

大前提
1. シリアライズ可能な型は、以下の通り
 ・UnityEngine.Object継承クラス
 ・intなど基本型
 ・Serializable付加クラス
 ・Vector3、Quaternionなど、Unityシリアライザで特別に処理される構造体(※)
2. 1の配列、List<T>型もシリアライズ可能。Dictionary<TKey, TValue>は無理

(※)
これらのUnity内の構造体は、Serializableではないため、
Unityと関係ない.NETのシリアライザやサードパーティのシリアライザを用意すると、
シリアライズされない

詳細
1. privateメンバや自動プロパティの暗黙のフィールドも、ScriptableObjectなら対象である
1. privateメンバ、自動プロパティはシリアライズされない。
2. staticメンバはシリアライズされない
3. read onlyメンバはシリアライズされない
4. [NonSerialized]付加メンバは、シリアライズされない
5. UnityEngine.Object継承クラスであるメンバは、参照も復元する
6. UnityENgine.Object継承クラスでないメンバは、参照が復元しない!(事実上のクローン)

2017/03/13 追記
privateメンバや自動プロパティは、シリアライズ対象ではありませんでした。
Instantiateせずに使用した場合、値が保持されるように見えますが、
エディタを起動しなおしたり、実機実行でアプリを終了して再起動すると、
値が初期化されています。
基本的にシリアライズはMonoBehaviourと同じ挙動をする、という認識でOKです。

5と6に関しては、6が変に見えるかもしれませんが、
シリアライズの仕組みを考えると、むしろ5がすごいことをしていると言えるでしょう。
ポインタやシリアライズIDを元に参照を復元しているのですから、恐ろしい。

大前提に関して補足すると、
シリアライズ対象でない型、つまりSerializable付加でない非基本型は、
シリアライズされませんし、復元もされません。
Instantiate後のインスタンスはnull(structなら暗黙コンストラクタされた値)になります。

詳細の6に関して補足すると、
これはSerializable付加クラス型のメンバに対して起こる内容です。
Serializable付加クラス型はUnityEngine.Object継承型とは限らないため、
UnityID管理されず参照が復元されません。
しかしながら、Serializable付加クラスはシリアライズ対象ですから、値は復元されます。
結果として、Serializable付加クラス自体のインスタンスは別々になります。


2、ScriptableObjectとは何者か?

ScriptableObjectは、Prefabとよく似ています。
しかし、
Prefabと異なり、Instantiateする文化が言及されていないので、
いまいちよくわからない動作をするイメージがあると思います。

厄介なのは、Unityエディタ上とビルドしたアプリで挙動が異なることです。
Unityエディタでは、実行時の情報はアセットに反映・保存されますが、
ビルドした後は通常の手順では反映されません。

ここら辺は、下記のリンクの下の方にある説明が詳しいです。

テラシュールブログ ScriptableObjectについて
http://tsubakit1.hateblo.jp/entry/2014/07/24/030607

3、ScritableObjectとInstantiate

PrefabをInstantiateせずに使うと、どうなるでしょう。

弾のPrefabだったら、複製されないし、色々使い物になりません。
しかし、値置き場として使うなら、優秀です。

ScriptableObjectも同じです。

値置き場として使うなら、Instantiateせずに使えば優秀です。
当然、値を変更すると、
エディタ実行中ならばScriptableObjectのアセットの内容が変わっちゃいます。

========
→ ※注意
前述の通りエディタではなくアプリ実行中は、
最終的にアセットに保存反映されないため、アプリを再起動すると元に戻っています。
========

一方、Prefabと同じようにScriptableObjectをInstantiateすると、
クローンされます。

クローンされたScriptableObjectは当然元とは関係がなくなるので、
自由に挙動させることが出来るようになります。

前述の通り、ScriptableObjectが別のPrefabやScriptableObjectを
リンク(publicフィールド接続、アウトレット接続)していた場合、
その参照は復元されてしまうので、同じアセットを共有したくない場合は、
それらもInstantiateする必要があります。

まとめ

ScriptableObjectのInstantiateは、非常に強力な機能です。
ScriptableObjectをInstantiateせずに使う場合、
初期パラメータとしてしか使えません(セーブデータも無理)が、
Instantiateすれば、現在ステータスとセットで表現できるようになります。
(ステータスメンバはNonSerializedをつけることが重要です。)

目的に応じてInstantiateするかしないかを使い分けることで、
最適なパフォーマンスと構造が作れるでしょう。
コメント
PAGETOPへ

コメント送信

お名前
タイトル
文字色
メールアドレス
URL
コメント
パスワード

パスワードを入れておかないと、コメントの再編集ができません!

フリーエリア

takemori
Twitter : @takemori_kondo

1. Unityと戯れてます
2. Cake3は劣化じゃないRails

iOS
coming soon...

Windows
Html Editor - Nazuna
Managed DirectX サンプル集

beginning since
2006.08.17
renewaled on
2011.06.03

最新コメント

[2013/06/14 ミューネ]
[2012/08/30 ノートPC]