지난 수요일에 친구들과 디자인패턴에 대해 스터디를 하면서 제 스스로로 잘 알고 사용하고 있으면서도 잘 모르겠는 아리송한 싱글톤 패턴에 대해서 다시 한번 정리하고자 합니다.
저는 예전에 게임을 구현하면서 유니티에서 C#으로 싱글톤으로 어떻게 구현하는지 찾아보면 원하는 스크립트에 static 멤버변수 instance를 선언하여 초기화한 후 다른 씬으로 넘어갈때 계속 유지할수있게 어떤 처리를 해서 계속 사용하는 방법을 자주 볼 수 있었습니다.
//// 구글링해서 나오는 대부분의 유니티 싱글톤 느낌
public class GameManager : MonoBehaviour
{
// instance 멤버변수는 private하게 선언
private static GameManager instance = null;
private void Awake()
{
if (null == instance)
{
// 씬 시작될때 인스턴스 초기화, 씬을 넘어갈때도 유지되기위한 처리
instance = this;
DontDestroyOnLoad(this.gameObject);
}
else
{
// instance가, GameManager가 존재한다면 GameObject 제거
Destroy(this.gameObject);
}
}
// Public 프로퍼티로 선언해서 외부에서 private 멤버변수에 접근만 가능하게 구현
public static GameManager Instance
{
get
{
if (null == instance)
{
return null;
}
return instance;
}
}
(물론 이 방법이 무조건 필요할 때도 있습니다만 저의 상황은 약간 달랐습니다.)
하지만 저의 경우에는 싱글톤으로 구현한 스크립트에서 간단한 데이터 혹은 연산 함수 등만 존재했기 때문에 굳이 게임오브젝트가 존재하지 않도록 하는 방법을 찾아야 했습니다.
다시 한번 싱글턴 패턴에 키워드를 정리해보자면 3가지가 있는 것 같습니다.
1. 해당 클래스의 instance가 하나만 존재하고, 어디서든지 그 instance에 접근 할 수 있어야 합니다.
2. public으로 지정된 생성자가 없어야합니다.
3. instance를 만들어 놓는 것이 아닌 요청할 때 생성해야 합니다. (게으른 싱글톤이라고 합니다.)
위에 요소 3가지를 참고하면 GameObject가 존재하지않아도 충분히 구현이 가능합니다.
// 유니티 GameObject에 붙이는 스크립트가 아니기때문에 MonoBehaviour 제거
public class Singleton
{
// Static 자기자신 클래스 멤버변수
private static Singleton instance;
private Singleton()
{
// 초기화 - 싱글톤을 처음 사용할때 호출
}
public static Singleton GetInstance()
{
// 해당 싱글톤 사용할때 호출, instance == null 일때만 생성
if(instance == null)
{
instance = new Singleton();
}
return instance;
}
}
위와 같이 구현하게 되면 필요할때 Singleton 클래스를 생성하고 게임이 끝날때까지 유일한 instance를 가진 클래스가 됩니다.
하지만 여기서 모든 클래스에 저렇게 다 싱글톤 구현부가 존재하는 것보다는 서브클래스를 만들 수 있게, 상속 가능한 버전을 만들었습니다.
// 제네릭 클래스로 구현한 싱글톤
public class Singleton<T> where T : new()
{
protected static T instance;
protected static T GetInstance()
{
if(instance == null)
{
instance = new T();
}
return instance;
}
}
그에 따른 서브클래스 GameManager입니다.
// 제네릭 클래스 싱글톤을 사용해서 instance가 GameManager임을 짐작할 수 있습니다.
public class GameManager : Singleton<GameManager>
{
protected int gold = 0;
public GameManager()
{
Debug.Log("게임 매니저 초기화");
// 초기화
}
// 편리한 사용을 위해 모든 함수를 Static 매소드로 구현했습니다.
public static int GetGold()
{
return GetInstance().gold;
}
public static void AddGold(int _gold)
{
GetInstance().gold += _gold;
}
}
자 그러면 마지막으로 실전 테스트!
체크해야할 사항은 2개입니다.
1. Scene을 넘어가도 instance가 유지되는가
2. 정말 GameObject가 없어도 되는가
간단하게 FirstScene에서 gold가 몇인지 확인하고 100 gold를 더해준 다음에 SecondScene으로 넘어갑니다.
마찬가지로 SecondScene에서 gold가 몇인지 확인하고 100 gold를 또 더해줍니다.
3번째 사진에서 확인 할 수 있는것은 Scene을 넘어가도 instance가 유지되고 있는 것을 확인하실 수 있습니다.
4번째 사진은 SecondSceneObject 스크립트가 들어있는 오브젝트를 제외하고는 따로 GameManager가 오브젝트로 존재하지 않는 것을 확인 할 수 있습니다.
이렇게 싱글톤 패턴을 다시알고 제가 원하는 싱글톤 게임매니저를 작성할 수 있게 되었습니다!
기회가 된다면 MonoBehaviour가 필요한 매니저도 있기때문에
필요할때만 생성하는 MonoBehaviour 싱글톤 버전도
준비 해보겠습니다.
'게임 개발 > 디자인 패턴' 카테고리의 다른 글
[디자인패턴] 6. 유니티에서 옵저버패턴 (0) | 2021.08.16 |
---|---|
[디자인패턴] 5. 유니티에서 추상팩토리 (0) | 2021.07.27 |
[디자인패턴]4. 유니티에서 팩토리 메소드 패턴 (7) | 2021.07.21 |
[디자인패턴]02. - FSM (Finite State Machine) 유한상태기계 (0) | 2019.03.01 |
[디자인패턴]01. 빌더패턴(builder pattern) (0) | 2019.02.24 |