共有データ保存になんとなく使っていた ScriptableObject について、いろいろ研究してみました。
そして、今までいろいろ間違った使い方をしていたことに気づきました。
目次
ScriptableObject インスタンスを参照(生成)する方法
インスペクタでシリアライズ
シーン内の MonoBehaviour オブジェクトに設定して置く方法。
注意したいのは、シーン内に間接的にリンクされたオブジェクトが存在する場合、メモリが確保されてしまうということです。
たとえば下のような、ScriptableObjectを持ったプレハブを
シーン内のオブジェクトにリンクしてしまうと
ScriptableObjectのメモリが確保され、さらにScriptableObject#Awakeなどのライフサイクル関数も呼ばれます。
あらゆるテーブルをScriptableObjectにして、一つのオブジェクトにリンクするようなことはやめておきませう。
Resource.Load
Resourcesフォルダの下に ScriptableObject のアセットを置くことで、動的にロードすることができます。
例えばプロジェクト内に以下のように配置しておけば
下のソースで動的に ScriptableObject を読み込むことができます。
// EnemyDataTable はScriptableObjectを継承したクラス EnemyDataTable table = Resources.Load("EnemyDataTable") as EnemyDataTable;
ライフサイクル関数は Load 関数実行時に呼ばれます。
たぶん、必要なタイミングでのみScriptableObjectを参照したい(メモリをなるべく確保したくない)と思ったときに、最終的に行き着くところがコレ。
嬉しいのは、Awake, OnEnableなどのライフサイクル系の関数は、初回にLoadしたときのみ呼ばれるところ。
つまりメモリも初回Load時のみ確保されていると思われる。
ScriptableObject.CreateInstance
新しくインスタンスが確保されます。
インスタンスが別なので、メモリ領域は別々に確保されます。
EnemyDataTable EnemyDataTable = ScriptableObject.CreateInstance<EnemyDataTable>();
プログラムから生成しているので、データのシリアライズなどはできませんので値を初期化する必要があります。
違う数値を持つScriptableObjectをスクリプトから作成したいときに使うかも?
ただ、値をコピーして別インスタンスにしたい場合は ↓
ScriptableObject.Instantiate
この関数を使えば、インスタンスを新しく確保できます。
EnemyDataTable = ScriptableObject.Instantiate(Resources.Load("EnemyDataTable")) as EnemyDataTable;
これで確保したインスタンスを ScriptableObject.Destroy で開放すると、OnDisable, OnDestroy メソッド が呼ばれます。
上記ライフサイクル関数を使いたい場合は有用かもしれません。
ライフサイクル関数
ScriptableObjectには、MonoBehaviourのStartなどにあたる関数として、AwakeやOnEnableなどがあります。
注意しなければならないのは、エディタ上で実行するのと、ビルドしたプログラムを実行するのとでは、呼ばれるタイミングが全く違うということ!
Awake, OnEnable
この二つの使い分けがよくわからないので一緒に書いておく。
エディタ
Unity起動後、はじめてプロジェクト内でScriptableObjectを選択してインスペクタに表示したときに呼ばれます。ビルド後
ScriptableObjectの参照が発生したときに呼ばれます。
シーン内に間接的にリンクされたオブジェクトが Instantiate された、Resources.Load した、など。
呼ばれる順番は
コンストラクタ > Awake > OnEnable
です。
OnDisable, OnDestroy
この二つの使い分けもよくわからないので一緒に書いておく。
ScriptableObject.Instantiate したインスタンスを ScriptableObject.Destroy で開放すると呼ばれます。
呼ばれる順番は OnDisable > OnDestroy
上記の関数を使わない限り、エディタ上では基本的に呼ばれないと思ったほうがいいかも?