공부/인프런 - Rookiss

Part 3-1-1. 싱글톤

셩잇님 2023. 8. 11. 19:16
반응형

 

 

게임 매니저

👉 전역으로 선언하여 게임 전체를 관리하는 스크립트!

 게임 매니저는 '컴포넌트'이므로 딱히 어떠한 오브젝트에 꼭 붙어야 하는 이유는 없지만, 모노비헤이비어를 이용하기 위해 실체는 없지만 Scene에 존재하긴 하는 빈 오브젝트를 만들어서 매니저 스크립트를 컴포넌트로서 붙이고 사용하는 것을 권장한다. 따라서 매니저 스크립트를 붙일 용도의 빈 오브젝트를 만들자.

 


 

싱글톤

게임 매니저 스크립트가 붙은 오브젝트를 가져오는 것.
현재 “@Managers” 라는 빈 오브젝트에 게임 매니저로 쓸 Managers 라는 C# 스크립트를 붙여준 상태이다.

 

Player.cs

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

public class Player : MonoBehaviour
{
    void Start()
    {
        GameObject obj = GameObject.Find("@Managers");
        Managers mag = obj.GetComponent<Managers>();
    }

    void Update()
    {
        
    }
}

 

obj 👉 현재 씬에 존재하는 오브젝트(GameObject) 중에서 이름이 “@Managers”인 오브젝트

Find 함수

  • GameObject 타입의 함수로서 클래스 함수라 GameObject.Find(String) 식으로 사용 된다. 현재 씬에서 인수와 일치하는 이름을 가진 오브젝트를 찾아 리턴해준다. 
  • 단, 활성화 되어있는(Active) 오브젝트들 중에서만 찾아준다.

 

 

mag 👉 obj 오브젝트의 Managers 컴포넌트를 mag에 담았다.

  • C# 스크립트도 컴포넌트다.
  • 즉, @Managers 오브젝트의 Managers 스크립트를 참조하게 된다.

 

 


 

[싱글톤 구현하기]
게임 매니저는 게임 전반을 관리하기 때문에 게임 매니저 스크립트는 딱 하나만 필요하므로, static을 이용하여 만들어주고 여러 곳에서 이 동일한 인스턴스를 참조하게 한다.

  • 게임 매니저 오브젝트의 스크립트 컴포넌트를 여러 곳에서 공유할 수 있도록 static을 이용하여 만들어 준다.
  • 단 하나만 존재하는 이 게임 매니저 컴포넌트를 리턴 받을 수 있는 static 함수를 만들어 둔다. 그렇지만, 다른 곳에서 사용할 수 있도록 public 붙어주어야 한다.

 

 

[구현 방법 1]

Managers.cs 👉 게임 매니저로서 동작할 스크립트

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

public class Managers : MonoBehaviour
{
    static Managers Instance;  // 유일성이 보장된다.
    public static Managers GetInstance() { return Instance; }  // 유일한 매니저를 갖고 온다.

    void Start()
    {
        GameObject obj = GameObject.Find("@Managers");
        Instance = obj.GetComponent<Managers>();   // Instance에 스크립트를 할당하는 곳
    }

    void Update()
    {
        
    }
}

 

싱글톤으로 만들 대상은 “@Managers” 오브젝트에 붙어 있는 Managers.cs 스크립트이다. 여러 곳에서 해당 인스턴스에 대해 공유할 수 있도록 static으로 선언한다.

static Managers Instance

 

여러 곳에서 동일한 인스턴스를 리턴받아 사용할 수 있도록 이 인스턴스를 리턴하는 static public 함수를 만들어준다. 이렇게 할 경우 클래스 함수가 되므로 다른 여러 곳에서 클래스이름.GetInstance() 로 사용할 수 있게 된다.

 

Instance에 Managers.cs 할당하는 것을 딱 한군데에서만 해주면 단 하나만 존재하는게 보장된다. 👉 Managers.cs에서 할당해준다.

GameObject obj = GameObject.Find("@Managers");
Instance = obj.GetComponent<Managers>();   // Instance에 스크립트를 할당하는 곳

 

 

Player.cs

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

