【Unity5】エディタ開発(拡張でない)において注意すべき事
皆さんエディタ拡張はしているでしょうか。
しかし今回ここで話すのはエディタ「拡張」ではなく「開発」についてです。
具体的には、エディタ上で編集した値の保存についてです。
全ての総称として「拡張」と呼ばれていますが、わかりにくいので、私的な区別ですが「拡張」は既にあるUI上の見た目等を変更、改変するのを拡張と呼び、それに比べ、新規にウィンドウやUIを配置して自作のツールを制作するのが「開発」だという体でここでは述べていきますのであしからず。
また、Hierarchy上やProject上のアセットやオブジェクトを直接弄るようなものではなく、エディタ内部の値を保存させる方法について話していきます。
シリアライズ
Unity上では値をシリアライズすることによってその値を永久なものにするようになっています。
具体的には、以下の書き方で変数をシリアライズします。
public int hoge1;
もしくは
[SerializeField]
int hoge2;
publicな変数は自動的にシリアライズされるため、なにも考えなくても良いですが、publicをつけていない、もしくはprivateな変数の場合、「[SerializeField]」を変数の上行へ付ければシリアライズされるようになります。
シリアライズしていないとどうなるか?
シリアライズしていない変数の場合、Unityの再生ボタンを押した際に値が初期化(nullに)されます。これは、Unityがエディタを再生させた際にシーンをロードしているためだそう。
そのため、privateな変数等のシリアライズされていない変数は、Start関数やAwake関数で初期化するか、null判定するなどして対処しなければ、思わぬところでnullエラーを吐かれることになります。
ところがどっこいおもわぬ罠
さて、これで自作エディタで色々した後、再生すればエディタ内部で編集した値が保存されると思いきや、そうはなりません。
Editor,EditorWindowを継承しているクラス上の変数は、publicだろうが[SerialiseField]を付けていようがお構いなしに値を初期化してくれやがります。
Hierarchyに置かれていなければ保存されない
実は、publicや[SerialiseField]を使用してシリアライズして値を保存してくれるのは、Hierarchyに置かれているオブジェクトのコンポーネントのみです。細かくいえば、MonobehaviourやScriptableObjectを継承しているクラスです。
つまり、EditorやEditorWindowを継承しているクラスはどうあがこうがそのクラス内部の値は初期化されてしまいます(Hierarchyにも置けない)。
さらに、例えMonobehaviourを継承していても、やはりHierarchyに置かれていなければ、どうやら初期化されてしまうようです。
エディタ開発で何が困るの?
さて、これらを述べたうえで、値の初期化がなぜ困るのか。
一番困るのは、Hierarchy上やProject上のアセットやオブジェクトを一時的に弄り完結するようなエディタではなく、エディタ内部で値をUnityの再生/停止を跨いで長期的に扱う必要のあるエディタです。
例えば、わかりやすいのが、よくある状態遷移エディタあたりでしょうか。
あれは(アセットによりますが)再生する前にエディタ上で「ノード」と呼ばれる矩形のUIを配置して、それらを線でつなぎ、再生時の動作を作成/制御するというものですUE4(UnrealEngine4)のBP(BluePrint)がそれです。
しかし、エディタで作成したノードや線の情報をEditor/EditorWindow継承のクラスで管理すると、Unityを再生した際に初期化されてnullになり、画面は消え、コンソール画面には何行ものnullエラーが並びます(ツライ)。
その為、エディタ上で編集した値を保存したい場合には、MonobehaviourもしくはScriptableObject継承のクラスを用いて、Hierarchyに配置、もしくはScriptableObjectのアセットを作成するなどして値を保存するようにしましょう。