본문으로 건너뛰기
게임 프로젝트 기초집LESSON 11

게임 상태 흐름: 시작·플레이·일시정지·게임 오버 전환 만들기

난이도초급 프로젝트
예상 시간55분
선수지식10강 점수·목숨과 이벤트

11강. 게임 상태 흐름: 시작·플레이·일시정지·게임 오버 전환 만들기

1. 이번 강의에서 해결할 문제​

게임 오버 뒤에도 택배가 생성되거나 일시정지 중 입력이 남을 수 있습니다. 허용된 상태 전환과 시스템 활성화를 명시적으로 관리합니다.

2. 학습 목표​

3. 핵심 개념​

게임 상태는 여러 bool 조합보다 enum 한 값으로 표현합니다. FlowController는 시스템의 켜짐 순서만 조정하고 점수 규칙이나 Panel 내용을 직접 계산하지 않습니다. Time.timeScale = 0만으로 모든 로직이 멈춘다고 가정하지 않고 입력과 생성도 명시적으로 막습니다.

먼저 알아야 할 상태 흐름 용어​

용어의미이 프로젝트의 예
상태(State)한 시점에 게임이 놓인 실행 단계Start, Playing, Paused, GameOver
전환(Transition)한 상태에서 다른 상태로 바뀌는 사건Start 버튼으로 Start → Playing
상태 머신가능한 상태와 허용 전환을 명시한 구조Paused에서는 재개 또는 게임 오버만 허용
조정자(Controller)여러 시스템을 직접 구현하지 않고 켜고 끄는 순서를 관리하는 객체GameFlowController
이벤트상태가 바뀌었다는 사실을 구독자에게 알리는 통지UI가 StateChanged를 받아 Panel 선택
Time Scalescaled time의 흐름 배율0이면 deltaTime 기반 이동·WaitForSeconds가 멈춤

Time.timeScale = 0은 게임 전체 프로세스를 정지하는 버튼이 아닙니다. Update 자체와 UI 입력은 계속 호출될 수 있고, unscaledDeltaTime을 쓰는 애니메이션도 움직일 수 있습니다. 따라서 Paused 상태에서는 택배 분류 입력을 별도로 막고, 어떤 UI와 애니메이션이 계속 동작할지 정책을 정합니다.

여러 bool을 쓰면 isPlaying == true와 isGameOver == true가 동시에 되는 불가능한 조합이 생깁니다. enum 한 값은 현재 상태를 하나로 제한하지만, 허용되지 않은 전환까지 자동으로 막지는 않습니다. StartRun, TogglePause, GameOver 같은 명령 메서드가 현재 상태를 검사해야 합니다.

4. 단계별 실습​

이 강의는 2~3강에서 만든 GameState enum을 사용합니다. 파일에는 Start, Playing, Paused, GameOver 네 값이 있어야 합니다. 코드에 상태 이름을 문자열로 반복하지 않습니다.

C# · 파일 경로: Assets/_Project/Scripts/Core/GameFlowController.cs

GameFlowController.cs
using System;
using DeliverySorter.Gameplay;
using UnityEngine;

namespace DeliverySorter.Core
{
public sealed class GameFlowController : MonoBehaviour
{
[SerializeField] private GameSession session;
[SerializeField] private PackageSpawner spawner;
[SerializeField] private PackageInputController input;
public GameState State { get; private set; } = GameState.Start;
public event Action<GameState> StateChanged;

private void OnEnable() => session.RunEnded += GameOver;
private void OnDisable() => session.RunEnded -= GameOver;

public void StartRun()
{
Time.timeScale = 1f;
session.ResetRun();
input.SetInputEnabled(true);
spawner.Begin();
SetState(GameState.Playing);
}

public void TogglePause()
{
if (State == GameState.Playing) SetPaused(true);
else if (State == GameState.Paused) SetPaused(false);
}

private void SetPaused(bool paused)
{
Time.timeScale = paused ? 0f : 1f;
input.SetInputEnabled(!paused);
SetState(paused ? GameState.Paused : GameState.Playing);
}

private void GameOver()
{
input.SetInputEnabled(false);
spawner.Stop();
Time.timeScale = 1f;
SetState(GameState.GameOver);
}

private void SetState(GameState next)
{
State = next;
StateChanged?.Invoke(State);
}
}
}

코드와 상태 변경 순서 이해하기​

  1. State는 외부에서 읽을 수 있지만 변경은 FlowController 내부에서만 합니다.
  2. OnEnable과 OnDisable은 RunEnded 이벤트 구독·해제를 짝으로 만들어 오브젝트 재활성화 뒤 중복 호출을 막습니다.
  3. StartRun은 Time Scale 복원 → 세션 초기화 → 입력 허용 → 생성 시작 → 상태 통지 순서입니다. 상태 이벤트를 받은 UI가 이미 준비된 값을 보게 하려는 순서입니다.
  4. TogglePause는 Playing과 Paused에서만 동작해 Start 화면에서 실수로 재개되는 것을 막습니다.
  5. Paused에서는 Spawner Coroutine을 제거하지 않습니다. scaled WaitForSeconds는 Time Scale 0에서 진행되지 않고, 재개하면 이어집니다. 입력은 별도로 차단합니다.
  6. GameOver는 입력과 생성을 멈추고 Time Scale을 1로 복원한 뒤 GameOver UI에 상태를 알립니다.
  7. StateChanged?.Invoke(State)의 ?.는 구독자가 한 명도 없을 때 호출을 건너뜁니다.

