싱글톤 패턴은 초보 개발자들이 가장 많이 쓰는 디자인 패턴이 아닐까 싶다. 

 

클래스 구조를 짜다보면 다른 클래스의 함수를 사용해야 할 수도 있고, 전체 클래스들이 공유하는 전역변수가 필요할 수도 있다.

규모가 작은 게임에서는 public으로 변수를 만든 다음 유니티 Inspector에서 드래그 앤 드롭으로 의존관계를 만들수도 있지만,

게임이 점점 복잡해진다면 다른 클래스를 참조하는 변수가 너무 많이 만들어져도 문제이다.

public 변수를 만든거 자체가 메모리를 사용하게 되는 것이고, 만약 클래스를 변경하거나 삭제할 때 일일이 다 바꿔줘야 해서 개발할 때 시간낭비가 될 수도 있다.

 

클래스 구조에서 공통적으로 사용하는 전역변수나 리소스, 데이터, 아니면 게임 전체를 관장하는 매니저 클래스는 싱글톤으로 따로 빼는게 도움이 될 수도 있다.

싱글톤을 이용하면 임의의 클래스에서 내가 만든 싱글톤 인스턴스를 사용할 수 있다.

 

 

유니티에서 싱글톤을 사용하는 방법은 2가지가 있다.

1. 이 싱글톤 클래스가 여느 유니티 c#스크립트처럼 Monobehaviour를 상속받아서 Hierarchy에 존재하게 하는 것.

2. Monobehaviour을 상속받지 않고 Hierarchy에 존재하지 않게 하는 것.

 

본 예제에서는 게임시작, 일시정지 등 게임의 흐름을 관장하는 GameMgr이라는 클래스를 예시로 들겠다.

 

1번째 방법(Monobehaviour를 상속받아서 Hierarchy에 존재하게 되는 싱글톤 인스턴스)

 

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

public class GameMgr : MonoBehaviour
{
    //게임매니저의 인스턴스를 담는 전역변수(static 변수이지만 이해하기 쉽게 전역변수라고 하겠다).
    //이 게임 내에서 게임매니저 인스턴스는 이 instance에 담긴 녀석만 존재하게 할 것이다.
    //보안을 위해 private으로.
    private static GameMgr instance = null;

    void Awake()
    {
        if (null == instance)
        {
            //이 클래스 인스턴스가 탄생했을 때 전역변수 instance에 게임매니저 인스턴스가 담겨있지 않다면, 자신을 넣어준다.
            instance = this;

            //씬 전환이 되더라도 파괴되지 않게 한다.
            //gameObject만으로도 이 스크립트가 컴포넌트로서 붙어있는 Hierarchy상의 게임오브젝트라는 뜻이지만, 
            //나는 헷갈림 방지를 위해 this를 붙여주기도 한다.
            DontDestroyOnLoad(this.gameObject);
        }
        else
        {
            //만약 씬 이동이 되었는데 그 씬에도 Hierarchy에 GameMgr이 존재할 수도 있다.
            //그럴 경우엔 이전 씬에서 사용하던 인스턴스를 계속 사용해주는 경우가 많은 것 같다.
            //그래서 이미 전역변수인 instance에 인스턴스가 존재한다면 자신(새로운 씬의 GameMgr)을 삭제해준다.
            Destroy(this.gameObject);
        }
    }

    //게임 매니저 인스턴스에 접근할 수 있는 프로퍼티. static이므로 다른 클래스에서 맘껏 호출할 수 있다.
    public static GameMgr Instance
    {
        get
        {
            if (null == instance)
            {
                return null;
            }
            return instance;
        }
    }

    public void InitGame()
    {

    }

    public void PauseGame()
    {

    }

    public void ContinueGame()
    {

    }

    public void RestartGame()
    {

    }

    public void StopGame()
    {

    }
}

 

사용 예시 :

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

public class UIMenu : MonoBehaviour
{
    //pause 버튼을 누르면 게임 일시정지
    public void OnClickBtnPause()
    {
        GameMgr.Instance.PauseGame();
    }
}

 

 

2번째 방법 (Monobehaviour를 상속받지 않아서 Hierarchy에 존재하지 않게 만드는 방법)은 아래와 같다.

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

public class GameMgr
{
    //게임매니저의 인스턴스를 담는 전역변수(static 변수이지만 이해하기 쉽게 전역변수라고 하겠다).
    //이 게임 내에서 게임매니저 인스턴스는 이 instance에 담긴 녀석만 존재하게 할 것이다.
    //보안을 위해 private으로.
    private static GameMgr instance;

    //게임 매니저 인스턴스에 접근할 수 있는 프로퍼티. static이므로 다른 클래스에서 맘껏 호출할 수 있다.
    public static GameMgr Instance
    {
        get
        {
            if(null == instance)
            {
                //게임 인스턴스가 없다면 하나 생성해서 넣어준다.
                instance = new GameMgr();
            }
            return instance;
        }
    }

    //생성자를 하나 만들어줘서 원하는 세팅을 해주면 좋다.
    public GameMgr()
    {

    }

    public void InitGame()
    {

    }

    public void PauseGame()
    {
       
    }

    public void ContinueGame()
    {

    }

    public void RestartGame()
    {

    }

    public void StopGame()
    {

    }
}

 

사용법은 1번째 방법과 같다.

 

 

 

Monobehaviour를 상속받지 않는 경우의 좋은 점 :

 

1. 우선 씬 이동시의 신경을 안 써도 된다. 씬 이동을 했을 때 그 씬의 Hierarchy에 같은 싱글톤 클래스가 존재한다면, 기존씬에서 쓰던 인스턴스를 계속 쓸지, 아니면 새로운 씬의 Hierarchy에 있는 인스턴스를 쓸지를 선택해야한다(보통은 기존 씬의 것을 사용하는 것 같다). 하지만 Monobehaviour를 상속받지 않고 메모리상에만 존재하게 한다면 이런 선택의 경우를 고려하지 않아도 돼서 편하다.

2. 현재 상용버전의 유니티 오브젝트라면 모두 갖게 될 Transform 컴포넌트를 안 가져도 되니 쓸데 없는 메모리 점유를 안해도 된다는 것이다(정말 미미한 극세사 도움이겠지만..). 하지만 눈에 안 보이면 헷갈릴 수도 있으니 보통은 1번째 방법을 사용한다.

 

 

 

 

싱글톤의 문제점 :

하나의 싱글톤에 너무 많은 기능, 너무 많은 데이터를 넣으면, 훗날 프로젝트의 규모가 커졌을 때 절망을 느낄 수 있다. 우선 하나의 클래스가 하나의 일을 한다는 Single Responsibility Principle과, 수정에는 닫히고 확장에는 열려야 한다는 Open-Closed Principle 등의 원칙을 어길 수 있으며, 클래스들과 싱글톤, 그리고 싱글톤이 가지고 있는 클래스 인스턴스들간의 의존도가 복잡해져서 게임 업데이트를 하려면 게임 전체를 갈아엎어야 될 수도 있다. 또한 싱글톤은 게임이 종료되지 않는 한 계속해서 메모리를 점유하고 있으므로, 싱글톤의 남발은 메모리를 비효율적으로 사용하게 한다.

+ Recent posts