유니티는 프로젝트를 구성함에 있어 씬(Scene)이라는 큰 단위로 구분할 수 있습니다.
물론 씬 하나에 모든 것을 집어넣을 수도 있지만, 그만큼 연산해야 되는 대상이 많아져 성능에 영향을 줄 수 있습니다.
실제로 작업에 있어서도 씬 위에 모든 게 다 늘어놔져 있으면 필요한 오브젝트를 찾기도 어렵습니다.
이에 적절하게 씬을 나눠서 구성하는 것이 필요합니다.
많은 게임들이 비슷한 3단계의 구분을 거쳐 게임이 진행됩니다.
프로젝트 마다 다른 이름이 붙겠지만 여기서는 Title - Lobby - InGame의 이름으로 구분하고 있습니다.
단일 게임으로는 제가 가장 오랜 기간 즐기고 있는 하스스톤을 예시로 보겠습니다.
게임 시작 시 최초로 보여질 화면을 구성하는 씬입니다.
보통 회사 로고, 타이틀 화면이 표시되며 로그인이나 인증이 필요하다면 여기서 이루어집니다. 이후 게임 실행에 필수적인 데이터와 시스템을 로드하는 과정을 거치면서, 바로 로비 등 게임에 진입하였을 때 발생할 수 있는 렉과 같은 문제를 방지하는 허들이 됩니다.
특히 게임 전체에 걸쳐 사용될 데이터나 시스템 오브젝트를 배치하고 static으로 존재할 오브젝트가 잘 구성되고 관리될 수 있도록 합니다.
게임 내 모든 컨텐츠에 접근할 수 있는 허브(Hub) 역할을 하는 씬입니다.
게임 진척도 확인, 핵심 게임 컨텐츠 진입, 그외의 모든 컨텐츠에 접근이 가능하도록 하는 기능이 필요합니다.
게임의 핵심 콘텐츠가 진행될 씬입니다.
게임 플레이가 끝나면 다시 로비로 돌아올 수 있는 구조여야 합니다. 각 콘텐츠가 다루는 애셋 역시 게임에 진입하는 과정에서 부르면 매번 모든 리소스를 메모리에 올려둘 필요가 없게 됩니다. 맵 또는 스테이지마다 새로운 씬이 존재하는 것은 비효율적이나 전혀 다른 성격의 컨텐츠가 진행된다면 씬이 구분되는 것이 좋습니다.
프로젝트의 머리-가슴-배를 나누었다면 이를 유기적으로 이어줄 기능들이 필요합니다.
SceneLoader
각 씬을 로드하고 초기화 하는 역할을 합니다.
특히 InGame씬에서 TimeScale이 바뀔 경우, 다른 씬으로 전환 또는 다시 로드하는 과정에서 초기화하는 역할을 합니다.
여기에 프로젝트를 진행하면서 필요한 기능으로 Logger와 SingletonBehaviour가 있습니다.
Logger
로그를 어떻게 남길 것인지 관리하는 클래스입니다.
단순히 UnityEngine의 Debug method를 호출해서 사용하는 것이 아니라, 필요한 형태로 래핑(wrapping)해서 사용하면서 일관된 형식을 유지합니다.
또한 Error 로그를 제외한 단순 로그들은 조건부 컴파일 과 커스텀 스크림팅 심볼을 활용해 빌드 시 포함이 되지 않게 할 수 있습니다.
실제로 빌드 시 로그 메서드가 이슈를 만들었던 경험이 있어 이 로거 클래스 구성이 무엇보다 와닿는 내용이었습니다.
SingletonBehaviour< T >
MonoBehaviour를 상속하면서 싱글톤 패턴으로 구현될 클래스의 기반이 될 제네릭 클래스
제네릭 제약 조건을 재귀적인 형태로 두어 잠재적 오류를 방지하고, 싱글톤 패턴의 올바른 사용을 강제합니다.
제약이 없거나 MonoBehaviour 제약만 두었을 경우public class IncompleteManager : SingletonBehaviour<MonoBehaviour> {}
와 같은 불완전한 타입 체크가 발생할 수 있습니다.
싱글톤 패턴 (Singleton Pattern)
클래스의 인스턴스가 프로그램 내에서 단 하나만 존재하도록 보장하는 디자인 패턴.
1) 자신의 유일한 인스턴스에 대한 전역 접근점을 제공하며 일반적으로 private 생성자를 사용하여 외부에서의 인스턴스 생성을 방지합니다.
2) 단일 인스턴스는 보통 static 멤버를 통해 접근되며, lazy initialization을 사용하여 필요할 때만 생성됩니다.
3) 전역 상태를 관리하거나 리소스를 공유할 때 주로 사용됩니다.
내용은 인프런의 유니티 시스템 프로그래밍 Pt.1 - 상용 게임 구현을 위한 핵심 시스템 올인원 패키지의 강의를 기반으로 하고 있습니다.