상태 전이
Coddy Unity 여정의 Unity 게임 아키텍처 섹션에 포함된 레슨. 64개 중 38번째.
상태가 언제 바뀔지는 누가 결정할까요? 소유자에게 맡기면 스위치 방식처럼 모든 규칙이 하나의 Update 안으로 다시 모입니다. 일반적인 해결책은 각 상태가 스스로 빠져나갈 방법을 알고 있는 것입니다. Tick에서 조건을 확인하고 다음 상태로 전환해 달라고 상태 머신에 요청합니다. 이 폭탄은 플레이어가 가까이 올 때까지 기다렸다가 퓨즈를 태우고, 플레이어가 달아나면 다시 대기 상태로 돌아갑니다.
public class Bomb : MonoBehaviour
{
public Transform player;
public StateMachine machine = new StateMachine();
public IState idle;
public IState fuse;
void Start()
{
idle = new IdleState(this);
fuse = new FuseState(this);
machine.ChangeState(idle);
}
void Update() { machine.Tick(); }
public bool PlayerNear()
{
return Vector3.Distance(transform.position, player.position) < 3f;
}
}
public class IdleState : IState
{
private Bomb bomb;
public IdleState(Bomb bomb) { this.bomb = bomb; }
public void Enter() { }
public void Exit() { }
public void Tick()
{
if (bomb.PlayerNear()) bomb.machine.ChangeState(bomb.fuse);
}
}
public class FuseState : IState
{
private Bomb bomb;
private float timer;
public FuseState(Bomb bomb) { this.bomb = bomb; }
public void Enter() { timer = 2f; }
public void Exit() { }
public void Tick()
{
if (!bomb.PlayerNear())
{
bomb.machine.ChangeState(bomb.idle);
return;
}
timer -= Time.deltaTime;
if (timer <= 0f)
{
Debug.Log("Boom!");
Object.Destroy(bomb.gameObject);
}
}
}각 상태는 생성자에서 폭탄을 전달받고, 이를 통해 상태 머신과 플레이어 확인 기능, 다른 상태에 접근합니다. 폭탄은 Start에서 각 상태를 한 번씩 생성하므로 상태가 new를 사용해 다른 상태를 만들지 않습니다. 생성자에서 서로를 만드는 두 상태가 있으면 스택이 넘칠 때까지 반복하게 됩니다. 폭탄의 Update는 상태 머신을 틱하는 일만 하고, 규칙은 해당 규칙이 속하는 상태에 있습니다. 2초 퓨즈는 오직 FuseState만 담당합니다.
ChangeState는 FuseState의 Exit와 IdleState의 Enter를 즉시 실행하지만, 호출 뒤에도 FuseState의 Tick 나머지 부분은 계속 실행됩니다. return이 이를 중단합니다. 그렇지 않으면 이미 끝난 상태에서 퓨즈가 한 프레임 더 타게 됩니다. Destroy는 Object.Destroy라고 씁니다. MonoBehaviour는 Unity의 Object 클래스에서 이를 상속받지만, 일반 클래스에서는 클래스 이름을 지정해야 합니다.
챌린지
중급FlytrapStates.cs의 각 Tick에서 파리지옥의 상태 전환을 작성하세요.
- Open:
trap.PlayerInReach()일 때trap.machine.ChangeState(trap.snap);을 호출하세요. - Snap:
timer에Time.deltaTime을 더하세요.0.5가 되면 플레이어가 여전히 범위 안에 있을 경우trap.chew로, 그렇지 않으면trap.open으로 전환하세요. - Chew:
timer를 3부터 카운트다운하세요. 0이 되면trap.open으로 전환하세요.
‘플레이’를 누르고, 함정 안으로 걸어 들어간 다음 피하세요. 그런 다음 ‘확인’을 누르세요.
직접 해보기
using UnityEngine;
public class OpenState : IState
{
private Flytrap trap;
public OpenState(Flytrap trap)
{
this.trap = trap;
}
public void Enter()
{
trap.Paint(new Color(0.3f, 0.8f, 0.2f));
}
public void Tick()
{
// 플레이어가 닿는 범위에 들어오면 덫을 닫기
}
public void Exit()
{
}
}
public class SnapState : IState
{
private Flytrap trap;
private float timer;
public SnapState(Flytrap trap)
{
this.trap = trap;
}
public void Enter()
{
timer = 0f;
trap.Paint(new Color(1f, 0.55f, 0.1f));
}
public void Tick()
{
// 0.5초 후: 플레이어가 여전히 닿는 범위에 있으면 씹고, 그렇지 않으면 다시 열기
}
public void Exit()
{
}
}
public class ChewState : IState
{
private Flytrap trap;
private float timer;
public ChewState(Flytrap trap)
{
this.trap = trap;
}
public void Enter()
{
timer = 3f;
trap.Paint(new Color(0.5f, 0.05f, 0.1f));
Debug.Log("Chomp!");
}
public void Tick()
{
// 3초 후: 다시 열기
}
public void Exit()
{
}
}
Unity 게임 아키텍처의 모든 레슨
직접 연습해 보세요: Unity 플레이그라운드