この記事でのバージョン
Unity 2017.4.6f1
はじめに
いきなりですが問題です!以下のスクリプトをてきとうなGameObjectにAddして、
エディタ上で再生した場合、ログには何が表示されるでしょうか?
using System; using UnityEngine; using UnityEditor; public class NewBehaviourScript : MonoBehaviour { private void Start() { Data data = ScriptableObject.CreateInstance<Data>(); AssetDatabase.CreateAsset(data, "Assets/Resources/Data.asset"); data = Resources.Load<Data>("Data"); Debug.Log(data.Info.Text); } } public class Data : ScriptableObject { public InfoClass Info = null; } [Serializable] public class InfoClass { public string Text = "テスト"; }
ちなみにTwitterのアンケートでは以下のような結果に。
Q.画像のスクリプトをてきとうなGameObjectにAddして、エディタ上で再生した場合、ログには何が表示されるでしょうか?
— カン神巫女 -KAMIKO- (@Kan_Kikuchi) June 27, 2018
なお、問題の画像はアンケートに添付出来なかったので、このTweetにリプライで付いてます。#Unity #アンケート
答え
さっそく答えですが、普通にテストと表示されます。
なお、ScriptableObjectのクラスと同名のスクリプトファイルがないので警告も出ますが、
エラーは出ません。
ここで疑問になってくるのが「dataのInfoがいつ生成されたのか」です。
プログラムを見る限り、最初からずっとnullのように見えます。
//ずっとnullのはず🤔 public InfoClass Info = null;
実はCreateAsset、つまりScriptableObjectをアセットとして出力した事が原因です。
なんとScriptableObjectは出力時にnullになっているシリアライズされたclassを生成します。
//data内のnullなclassが生成される! AssetDatabase.CreateAsset(data, "Assets/Resources/Data.asset");
つまり、(ScriptableObjectを出力せずに使う事は無いと思われるので)
ScriptableObjectに含んでいるシリアライズされたclassはnullに出来ないのです。
ちなみにCreateAssetしなければdataのInfoがnullなのでエラーが出ますし、
using System; using UnityEngine; using UnityEditor; public class NewBehaviourScript : MonoBehaviour { private void Start() { Data data = ScriptableObject.CreateInstance<Data>(); //AssetDatabase.CreateAsset(data, "Assets/Resources/Data.asset"); //data = Resources.Load<Data>("Data"); //dataのInfoがnullなのでエラー! Debug.Log(data.Info.Text); } } public class Data : ScriptableObject { public InfoClass Info = null; } [Serializable] public class InfoClass { public string Text = "テスト"; }
InfoClassをシリアライズしなくてもdataのInfoがnullなのでエラーが出ます。
using System; using UnityEngine; using UnityEditor; public class NewBehaviourScript : MonoBehaviour { private void Start() { Data data = ScriptableObject.CreateInstance<Data>(); AssetDatabase.CreateAsset(data, "Assets/Resources/Data.asset"); data = Resources.Load<Data>("Data"); //dataのInfoがnullなのでエラー! Debug.Log(data.Info.Text); } } public class Data : ScriptableObject { public InfoClass Info = null; } //[Serializable] public class InfoClass { public string Text = "テスト"; }
なお、Serializableなクラスをフィールドにしたら、
nullなのにコンストラクタが実行される(nullじゃなくなる)という事もあるので、
ScriptableObjectだけの話ではなかったりします。