GameDevelop/Unity팀프로젝트

[TeamProject2_2025.05.12] 제네릭 싱글톤(Singleton<T>) 유틸리티

도도돋치 2025. 5. 12. 21:16
Contents 접기
728x90

게임 개발을 하다 보면 꼭 필요한 패턴 중 하나가 바로 싱글톤(Singleton) 이다. 

 

특히 Unity에서는 게임 매니저, 사운드 매니저, 풀 매니저 등 전역에서 딱 하나만 존재해야 하는 오브젝트가 많기 때문에 싱글톤을 아주 자주 사용한다.

 

그래서 이번 팀프로젝트때는 재사용 가능한 제네릭 싱글톤(Singleton<T>) 유틸리티를 만들어서 편하게 쓸 수 있게 하였다.

그 내용을 정리해보려고 한다.

 

 

 Singleton<T> 유틸리티 코드

// 파일명: Singleton.cs
using UnityEngine;

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    public static T Instance { get; private set; }

    protected virtual void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
        }
        else
        {
            Instance = this as T;
            DontDestroyOnLoad(gameObject); // (선택) 씬 이동에도 유지하고 싶을 때
        }
    }
}

 

이 클래스를 상속받아서 필요한 매니저 클래스를 만들 수 있다.

 

 

✅ 사용하는 방법

 

[1] 초기화가 필요 없는 경우

 

초기화할 게 없으면 그냥 싱글톤만 상속받고 바로 사용하면 된다.

public class SoundManager : Singleton<SoundManager>
{
    public void PlaySound(string name)
    {
        Debug.Log($"사운드 재생: {name}");
    }
}

 

사용 예시:

SoundManager.Instance.PlaySound("Click");

 

  • 별다른 초기화가 필요 없으면 Awake()를 오버라이드하지 않아도 된다.
  • Instance로 전역 접근이 가능하고, 하나만 존재한다. 

 

[2] 초기화 코드가 필요한 경우

 

만약 클래스에 초기화 코드가 필요하다면 Awake()를 오버라이드하고, base.Awake()를 먼저 호출해주어야 한다.

public class PoolManager : Singleton<PoolManager>
{
    [SerializeField] private GameObject bulletPrefab;
    private Queue<GameObject> bulletPool = new Queue<GameObject>();

    protected override void Awake()
    {
        base.Awake(); // 싱글톤 등록 먼저
        InitBulletPool(); // PoolManager만의 초기화
    }

    //그 외 코드
}

 

사용 예시:

PoolManager.Instance.SpawnBullet(Vector3.zero);

 

  • InitBulletPool() 같은 초기화 작업은 게임이 시작될 때 딱 한 번 실행되어야 한다. 
  • 그 작업을 Awake()에서 해준다.
  • 중요한 건 그 전에 base.Awake()를 꼭 호출해야 싱글톤 등록이 정상적으로 된다. 

 

📖 마무리

🔥 Unity에서는

  • 매니저처럼 하나만 존재해야 하는 오브젝트가 많고
  • 씬 이동에도 유지해야 하는 경우가 많기 때문에

이렇게 Singleton<T> 베이스 클래스를 만들어두고 상속해서 쓰면 정말 편하다.

 

728x90