MRが楽しい

MRやVRについて学習したことを書き残す

HoloToolkitのSingletonクラスを見てみる

本日は HoloToolkit の技術枠です。
HoloToolkit で使われている Singleton パターンについてです。
Singleton パターン - Wikipedia

多用して泣きを見るのは多くの方が通った道だと思いますが。
プログラム内で一意の状態を保持する仕組みとして活用しない手はありません。
Unity ではシーン移動後に情報を引き継ぐ方法としても活用できるようです。
medium.com

HoloToolkit には「Singleton」というクラスが用意されており、シェアリング状態の保持などの各所で利用されています。
今回はこのクラスについて確認してみました。
・Singleton.cs

using UnityEngine;

namespace HoloToolkit.Unity
{
    /// <summary>
    /// Singleton behaviour class, used for components that should only have one instance
    /// </summary>
    /// <typeparam name="T">The Singleton Type</typeparam>
    public class Singleton<T> : MonoBehaviour where T : Singleton<T>
    {
        private static T instance;

        /// <summary>
        /// Returns the Singleton instance of the classes type.
        /// If no instance is found, then we search for an instance
        /// in the scene.
        /// If more than one instance is found, we throw an error and
        /// no instance is returned.
        /// </summary>
        public static T Instance
        {
            get
            {
                if (instance == null && searchForInstance)
                {
                    searchForInstance = false;
                    T[] objects = FindObjectsOfType<T>();
                    if (objects.Length == 1)
                    {
                        instance = objects[0];
                    }
                    else if (objects.Length > 1)
                    {
                        Debug.LogErrorFormat("Expected exactly 1 {0} but found {1}.", typeof(T).ToString(), objects.Length);
                    }
                }
                return instance;
            }
        }

        private static bool searchForInstance = true;

        public static void AssertIsInitialized()
        {
            Debug.Assert(IsInitialized, string.Format("The {0} singleton has not been initialized.", typeof(T).Name));
        }

        /// <summary>
        /// Returns whether the instance has been initialized or not.
        /// </summary>
        public static bool IsInitialized
        {
            get
            {
                return instance != null;
            }
        }

        /// <summary>
        /// Base Awake method that sets the Singleton's unique instance.
        /// Called by Unity when initializing a MonoBehaviour.
        /// Scripts that extend Singleton should be sure to call base.Awake() to ensure the
        /// static Instance reference is properly created.
        /// </summary>
        protected virtual void Awake()
        {
            if (IsInitialized && instance != this)
            {
                if (Application.isEditor)
                {
                    DestroyImmediate(this);
                }
                else
                {
                    Destroy(this);
                }

                Debug.LogErrorFormat("Trying to instantiate a second instance of singleton class {0}. Additional Instance was destroyed", GetType().Name);
            }
            else if (!IsInitialized)
            {
                instance = (T)this;
            }
        }

        /// <summary>
        /// Base OnDestroy method that destroys the Singleton's unique instance.
        /// Called by Unity when destroying a MonoBehaviour. Scripts that extend
        /// Singleton should be sure to call base.OnDestroy() to ensure the
        /// underlying static Instance reference is properly cleaned up.
        /// </summary>
        protected virtual void OnDestroy()
        {
            if (instance == this)
            {
                instance = null;
                searchForInstance = true;
            }
        }
    }
}

注目する点は Instance の中にある FindObjectsOfType(); です。
Unity で Singleton パターンを利用するにあたり問題となるのが、Singletonインスタンスの初期化のタイミングです。
Awake の実行時に初期化を行うと、他の Awake で呼び出すことができません。Null 参照する可能性があるためです。

上記の実装方法の場合、Find で参照を行っているため、Awake 時に呼び出してもインスタンスが取得できます。
以下が詳しいです。
tsubakit1.hateblo.jp

UWPアプリ開発以外でも有効な手段なのでメモとして残して置きます。