[Unity] 스테이지 전환 구조 설계기 — Dictionary 기반 구현

shin0112·2025년 10월 30일

Unity

목록 보기
7/13

✨ 들어가며

오늘은 StageScene 구조를 정리하다가 “각 스테이지 정보를 어떻게 불러오고 관리할까?”에 대해 고민했다.

Scene 안에는 StageManager 오브젝트가 있고, 그 하위에는 여러 개의 Stage 프리팹이 들어 있다.

Stage를 선택하면 그 스테이지로 진입하고, 진행 중에는 나머지 스테이지가 간섭하지 않게 해야 한다.
즉, “어떤 스테이지를 선택했는가”에 따라 그 스테이지만 활성화하고 나머지는 비활성화하는 구조가 필요했다.

1️⃣ 구조 고민

처음엔 크게 세 가지 방향을 생각했다.

  1. Instantiate 방식
    → 프리팹을 Resources에서 불러와서 즉시 생성
    → 초기화는 쉽지만 로딩 비용이 발생

  2. 활성화/비활성화 방식
    → 씬 안에 미리 모든 스테이지를 넣고, 선택된 스테이지만 SetActive(true)
    → 빠르고 간단하지만, 메모리 상에 모든 스테이지가 상주

  3. 데이터 기반 로드 (JSON / ScriptableObject)
    → 스테이지 정보만 저장하고 필요할 때 불러오는 방식
    → 구조는 깔끔하지만, 지금은 오버엔지니어링에 가까움

결국 지금은 스테이지 수가 많지 않으니까, 두 번째 방법(활성화/비활성화)을 택했다.
로딩 속도가 빠르고, 구조도 단순하기 때문이다.

2️⃣ StageManager 구조 설계

StageManager는 전체 스테이지를 관리하고, StageController는 각 스테이지 내부 로직을 담당한다.

StageManager는 게임의 진행 상태에 따라 각 StageController의 메서드를 호출한다.

public enum GameState
{
    None,
    Ready,
    Play,
    Dead,
    Stop,
}
public void Init(GameManager gameManager)
{
    _gameManager = gameManager;

    var stages = GetComponentsInChildren<StageController>(true); // 비활성화 포함
    _stages.Clear();

    foreach (var stage in stages)
    {
        int id = stage.StageId;
        if (_stages.ContainsKey(id))
        {
            Debug.Log($"중복된 ID: {id}");
            continue;
        }
        _stages[id] = stage;
        stage.gameObject.SetActive(false);
    }
}

public void SelectStage(int id)
{
    _currentStage = _stages[id];
    _currentStage.gameObject.SetActive(true);
    StartStage();
}

3️⃣ 설계 의도

이 구조의 핵심은 “모든 Stage를 Dictionary로 캐싱하고, 필요할 때만 활성화한다”는 점이다.

  • 로딩 속도는 매우 빠르고
  • 중복 호출도 막을 수 있고
  • Stage 간 상태 간섭이 없다.

또한 GameState를 따로 분리했기 때문에 UI나 ScoreManager에서도 손쉽게 게임 상태를 참조할 수 있다.

나중에는 여기에 이벤트 시스템(OnGameStateChanged)을 추가해서 UI가 자동으로 반응하도록 확장할 수도 있을 것 같다.

✍️ 마치며

이번 구조는 ‘모든 걸 새로 불러오기’보다 ‘이미 있는 걸 효율적으로 관리하기’에 초점을 맞췄다.

지금은 스테이지가 6개뿐이지만, 나중에 규모가 커지면 Addressable이나 JSON 로드 방식으로 자연스럽게 확장할 수 있는 구조이기도 하다.

튜터님께 들은 바로는, 실제 프로젝트에서는 대부분 외부 리소스에서 스테이지를 로드하는 방식을 쓴다고 한다.
예를 들어 JSON에 프리팹 이름만 저장해두고 그 이름을 기준으로 로딩하거나, ScriptableObject를 통해 데이터를 직렬화해 관리하는 식이다.

하지만 지금처럼 소규모 프로젝트에서는 복잡한 설계보다는 “돌아가는 구조”가 더 중요하다.
Dictionary 기반 구조만으로도 충분히 유연하게 관리할 수 있었고, 무엇보다 개발 속도가 빨랐다.

다만 나중에 시간이 생기면 외부 리소스 로드나 ScriptableObject 기반으로 이 구조를 확장해보는 것도 좋은 도전이 될 것 같다.

0개의 댓글