게임 개발이 진행될수록 GameManager, UIManager, AudioManager... 수많은 매니저들이 생겨납니다. 각각을 따로 관리하다 보면 코드가 뒤엉키고 유지보수가 어려워지곤 합니다. 이 모든 매니저를 한 곳에서 깔끔하게 관리하는 방법에 대해 포스팅 해보겠습니다.
GameManager, UIManager, InputManager,… 등을 한 곳에서 관리하기 위해 이 Manager들을 관리하는 클래스를 Managers 라고 하겠습니다. Managers는 어디서든 편하게 접근할 수 있어야 하는데, 이를 구현하는 여러 시도들을 소개하고 가장 괜찮은 방법을 말씀드리겠습니다.
GameObject go = GameObject.Find("Manager"); // 씬에 존재하는 Managers 오브젝트를 찾기
Managers mg = go.GetComponent<Managers>(); // 해당 오브젝트에 부착된 스크립트 컴포넌트 받기
Managers
클래스를 사용하기 위해, 씬에 빈 오브젝트를 생성하여 Managers
스크립트를 컴포넌트로 부착한다.Managers
를 찾아와 사용할 수 있다.하지만 이름으로 오브젝트를 찾는 건 비용이 매우 큽니다. 각각의 스크립트에서 Managers
가 필요할 때마다 이름으로 찾아와야 하므로 비추천되는 방식입니다.
static Managers Instance;
public static Managers GetMg() { return Instance; }
// Start() 등에서 Instance 초기화
void Start()
{
Instance = this;
}
이 코드에서는 씬에 여러 개의 Manager
오브젝트가 존재한다면, 정적 변수인 Instance
에 여러 개의 Manager
오브젝트가 덮어 씌워지게 됩니다. 즉, Manager
, Manager(1)
, Manager(2)
… 중 마지막으로 Start()
를 실행한 오브젝트가 Instance
에 자신을 대입하게 됩니다.
static Managers Instance;
public static Managers GetMg() { return Instance; }
void Start()
{
GameObject go = GameObject.Find("Manager"); // 씬에서 이름으로 오브젝트를 찾기
Instance = go.GetComponent<Managers>();
}
씬에서 Manager
오브젝트를 찾은 뒤, 정적 변수인 Instance
에 Managers
컴포넌트를 넣어줍니다. 씬에 여러 개의 Manager
가 있더라도 첫 번째 오브젝트를 찾아오기 때문에, 덮어 씌워지는 문제가 줄어들 수 있습니다.
그러나, 씬에 Manager
오브젝트가 없다면 null을 반환하여, NullReferenceException
이 발생하게 됩니다. null인지 확인하는 절차를 추가해줘야 합니다.
이 코드에서는 s_instance
에 접근하기 전 null 인지 확인합니다. 만약 s_instance
에 오브젝트가 할당된 상태가 아니라면 씬에서 Manager
오브젝트를 찾아와 s_instance
에 오브젝트를 할당해줍니다. 심지어, 씬 상에 Manager
가 존재하지 않았다면 Manager
를 생성하고 Managers
스크립트를 컴포넌트로 부착해주며 오류 가능성을 차단합니다.
static Manager s_instance;
public static Manager Instance
{
get
{
Init();
return s_instance;
}
}
void Start()
{
Init();
}
static void Init()
{
if (s_instance == null)
{
GameObject go = GameObject.Find("Manager");
if (go == null)
{
go = new GameObject { name = "Manager" }; // "Manager" 이름의 오브젝트 생성
go.AddComponent<Managers>(); // Managers 스크립트를 컴포넌트로 부착
}
DontDestroyOnLoad(go);
s_instance = go.GetComponent<Managers>();
}
}
지금까지 Managers
클래스 초기화와 생성하는 부분을 작성해봤습니다. 이제 이 클래스에서 다양한 ~~~ Manager
들을 어떻게 관리할지 얘기하겠습니다.
InputManager
와 ResourceManager
가 필요하다고 가정합니다. 이 클래스 다이어그램에서 Managers
는 여러 ~Manager
들을 private
변수로 갖고, 다른 클래스에서 프로퍼티를 통해 접근할 수 있게 하고 있습니다. 이를 통해, 모든 스크립트에서 Managers.Input
과 같이 접근하여 매니저들의 기능을 이용할 수 있게 됩니다.
// PlayerController.cs
void SomeMethod()
{
Managers.Input.OnKeyUpdate(); // InputManager의 OnKeyUpdate()을 사용한다.
Managers.Resource.Instantiate(); // ResouceManager의 Instactiate()을 사용한다.
}
여러 매니저들을 한 곳에서 관리한다면 코드의 유지보수성을 높일 수 있습니다. 예를 들어 플레이어의 입력을 감지하는 InputManager
의 경우, Managers
의 Update()
에서 키 입력이 있는지를 판별하고 입력 발생 시 이를 이벤트로 알린다면, 입력 감지가 필요한 모든 클래스는 입력을 감지하는 로직을 작성할 필요가 없게 됩니다. 이에 대해서는 다른 포스팅으로 더 자세히 말씀드리겠습니다.
여러 Manager 클래스들을 관리하는 Managers
클래스의 작성 방법과 관리 방법에 대해 알아봤습니다. 이상으로 포스팅 마치겠습니다.