프로그래밍/유니티

[유니티] MonoSingleton

갓똥 2022. 1. 14. 02:35
728x90
반응형

 

유니티에서 싱글톤을 쓰면서 MonoBehaviour를 상속받는 싱글톤과 아닌것을 나누는 필요성을 느꼈다.

 

만들고나니 왠만한 프로젝트에서 다 써도 될 것 같아 남김

 

using UnityEngine; 
using System.Collections; 
using System.Collections.Generic;

//씬 변경시 삭제가 되야하는 싱글톤들은 'class : MonoSingleton<T>, IDestructible' 의 형태로 선언한다.
interface IDestructible
{
}
//하이라키에서 보여져야 하는 싱글톤들은 'class : MonoSingleton<T>, IAppearable' 의 형태로 선언한다.
interface IAppearable
{
}

//싱글톤 관리 루트 클래스
public static class SingletonRoot
{
    public const string SingletonRootName = "ManagerRoot";
    
    private static GameObject _singletonRootInstance = null;

    public static GameObject GetRootInstance()
    {
        if (_singletonRootInstance == null && Application.isPlaying)
        {
            _singletonRootInstance = new GameObject(SingletonRootName);
            _singletonRootInstance.AddComponent<RectTransform>();
            GameObject.DontDestroyOnLoad(_singletonRootInstance);
        }

        return _singletonRootInstance;
    }

    public static GameObject EditGetRootInstance()
    {
        if (_singletonRootInstance == null)
        {
            _singletonRootInstance = new GameObject(SingletonRootName);
        }

        return _singletonRootInstance;
    }

    public static void DestroyInstance()
    {
        GameObject.DestroyImmediate(_singletonRootInstance);
    }
}

//매니저 클래스의 부모가 되는 MonoBehaviour 타입의 싱글턴 클래스
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
    public const string StaticManagerOption = "(Static)";
    public const string DynamicManagerOption = "(Dynamic)";

    protected static T _instance = null;

    public static bool IsNull()
    {
        return _instance == null;
    }

    public static T Instance 
    { 
        get 
        { 
            if(_instance == null)
            {
                _instance = FindObjectOfType<T>();
                if(_instance == null)
                {
                    CreateInstance();
                }
                else
                {
                    SetInstanceObject(_instance.gameObject);
                }
            }
            return _instance; 
        } 
    }

    public static void CreateInstance()
    {
        if (_instance != null || Application.isPlaying == false)
            return;
                
        GameObject singletonObject = new GameObject(typeof(T).ToString());
        _instance = singletonObject.AddComponent<T>();

        if(_instance is IAppearable)
        {
            singletonObject.hideFlags = HideFlags.None;
        }
        else
        {
            singletonObject.hideFlags = HideFlags.HideInHierarchy;
        }

        SetInstanceObject(singletonObject);
    }

    private static void SetInstanceObject(GameObject singletonObject)
    {
        //인터페이스 클래스의 유무 따라 옵션형태로 기능이 추가된다.
        if (_instance is IDestructible == false)
        {
#if UNITY_EDITOR
            singletonObject.name = singletonObject.name + StaticManagerOption;
#endif
            if (Application.isPlaying)
                DontDestroyOnLoad(singletonObject);

            singletonObject.transform.parent = SingletonRoot.GetRootInstance().transform;
        }
        else
        {
#if UNITY_EDITOR
            singletonObject.name = singletonObject.name + DynamicManagerOption;
#endif
        }
    }
    
    public static void DestroyInstance()
    {
        if (_instance == null)
            return;

        Destroy(_instance.gameObject);
        _instance = null;
    }

    public virtual void OnDestroy()
    {
        //인터페이스 클래스의 유무 따라 옵션형태로 기능이 추가된다.
        if (_instance is IDestructible || Application.isPlaying == false)
        {
            DestroyInstance();
        }
    }

    public static void EditCreateInstance()
    {
        if (_instance != null)
            return;
        
        GameObject singletonObject = new GameObject(typeof(T).ToString());
        _instance = singletonObject.AddComponent<T>();

        if (_instance is IAppearable)
        {
            singletonObject.hideFlags = HideFlags.None;
        }
        else
        {
            singletonObject.hideFlags = HideFlags.HideInHierarchy;
        }

        EditSetInstanceObject(singletonObject);
    }

    private static void EditSetInstanceObject(GameObject singletonObject)
    {
        //인터페이스 클래스의 유무 따라 옵션형태로 기능이 추가된다.
        if (_instance is IDestructible == false)
        {
            singletonObject.name = singletonObject.name + StaticManagerOption;
            if (Application.isPlaying)
                DontDestroyOnLoad(singletonObject);

            singletonObject.transform.parent = SingletonRoot.EditGetRootInstance().transform;
        }
        else
        {
            singletonObject.name = singletonObject.name + DynamicManagerOption;
        }
    }
}
728x90
반응형