public class Player : MonoBehaviour
{
    void Start()
    {
        Managers mag = Managers.GetInstance();  // Instance 리턴 받아 사용
    }
}

 

“@Managers” 오브젝트에 붙어 있는 Managers 스크립트가 할당 되어 있는 Instance를 가져와 사용하면 된다. 👉 Managers.GetInstance()

 

해당 방법의 문제점
1. 만약 @Managers 오브젝트가 없다면 오브젝트가 null이 된다.
👉 이럴 경우 코드를 통하여 @Managers 직접 오브젝트를 만들어 주자.

 

2. @Managers가 있긴 있어도 Managers.cs 가 붙어있지 않다면 Instance 가 null이 된다.
👉 이럴 경우 마찬가지로 코드를 통하여 @Managers 오브젝트에 Managers.cs 컴포넌트를 붙여준다 😎

 

 

[구현 방법 2 - 개선]

Managers.cs 👉 게임 매니저로서 동작할 스크립트

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

public class Managers : MonoBehaviour
{
    static Managers Instance;  // 유일성이 보장된다.
    public static Managers GetInstance() // 유일한 매니저를 갖고 온다.
    {
        Init();
        return Instance;
    }  

    void Start()
    {
        Init();
    }

    void Update()
    {
        
    }

    static void Init()
    {
        if (Instance == null)
        {
            GameObject obj = GameObject.Find("@Managers");
            if (obj == null)
            {
                obj = new GameObject { name = "@Managers" };
                obj.AddComponent<Managers>();
            }

            DontDestroyOnLoad(obj);
            Instance = obj.GetComponent<Managers>();
        }
    }
}

 

GetInstance(), Start() 두 함수에서 Init()을 호출하는 이유
👉 Managers.cs의 Start() 함수가 실행되기도 전에 다른 스크립트에서 게임 매니저 Instance를 사용해야 할 일이 생긴다면 먼저 그 곳에서 Instance를 만들어 두도록 하기 위하여.

 

 

Instance가 아직 없을 경우 인스턴스를 만들어주자. 만약 있을 경우 다음과 같은 과정을 무시하고 지나가면 된다. 👉 if (Instance == null)

  • Instance가 단 하나만 존재할 수 있도록 보장이 된다.
  • Instance 만들기
  • 👉 @Managers라는 오브젝트가 없다면 👉 if (obj == null)
  • 👉👉 직접 만들고 Managers.cs 컴포넌트도 붙여주자.
obj = new GameObject { name = "@Managers" };
obj.AddComponent<Managers>();
  • DontDestroyOnLoad 함수를 이용해서 “@Managers” 오브젝트가 씬이 변경되도 삭제되지 않고 유지되도록 안전 장치를 걸어주자. 👻
  • 마지막으로 Instance에 “@Managers” 오브젝트에 붙어 있는 Managers.cs 가져오기

 

 

Player.cs

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

public class Player : MonoBehaviour
{
    void Start()
    {
        Managers mag = Managers.GetInstance();
    }
}

 

 

[구현 방법 3 - 프로퍼티 사용]

Managers.cs 👉 게임 매니저로서 동작할 스크립트

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

public class Managers : MonoBehaviour
{
    static Managers s_instace;  
    public static Managers Instance
    {
        get
        {
            Init();
            return s_instace;
        }
    }

    void Start()
    {
        Init();
    }

    void Update()
    {
        
    }

    static void Init()
    {
        if (s_instace == null)
        {
            GameObject obj = GameObject.Find("@Managers");
            if (obj == null)
            {
                obj = new GameObject { name = "@Managers" };
                obj.AddComponent<Managers>();
            }

            DontDestroyOnLoad(obj);
            s_instace = obj.GetComponent<Managers>();
        }
    }
}

 

GetInstance() 함수를 프로퍼티로 변경! 🤠

Player.cs

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

public class Player : MonoBehaviour
{
    void Start()
    {
        Managers mag = Managers.Instance;
    }
}

 

변경된 프로퍼티를 이용하여 기존코드에서 Managers mag = Managers.Instance로 변겅해준다. 😑

 

 

 

반응형