Menu
CoddyTech

클래스로 상태 표현하기

Coddy Unity 여정의 Unity 게임 아키텍처 섹션에 포함된 레슨. 50개 중 28번째.

섹션 3의 열거형 switch는 모든 상태의 작업을 하나의 Update에 넣습니다. 상태마다 자체 데이터가 생기면 이러한 방식은 더 이상 확장하기 어렵습니다. 한 상태만 사용하는 타이머가 다른 모든 상태의 필드 옆에 MonoBehaviour의 필드로 들어가고, 각 case는 자신이 사용하는 값을 초기화하는 것을 기억해야 합니다. C#의 상태 패턴은 각 상태에 고유한 클래스를 제공하고, 모든 클래스가 하나의 인터페이스를 구현하도록 합니다. 이 도로용 비컨은 깜박이거나 계속 켜져 있으며, Space 키를 누르면 두 상태 사이를 전환합니다:

public interface IState
{
    void Enter();
    void Tick();
    void Exit();
}

public class BlinkState : IState
{
    private Renderer lamp;
    private float timer;

    public BlinkState(Renderer lamp)
    {
        this.lamp = lamp;
    }

    public void Enter() { timer = 0f; }

    public void Tick()
    {
        timer += Time.deltaTime;
        if (timer >= 0.5f)
        {
            timer = 0f;
            lamp.enabled = !lamp.enabled;
        }
    }

    public void Exit() { lamp.enabled = true; }
}

public class SteadyState : IState
{
    public void Enter() { }
    public void Tick() { }
    public void Exit() { }
}

public class Beacon : MonoBehaviour
{
    public Renderer lamp;
    private IState blink;
    private IState steady;
    private IState current;

    void Start()
    {
        blink = new BlinkState(lamp);
        steady = new SteadyState();
        SetState(steady);
    }

    void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame) SetState(current == steady ? blink : steady);
        current.Tick();
    }

    void SetState(IState next)
    {
        if (current != null) current.Exit();
        current = next;
        current.Enter();
    }
}

상태는 MonoBehaviour가 아니라 일반 C# 클래스입니다. new로 만들고 생성자를 통해 필요한 것을 전달합니다. 여기서는 램프의 Renderer를 전달합니다. 상태는 컴포넌트가 아니므로 GetComponent를 호출하거나 Inspector에 필드를 표시할 수 없습니다. Unity도 해당 클래스의 메서드를 호출하지 않습니다. MonoBehaviour는 Beacon이므로 그 Update가 매 프레임 current.Tick()을 호출합니다.

timer는 BlinkState에만 속하며, Enter는 비컨이 깜박이기 시작할 때마다 값을 0으로 설정합니다. Exit는 상태가 끝날 때 정리 작업을 합니다. 램프가 꺼져 있을 때 Space 키를 누를 수 있는데, Exit가 램프를 다시 켜므로 SteadyState는 램프가 꺼진 상태로 시작하지 않습니다. SetState는 이전 상태의 Exit를 실행한 다음 새 상태의 Enter를 실행합니다. 느린 점멸 같은 세 번째 상태를 추가하려면 클래스 하나만 더 만들면 되며, Beacon의 Update는 바뀌지 않습니다.

challenge icon

챌린지

중급

감시 포탑이 마당을 지킵니다. 화살표 키로 플레이어를 움직여 보세요(준비됨). 포탑에 다음 두 상태를 클래스로 만드세요.

  • IState.cs에서 public interface IState를 선언하고, void Enter(), void Tick(), void Exit()를 포함하세요.
  • TurretStates.cs에서 인터페이스를 구현하는 일반 클래스를 두 개 작성하세요. ScanState는 생성자에서 포탑의 머리를 받습니다. 머리를 좌우로 움직입니다. 머리의 로컬 y 각도는 초당 90도씩 -45와 45 사이에서 움직이며, 0에서 시작하고 스캔이 시작될 때마다 위쪽으로 회전합니다. Enter에서는 머리의 색도 Color.green으로 바꿉니다.
  • AlertState는 머리와 플레이어를 받습니다. Enter에서는 머리의 색을 Color.red로 바꾸고 Intruder!를 로그에 기록합니다. Tick에서는 기울이지 않고 y축만 기준으로 머리가 플레이어를 향하게 합니다. Exit에서는 All clear를 로그에 기록합니다.
  • Turret.cs에서 Start에 두 상태를 모두 만들고 스캔을 시작하세요. 매 프레임 플레이어가 포탑에서 range(4)보다 가까우면 경계 상태로 전환하고, 그렇지 않으면 다시 스캔 상태로 전환한 다음 현재 상태의 Tick을 실행하세요. 상태가 바뀌면 이전 상태의 Exit을 실행한 다음 새 상태의 Enter를 실행합니다.

Play를 누르고 포탑에 다가갔다가 다시 멀어져 보세요. 그런 다음 Check를 누르세요.

직접 해보기

using UnityEngine;

// ScanState와 AlertState
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

Unity 게임 아키텍처의 모든 레슨