게임 개발/Unity

[유니티 C#] MonoBehaviour를 상속 받아야하는 Singleton, 모노 싱글톤

Heesuk Lee 2021. 8. 29. 17:05

 

이전에 싱글톤 관련 글을 정리하면서 마지막에 한번 언급했던 내용을 다뤄보겠습니다!

 

https://welcomeheesuk.tistory.com/62

 

[디자인패턴] 03. 유니티에서의 싱글톤(Singleton) 패턴

지난 수요일에 친구들과 디자인패턴에 대해 스터디를 하면서 제 스스로로 잘 알고 사용하고 있으면서도 잘 모르겠는 아리송한 싱글톤 패턴에 대해서 다시 한번 정리하고자 합니다. 저는 예전에

welcomeheesuk.tistory.com

 

 

유니티로 게임을 개발하다보면 Sington패턴으로 정보들을 관리하면 좋을때가 많습니다.

그러면서 MonoBehaviour를 상속받는데 싱글톤처럼 동작하도록 구현하고 싶을때가 있습니다.

 

흠흠...

 

그럴때 사용하면 좋은것이 바로 '모노싱글톤(MonoSingleton)'입니다.

이 싱글톤이야말로 유니티 관련 싱글톤을 검색하면 흔히 볼 수 있는 싱글톤입니다.

 

대표적으로 사운드매니저, 오디오 매니저가 있습니다.

AudioSource 컴포넌트를 들고있어야 하지만 싱글톤 처럼 하나만 존재하면 정말 좋을 것 같은 기능입니다.

 

우선 MonoSingleton부터 살펴보겠습니다.

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

public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static object lockObject = new object();
    private static T instance = null;
    private static bool IsQuitting = false;

    public static T Instance
    {
        // 쓰래드 안전화 - Thread-Safe
        get
        {
            // 한번에 한 스래드만 lock블럭 실행
            lock (lockObject)
            {
                // 비활성화 됐다면 기존꺼 내비두고 새로 만든다.
                if ( IsQuitting )
                {
                    return null;
                }

                // instance가 NULL일때 새로 생성한다.
                if (instance == null )
                {
                    instance = GameObject.Instantiate(Resources.Load<T>("MonoSingleton/" + typeof(T).Name));
                    DontDestroyOnLoad(instance.gameObject);
                }
                return instance;
            }
        }
    }

    private void OnDisable()
    {
        // 비활성화 된다면 null로 변경
        IsQuitting = true;
        instance = null;
    }
}

 

보시면 느껴지시겠지만 Resources/MonoSingleton 폴더에 있는 Prefab을 Instantiate해서 하이라키상에 유지시키는 방법입니다. 다양한 여러 방식이 있지만 해당 방법이 가장 알아보기도 쉽고 관리하기도 쉬운 방법이라고 생각했습니다.

모든 MonoSingleton관련된 프리팹은 해당 폴더에 전부 모여있어 관리하거나 변경사항이 있을때 쉽게 접근이 가능할 것 같습니다.

 

변경사항 있으면 프리팹 들어가서 바꾸면 된다.

 

 

다음은 MonoSingleton을 상속 받는 AudioManager입니다.

 

using UnityEngine;

public enum SFX
{
    BUTTON,
}

public enum BGM
{
    OUTGAME,
}

public class AudioManager : MonoSingleton<AudioManager>
{
    [SerializeField]
	private AudioSource sfxSource = null;
    [SerializeField]
	private AudioSource bgmSource = null;

    [SerializeField]
    private AudioClip[] sfxList = {};
    [SerializeField]
    private AudioClip[] bgmList = {};

    private void Awake() 
    {
        // 사운드 초기화
        this.sfxSource.loop = false;
        this.bgmSource.loop = true;
    }
    
    public static void PlayBgm(BGM _bgmType)
	{
        int bgmType = (int)_bgmType;
        Instance.bgmSource.clip = Instance.bgmList[bgmType];
        Instance.bgmSource.Play();
	}
    
    public static void PlaySfx(SFX _sfxType, float _distance = 4f)
    {
        int sfxNumber = (int)_sfxType;
        Instance.sfxSource.PlayOneShot(Instance.sfxList[sfxNumber], Instance.sfxSource.volume);
    }

	public static void StopSfx()
	{
        Instance.sfxSource.Stop();
	}
	public static void StopBgm()
	{
        Instance.bgmSource.Stop();
	}

    public static void SetMuteSfx(bool _mute)
    {
        Instance.sfxSource.mute = _mute;
    }
    public static void SetMuteBgm(bool _mute)
    {
        Instance.bgmSource.mute =_mute;
    }

    public static void ChangeSfxVolume(float _value)
    {
        Instance.sfxSource.volume = _value;
    }
    public static void ChangeBgmVolume(float _value)
    {
        Instance.bgmSource.volume = _value;
    }

    public static float GetVolumeSfx()
    {
        return Instance.sfxSource.volume;
    }
    public static float GetVolumeBgm()
    {
        return Instance.bgmSource.volume;
    }

    public static bool GetMuteSfx()
    {
        return Instance.sfxSource.mute;
    }
    public static bool GetMuteBgm()
    {
        return Instance.bgmSource.mute;
    }
}

 

메소드들은 bgm과 sfx들을 켜고 끄는 기능들이 있습니다.

 

프리팹으로 관리합니다.

 

다음은 실행부입니다. 별거 없이 그냥 audiomanager에 있는 메소드를 한번 호출해줍시다.

singleton의 가장 강한 기능인 호출안하면 생성안하면 한번 호출하고나서부터는 이것만 쓴다를 따릅니다.

 

public class OutgameUI : MonoBehaviour
{
	// 리소스가 없어 그냥 Instance만 하도록 해봤습니다.
    void Start()
    {
        AudioManager.StopBgm();
    }
}

 

한번 호출해주니 오디오매니저가 생성되는것을 확인할 수 있습니다.

 

당연하지만 씬을 옮기게 되도 파괴되지않고 계속 유지됩니다.

 

이렇게 데이터 연산에만 필요한 일반 싱글톤에 이어

MonoBehaviour을 이용해야만 하는데 싱글톤 기능을 하는 모노싱글톤까지 살펴봤습니다.

 

싱글톤만 잘 관리하고, 역할 분리만 잘 해줘도 경험상 행복하게 유지보수 할 수 있는 개발에 다가갈 수 있는것 같습니다!

 

모두가 행복해지는 길로 나아갑니다~

 

반응형