유니티 싱글톤(싱글턴) 기본 패턴 Unity singleton parttern

 

 

1) 기본형 싱글톤

 - DonDestroyOnLoad 를 삭제하면 해당 씬에서만 쓰이는 싱글톤으로 사용.

public class SingletonExample : MonoBehaviour
{
    #region 싱글톤
    private static SingletonExample _instance = null;

    public static SingletonExample Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = (SingletonExample)FindObjectOfType(typeof(SingletonExample));
                if (_instance == null)
                {
                    Debug.Log("There's no active ManagerClass object");
                }
            }
            return _instance;
        }
    }

    void Awake()
    {
        if (_instance != null && _instance != this)
        {
            DestroyImmediate(gameObject);
        }
        else
        {
            _instance = this;
            DontDestroyOnLoad(this.gameObject);
            AwakeAfter();
        }
    }
    #endregion
    
    void AwakeAfter()
    {
    
    }
}

 

2) 다른 씬으로 전환할 때 마다 특정 함수 호출하는 싱글톤 

 - 문제점 : DonDestroyOnLoad상태의 오브젝트는 Awake랑 Start가 최초 씬에서만 호출된다. 하지만 씬이 전환될 때마다 처리를 해주어야 하는 경우가 있다.

 - 해결 : DonDestroyOnLoad + 다른 씬으로 갈때 OnLevelLoaded 델리게이트를 체인 시켜준다.

 

 - 주의할 점은 함수의 호출 순서가  Awake -> OnEnable -> OnLevelLoaded -> Start 순이라서 Awake가 아니라 Start에서 호출할 경우 첫 번째 씬에서는 체인 메소드가 실행되지 않는다.

 - https://docs.unity3d.com/kr/530/Manual/ExecutionOrder.html 함수 호출 순서 참고.

 

public class LevelLoadedSingleton : MonoBehaviour
{
    #region 싱글톤
    private static LevelLoadedSingleton _instance = null;

    public static LevelLoadedSingleton Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = (LevelLoadedSingleton)FindObjectOfType(typeof(LevelLoadedSingleton));
                if (_instance == null)
                {
                    Debug.Log("There's no active ManagerClass object");
                }
            }
            return _instance;
        }
    }

    void Awake()
    {
        if (_instance != null && _instance != this)
        {
            DestroyImmediate(gameObject);
        }
        else
        {
            _instance = this;
            DontDestroyOnLoad(gameObject);
            
            // 델리게이트 체인을 걸어준다. Awake -> OnEnable -> OnLevelLoaded -> Start 순이라서 첫 씬도 
            SceneManager.sceneLoaded += OnLevelLoaded;
        }
    }

    private void OnLevelLoaded(Scene scene, LoadSceneMode mode)
    {
        // 레벨 변경시에 호출됨.
    }

    #endregion
}

 

 

3) 심화. 제네릭 싱글톤

 - 1)의 싱글톤 패턴은 새 클래스를 만들 때 마다 클래스 이름을 5번이나 써줘야 한다는 문제점이 있다. 

 - 때문에 제네릭 <T> 로 상속하여 간단하게 만들어 볼 수 있다.

 

 

Base가 되는 Singleton.cs 는 아래와 같이 만들어두고,

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    protected static T _instance = null;
    public static T Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType(typeof(T)) as T;
                if (_instance == null)
                {
                    Debug.Log("싱글턴 " + typeof(T) + "이 씬에 존재하지않음");
                }
            }
            return _instance;
        }
        set => _instance = value;
    }
}

 

상속시킬 스크립트에서는 : Singleton<해당클래스명> 으로 상속시켜주면 된다.

public class SingletonChildExample : Singleton<SingletonChildExample>
{
    #region 싱글톤
    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            DestroyImmediate(gameObject);
        }
        else
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
            AwakeAfter();
       }
    }
    #endregion
}

 

 

즉 구조가

Singleton<T>    ---상속-->    PlayerDataManager 이렇게 되는데, 

상속을 받지않았던 싱글톤들은 쉽게 적용이 가능하지만

 

아래와 같이 상속을 먼저 받은 후 개별적으로 싱글톤을 적용했던 스크립트들은 적용하기 힘들다.

FlowManager    ---상속--->   MainFlowManager / BattleFlowManager 

 

Singleton<T>를 상속받는 순간 <T>의 자료형이 해당 스크립트(아래에서는 FlowManager)가 돼버린다.

Singleton<T>    ---상속-->   FlowManager    ---상속--->    MainFlowManager 

 

이 경우 FlowManager 를 interface로 바꾸고, 

MainFlowManager는 Singleton<T>를 상속받으면서 Interface IFlow 를 상속받으면 된다.

 

인터페이스는 변수를 상수 밖에 못 가지니 공통 변수가 필요한 거라면, 그냥 싱글톤을 개별적으로 선언할 것. 

 

 

댓글

Designed by JB FACTORY