読者です 読者をやめる 読者になる 読者になる

ちょこっとベターなSingletonMonoBehaviour

tsubakit1.hateblo.jp

こちらの記事を参考にしたSingletonMonoBehaviourを利用していたのですが、SingletonMonoBehaviourの絡むバグが発生し、それを発生しにくくできないかと思って、ちょこっと変えてみました。

上記のSingletonMonoBehaviourでは、Instanceにアクセスすると最初の一度だけ、 FindObjectOfType( typeof( T ) ); をしています。

この時に、そのオブジェクトのactiveがfalseだと見つからなくてエラーになるという問題がありました。

まあ、普通はactivateをfalseになんてしていないと思うんですが、例えば、

class HogeManager : SingletonMonoBehaviour<HogeManager>

というのがあったときに、

class HogeMain{
    public HogeManager hogeManager;//Inspectorで与えとく

    void Start()
    {
        hogeManager.SetActive( false );//最初は使わないから false にしとこう。

        ... //いろいろ処理

        HogeManager.Instance.Function();//HogeManagerのFunctionを呼ぼう! <- ここで死亡
    }
}

という感じで死んでしまったことがありました。

それでも、普通はAwakeが呼ばれたさいにinstanceに値が入るのでだいじょうぶなんですけど、往々にして継承先でAwakeを書きたくなるんで、継承元のAwakeを隠しちゃっていました。

隠しちゃってるよっていう、warning出ていたんですけどね。

継承先でAwakeを書きたくなることは結構あるので、継承元のAwakeをvirtualにしました。

そして、継承先で、

protected override void Awake()
{
    base.Awake();
    ...//継承先独自の処理
}

としています。

ただ、これでもAwake内でSetActive(false)されて、Instanceにアクセスされたり、そもそもEditor上でactiveをfalseにされていたら、同様にエラーになっちゃいますけど。

他にも、

abstractにしてたり、 where T : SingletonMonoBehaviourにしてたりと、ちょこっと安全にしました。

では、ソース。

using UnityEngine;

public abstract class SingletonMonoBehaviour<T> : MonoBehaviour where T : SingletonMonoBehaviour<T>
{
	private static T instance;
	public static T Instance {
		get {
            if ( instance == null ) {
                instance = ( T )FindObjectOfType( typeof( T ) );
                if ( instance == null ) {
                    Debug.LogError ( typeof( T ) + "is nothing" );
                }
            }
			return instance;
		}
	}

	protected virtual void Awake()
	{
		CheckInstance();
	}
	
	protected bool CheckInstance()
	{
		if( instance == null)
		{
			instance = (T)this;
			return true;
		}else if( Instance == this )
		{
			return true;
		}
		
		Destroy(this);
		return false;
	}

	void OnDestroy () {
		instance = null;
	}
}