유한 상태 머신 (Finite State Machine)
유한 상태 머신은 상태 머신이 유한한 개수의 상태 중 단 하나의 상태만 가지는 것을 뜻합니다.
수학적 모델이라고 하는데 게임에서 정말 많이 쓰이는 기술 중입니다.
예시
FSM의 예시를 배그로 한번 들어보겠습니다.
State Machine이 가지는 현재 상태를 ( )로 표시해 보겠습니다.
맨 처음 인게임에 들어가서 아무것도 안 하면 캐릭터는 멈춰있습니다. ( 정지 상태 )
WASD를 누르면 걷겠죠 ( 정지 상태 -> 걷기 상태 )
걷는 도중에 Shift를 꾹 누르면 캐릭터가 뜁니다. ( 걷기 상태 -> 뛰기 상태 )
여기서 캐릭터가 Z키를 눌러서 엎드리기 애니메이션을 하고 있다고 생각해 보겠습니다.
엎드린 상태에서 WASD를 누르면 기어가기는 되는데 걷기는 안 됩니다.
엎드린 상태에서 두 발로 걷는다? 말이 안 됩니다.
유한 상태 머신이 한 가지 상태만 가지는 이유가 이겁니다.
엎드린 상태에서 걷기로 갑자기 변하는 것을 막으려고.
쓰다 보니까 현실에서 한 번에 한 가지 행동만 할 수 있다는 점이 이 상태 머신과 많이 닳았네요.
구현
public interface IState
{
public void Enter();
public void Execute();
public void Exit();
}
우선 IState 인터페이스를 구현해 줍니다.
걷기 상태 -> 뛰기 상태로 변할 때
걷기 상태의 Exit를 실행하고
뛰기 상태는 Enter를 실행한 다음
Execute를 계속 실행합니다.
public class RunState : IState
{
public void Enter()
{
Debug.Log("Enter Run State");
}
public void Execute()
{
Debug.Log("Execute Run State");
}
public void Exit()
{
Debug.Log("Exit Run State");
}
}
그다음에 이런 식으로 상태가 되기를 원하는 것에 IState를 상속받습니다.
그리고 Enter, Execute, Exit에 원하는 코드를 적으면 됩니다.
public class StateMachine : MonoBehavior
{
private IState currentState;
public void ChangeState(IState toState)
{
if (toState == null)
{
Debug.LogWarning("바꿀려는 상태가 null입니다.");
return;
}
// 현재 상태와 바꿀려는 상태가 같으면 return
if (currentState == toState)
{
Debug.LogError($"이미 {currentState?.GetType().Name} 상태입니다.");
return;
}
// 우선 현재 상태를 Exit하고
currentState?.Exit();
// 바꿀려는 상태로 변경
currentState = toState;
// 바꿀려는 상태의 Enter를 실행
currentState?.Enter();
}
void Update()
{
// 현재 상태의 Execute를 계속 실행
currentState?.Execute();
}
public override string ToString()
{
return $"현재 상태: {currentState?.GetType().Name}";
}
}
핵심인 StateMachine 스크립트입니다.
정말 최소한의 기능들만 구현했습니다.
이제 StateMachine을 GameObject 하나 만들어서 컴포넌트로 넣습니다.
그리고 사용하고자 하는 스크립트에 StateMachine을 가져옵니다.
public class Character : MonoBehavior
{
[SerializeField] private StateMachine charMachine;
private IState idleState = new IdleState();
private IState runState = new RunState();
private void Start()
{
charMachine.ChangeState(idleState);
}
private void Update()
{
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");
bool isMoving = !Mathf.Approximately(horizontalInput, 0) || !Mathf.Approximately(verticalInput, 0);
// Approximately는 첫번째 인수가 두번째 인수하고 근사한지 확인
if (isMoving)
{
charMachine.ChangeState(runState);
}
}
}
이런 식으로 코드를 짤 수 있습니다.
여담
유니티의 Animator에서도 State Machine을 사용합니다.
예전에 프로젝트를 했을 때 팀원 분이 FSM을 이용해서 Animator를 되게 간략하게 만드신 게 있었는데
모든 Patameters를 Trigger로 놓고 스크립트로만 관리하는 식으로 짠 FSM이 있었습니다.
물론 코드가 복잡해진다는 단점이 있지만 애니메이션 관리가 쉬워서 좋았던 것 같습니다.
이 방법도 나중에 연구에 작성해 보겠습니다.
'유니티 > Tutorial' 카테고리의 다른 글
Unity 디자인 패턴 Observer (0) | 2025.04.25 |
---|---|
Unity 디자인 패턴 Singleton (0) | 2025.04.07 |
Unity Compute Shader 01 (0) | 2025.03.22 |
Unity New Input System (0) | 2025.02.19 |
UniTask란? (0) | 2025.01.09 |