상태별 기대값을 코드 실행 전에 먼저 적습니다.

상태택배 입력새 택배 생성Time Scale주 화면
Start꺼짐중지1시작 안내
Playing켜짐실행1플레이 HUD
Paused꺼짐대기0일시정지
GameOver꺼짐중지1결과·재시작

Unity Editor에서 연결하고 가장 작은 전환 확인​

  1. Hierarchy에 GameFlow GameObject를 만들고 GameFlowController를 붙입니다.
  2. Session, Spawner, Input 슬롯에 현재 Scene의 실제 Component를 연결합니다. 같은 종류의 오브젝트가 여러 개라면 이름이 아니라 Inspector 선택 강조로 대상을 확인합니다.
  3. Start Button의 OnClick에는 GameFlowController.StartRun, Pause Button에는 TogglePause를 연결합니다.
  4. 상태를 화면에서 추적하려면 임시로 SetState 또는 StateChanged 구독부에 Debug.Log($"State: {State}", this);를 넣습니다.
  5. Play 직후 Start, Start 버튼 뒤 Playing, Pause 버튼 뒤 Paused, 다시 누른 뒤 Playing 로그가 순서대로 나오는지 확인합니다.

5. 코드가 동작하는 이유​

상태 전환 메서드가 관련 시스템을 같은 순서로 갱신하고 마지막에 이벤트를 냅니다. UI는 StateChanged를 받아 화면만 바꾸고, 세션은 GameOver 화면의 존재를 모릅니다.

6. 자주 하는 실수와 해결법​

7. 직접 실습​

실습 목표​

Start → Playing → Paused → Playing → GameOver 전환에서 입력·생성·시간·UI가 같은 상태를 가리키는지 검증합니다.

시작 전 상태​

위 상태표를 복사해 예상 열과 실제 열을 만듭니다. Inspector 참조 세 개와 두 Button 이벤트가 연결됐는지 확인한 뒤 Console을 비웁니다.

1단계: 따라 하기​

Start 버튼을 한 번 누릅니다. 점수·목숨이 초기화되고, 입력과 Spawner가 켜진 뒤 Playing 상태가 통지되어야 합니다. 이 순서를 Console 로그와 첫 택배 생성으로 확인합니다.

2단계: 값 바꿔 보기​

Pause와 Resume을 반복하며 Time.timeScale, 입력 반응, 새 택배 생성 여부를 비교합니다. Pause 중 UI Button은 계속 눌릴 수 있지만 택배 분류 입력은 처리되지 않아야 합니다.

3단계: 직접 적용​

Start 상태에서 Pause, GameOver 뒤 Pause, Paused 상태에서 StartRun처럼 허용하지 않을 전환 세 개를 정합니다. 각 public 명령 시작에서 현재 State를 검사하고, 거부할 때 Warning 로그를 한 번 남기세요. 정상 전환은 계속 동작해야 합니다.

4단계: 스스로 확인​

막혔을 때​

증상원인해결
Start 버튼에서 NullReferenceExceptionSession·Spawner·Input 참조 누락GameFlow Inspector의 세 슬롯을 확인합니다.
Pause 중에도 분류 입력이 반응Time Scale만 0으로 설정input.SetInputEnabled(false) 호출과 Input 내부 guard를 확인합니다.
Resume 뒤 생성이 두 배Begin을 다시 호출하거나 Coroutine guard 누락Resume은 기존 routine을 이어 쓰고 Spawner의 중복 시작 방지를 확인합니다.
GameOver 처리가 여러 번 실행이벤트 중복 구독OnEnable과 OnDisable이 대칭이고 같은 객체가 둘 이상 없는지 봅니다.

8. 이해 점검 질문 3개​

9. 핵심 요약​

다음 강의 연결​

다음 강의: 난이도 조절에서는 Playing 시간이 늘어날수록 생성 간격과 속도를 바꾸되, Paused·GameOver에서는 난이도 시간이 진행되지 않게 상태 흐름과 연결합니다.

MINI QUIZ

게임 상태 흐름: 시작·플레이·일시정지·게임 오버 전환 만들기 미니 퀴즈

선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.

0 / 2
  1. 문제 1다음 중 “게임 상태 흐름: 시작·플레이·일시정지·게임 오버 전환 만들기”의 핵심 요약을 실제 상황에 맞게 적용한 것은 무엇인가요?
  2. 문제 2“게임 상태 흐름: 시작·플레이·일시정지·게임 오버 전환 만들기”의 작업 기준으로 ‘Panel이 직접 상태를 변경’을 진단하거나 바로잡은 선택은 무엇인가요?
LESSON STATUS

학습을 마쳤나요?

직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.

11강. 게임 상태 흐름: 시작·플레이·일시정지·게임 오버 전환 만들기 미완료 상태