Project/OUTPOST

[Unity] 웨이브부터 헬기 탈출까지 하나의 상태 흐름으로 묶기

유탐기 2026. 8. 29. 19:44
반응형

OUTPOST에서는 일정 수의 좀비를 처치하는 것만으로 게임이 끝나지 않습니다. 준비 시간, 웨이브 방어, 발전기 활성화, 헬기 착륙장 이동, 탈출이라는 서로 다른 목표가 순서대로 이어지도록 구성했습니다.

1. 게임의 진행 상태를 명확하게 구분하기

게임의 모든 진행 상황을 여러 개의 bool 값으로 관리하면 조합이 복잡해질 수 있습니다. 예를 들어 isWavePlaying, isGeneratorReady, isEscaping이 동시에 true가 되면 어떤 상태를 우선해야 하는지 판단하기 어렵습니다.

그래서 GameCycleDirector에서는 CyclePhase 열거형으로 진행 단계를 구분했습니다.

public enum CyclePhase
{
    Preparing,
    Defending,
    GeneratorActivation,
    Cleared,
    HelipadTransit,
    Extraction,
    Victory,
    Failed
}

이 구조의 핵심은 현재 게임이 어떤 일을 해야 하는지 하나의 단계로 표현한다는 것입니다. UI, 적 생성, 발전기 작동 여부, 배경음악이 이 상태를 참조할 수 있습니다.

2. 웨이브가 끝난 뒤 다음 목표를 선택하기

GateWaveSpawner에서 WaveCompleted 이벤트를 발행하면 GameCycleDirector.OnWaveCompleted()가 호출됩니다. 그 뒤 ClearIntermissionAsync()에서 현재 사이클에 맞는 다음 행동을 결정합니다.

void OnWaveCompleted()
{
    if (Phase == CyclePhase.Defending)
        StartFlow(ClearIntermissionAsync);
}

async UniTask ClearIntermissionAsync(CancellationToken token)
{
    if (CycleNumber == 1 && !_generatorActivated)
    {
        Phase = CyclePhase.GeneratorActivation;
        PhaseTimeRemaining = 0f;
        return;
    }
    // 이후 중간 준비시간을 거쳐 다음 웨이브 또는 탈출로 진행
}

코드는 일부 생략한 발췌입니다. 첫 번째 웨이브가 끝나면 발전기 활성화를 요청하고, 활성화된 이후 다음 사이클로 넘어갑니다. 마지막 사이클에서는 헬기 착륙장으로 이동해야 다음 진행이 시작됩니다.

3. 발전기와 헬기 이동도 상태에 따라 제어하기

GeneratorActivationZone은 플레이어가 영역에 들어왔더라도 무조건 발전기를 켜지 않습니다. CanActivateGenerator가 true인 상태인지 먼저 검사합니다.

마지막 사이클에서는 HelipadTransit 단계로 이동한 뒤, 플레이어가 착륙장에 도착했는지를 HelicopterExtractionController.IsPlayerAtLandingZone()으로 확인합니다. 이후 준비·방어 단계가 진행되고, 마지막 웨이브 완료 시 Extraction으로 들어갑니다.

이처럼 트리거 충돌 자체가 진행을 결정하지 않고, 현재 상태와 함께 조건을 검사하게 했습니다.

4. 다른 시스템도 같은 상태를 읽게 하기

게임 음악은 GameMusicBridge가 CyclePhase를 확인해 결정합니다. 예를 들어 준비 상태에서는 탐색 BGM, 방어 단계에서는 전투 BGM, 패배 상태에서는 패배 음악으로 전환합니다.

음악 파일 전환 자체는 MusicManager가 두 AudioSource를 이용해 크로스페이드합니다. 즉 GameCycleDirector가 음악을 직접 재생하지 않고, 음악 시스템이 현재 상태를 보고 적절한 음악을 선택합니다.

또 GameCycleDirector.Update()에서는 플레이어 사망이나 발전기 파괴를 감지해 Failed 상태로 바꾸고 진행 중인 작업을 취소합니다.

5. 비동기 진행 작업이 겹치지 않도록

StartFlow()는 새 비동기 흐름을 시작하기 전에 이전 CancellationTokenSource를 취소·해제합니다. 이 덕분에 웨이브 전환과 탈출 진행이 중복으로 시작되는 상황을 줄일 수 있습니다.

_flowCts?.Cancel();
_flowCts?.Dispose();
_flowCts = CancellationTokenSource.CreateLinkedTokenSource(
    this.GetCancellationTokenOnDestroy());
flow(_flowCts.Token).Forget();

 


정리

웨이브 준비 → 방어 → (발전기 활성화) → 다음 웨이브 → 착륙장 이동 → 마지막 방어 → 헬기 탈출의 흐름을 GameCycleDirector에서 관리했습니다. 게임 진행의 결정은 Director가 맡고, 스폰·BGM·헬기 연출은 별도 시스템에서 처리하도록 구분했습니다.


참고한 구현 코드

반응형