Unity / C#
Intermediate
Unity 2021.3+ / Unity 6
인터페이스 기반 유한 상태 머신(FSM)과 플레이어 액션 상태 전이 제어
복잡한 Switch 분기를 제거하고 Enter/Execute/Exit 라이프사이클을 보장하는 C# 인터페이스 기반 유한 상태 머신(Finite State Machine) 아키텍처.
#Unity #C# #FSM #State Pattern #Gameplay Architecture
핵심 개념플레이어 캐릭터에 달리기, 점프, 벽타기, 피격 등의 액션이 늘어날수록
enum과 거대한 switch 문은 버그의 온상이 됩니다. 각 상태를 독립 클래스로 격리하고, 상태 진입(Enter), 갱신(Execute), 탈출(Exit)을 강제하는 FSM을 구축하면 코드 확장성과 유지보수성이 획기적으로 개선됩니다.1. FSM 핵심 인터페이스 및 컨트롤러
public interface IState
{
void Enter();
void Execute();
void FixedExecute();
void Exit();
}
public class StateMachine
{
public IState CurrentState { get; private set; }
public void ChangeState(IState newState)
{
if (CurrentState == newState || newState == null) return;
CurrentState?.Exit();
CurrentState = newState;
CurrentState.Enter();
}
public void Update() => CurrentState?.Execute();
public void FixedUpdate() => CurrentState?.FixedExecute();
}
2. 플레이어 상태 구현 예제 (Idle & Run)
using UnityEngine;
public class PlayerIdleState : IState
{
private readonly PlayerController _player;
public PlayerIdleState(PlayerController player) => _player = player;
public void Enter()
{
_player.Anim.Play("Idle");
_player.Rb.linearVelocity = new Vector2(0f, _player.Rb.linearVelocity.y);
}
public void Execute()
{
if (Mathf.Abs(_player.MoveInput) > 0.01f)
{
_player.SM.ChangeState(_player.RunState);
}
}
public void FixedExecute() { }
public void Exit() { }
}
public class PlayerRunState : IState
{
private readonly PlayerController _player;
public PlayerRunState(PlayerController player) => _player = player;
public void Enter() => _player.Anim.Play("Run");
public void Execute()
{
if (Mathf.Abs(_player.MoveInput) <= 0.01f)
{
_player.SM.ChangeState(_player.IdleState);
}
}
public void FixedExecute()
{
_player.Rb.linearVelocity = new Vector2(_player.MoveInput * _player.Speed, _player.Rb.linearVelocity.y);
}
public void Exit() { }
}
3. 성능 및 메모리 팁 (GC 할당 방지)
- 상태 인스턴스 재사용:
ChangeState(new PlayerRunState())처럼 매 전이마다new를 호출하면 GC 스파이크가 발생합니다.Awake에서 각 상태 인스턴스를 미리 캐싱해 두고 참조만 교체해야 합니다. - 물리 연산 분리: Update에서는 입력 및 애니메이션 플래그만 검사하고, Rigidbody 속도 제어는 반드시
FixedExecute()에 배치하여 물리 프레임 틱과 완벽히 동기화합니다.