클래스로 상태 표현하기
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는 바뀌지 않습니다.
챌린지
중급감시 포탑이 마당을 지킵니다. 화살표 키로 플레이어를 움직여 보세요(준비됨). 포탑에 다음 두 상태를 클래스로 만드세요.
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
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.