싱글톤 패턴이란 특정 클래스의 인스턴스가 단 하나만 생성되도록 보장하는 디자인 패턴이다.
싱글톤 패턴을 처음 본다면 위의 말이 무슨 말인지 이해하기 힘들 것 같은데,
사실 단순하게 현재 실행되고 있는 씬에 해당 클래스가 단 하나만 있다는 말이다.
씬에 해당 클래스가 단 하나만 있기에 해당 클래스에 접근하고 싶을 때, 쉽게 접근할 수 있다.
이러한 장점 때문에 Manager 역할을 하는 클래스에 주로 사용된다.
이제 어떻게 구현하는지 알아보자.
먼저 기본적인 싱글톤 구현 방법이다.
public class GameManager : MonoBehaviour
{
private static GameManager instance;
public static GameManager Instance
{
get
{
// 만약 인스턴스가 없다면
// => 씬에 해당 클래스가 없거나 비활성화 되어있을 때
if (instance == null)
{
// GameManager 클래스를 가지고 있는 오브젝트를 생성하여 인스턴스에 넣어줍니다.
instance = new GameObject("GameManager").AddComponent<GameManager>();
}
return instance;
}
}
void Awake()
{
// 만약 인스턴스가 없다면
if (instance == null)
{
// 인스턴스에 자기 자신을 넣어줍니다.
instance = this;
// 씬 이동간에 파괴되지 않습니다.
// 선택사항이며, 파괴되지 않는 오브젝트는 부모 오브젝트가 없어야합니다.
DontDestroyOnLoad(this.gameObject);
}
else
{
// 자기 자신을 삭제합니다.
Destroy(this);
}
}
... 추가 로직 ...
}
근데 이렇게 작성하면 스크립트 위에 싱글톤을 구현해야해서 보기도 불편하고, 필요할 때 마다 구현하기도 귀찮다.
이를 해결하기 위해 제네릭 클래스를 활용하여 싱글톤을 만들 수 있다.
추가로 Awake나 Start 대신 인스턴스가 생성되었을 때, 실행될 Initialize 메소드도 넣어주었다.
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
protected static T instance;
public static T Instance
{
get
{
if (instance == null)
{
instance = new GameObject(nameof(T)).AddComponent<T>();
instance.Initialize();
}
return instance;
}
}
protected virtual void Awake()
{
if (instance == null)
{
instance = this as T;
instance.Initialize();
}
}
protected virtual void Start()
{
if (instance != this)
Destroy(gameObject);
}
protected virtual void Initialize()
{
}
}
이제 싱글톤을 사용하고 싶다면 다음과 같이 사용하면 된다.
public class GameManager : Singleton<GameManager>
{
protected override void Initialize()
{
base.Initialize();
// Awake문을 사용하고 싶을 경우 재정의하여 사용
}
... 추가 로직 ...
}
추가로 파괴되지 않는 싱글톤도 제네릭으로 만들어보았다.
using UnityEngine;
namespace FrameWork
{
public class PersistentSingleton<T> : MonoBehaviour where T : PersistentSingleton<T>
{
protected static T instance;
public static T Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType<T>();
if (instance == null)
{
instance = new GameObject(nameof(T)).AddComponent<T>();
instance.Initialize();
}
}
return instance;
}
}
protected virtual void Awake()
{
if (this.transform.parent != null)
{
this.transform.SetParent(null);
}
if (instance == null || instance == this)
{
instance = this as T;
DontDestroyOnLoad(transform.gameObject);
}
else
{
if (this != instance)
{
Destroy(this.gameObject);
}
}
}
protected virtual void Initialize()
{
}
}
}
'공부 > 유니티' 카테고리의 다른 글
[Unity] 스레드와 비동기 (0) | 2024.02.07 |
---|---|
[Unity] Addressable Asset System (0) | 2024.02.06 |
[Unity] 컴퓨터가 랜덤을 구하는 방법 (0) | 2024.01.29 |
[Unity] Foreach를 써도 괜찮은 걸까? (0) | 2024.01.29 |
[Unity] Unity Analytics Service (0) | 2024.01.29 |