(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

ScriptableObjectに含んでいるシリアライズされたclassはnullに出来ない【Unity】【ScriptableObject】


このエントリーをはてなブックマークに追加


この記事でのバージョン
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のアンケートでは以下のような結果に。





答え

さっそく答えですが、普通にテストと表示されます。

なお、ScriptableObjectのクラスと同名のスクリプトファイルがないので警告も出ますが、

エラーは出ません。


f:id:kan_kikuchi:20180701060404j:plain


ここで疑問になってくるのが「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だけの話ではなかったりします。