[Unity] State Pattern

조경민·2026년 2월 23일

State Pattern

// 문제 코드
public enum StateList
{
   Idle, Move, Jump, Attack
}

public class PlayerState : MonoBehaviour
{
   private int _direction;
   [SerializeField] private StateList CurrentState; 

   private void Update() 
   {
     // 키 입력 에 따른 각 상태 변화
     
   }

   private void Idle()
   {
     // 기능 로직
     // 애니메이션 변화 로직
     // 예외처리 등
   }

   private void Move()
   {
     // 기능 로직
     // 애니메이션 변화 로직
     // 예외처리 등
   }

   private void Jump()
   {
     // 기능 로직
     // 애니메이션 변화 로직
     // 예외처리 등
   }

   private void Attack()
   {
     // 기능 로직
     // 애니메이션 변화 로직
     // 예외처리 등
   }
}
  • 상태(State)가 늘어나면, 조건 분기 수(if) 기하급수적으로 늘어남.

// 문제 코드
void Update()
{
    HandleInput();
    ApplyGravity();
    CheckGround();

    if (_state == PlayerState.Idle)
    {
        ...
    }
    else if (_state == PlayerState.Move)
    {
        ...
    }
    else if (_state == PlayerState.Jump)
    {
        ...
        if (CanDoubleJump()) ...
        if (InputDash()) ...
        if (InputAttack()) ...
        if (IsHit()) ...
    }
    else if (_state == PlayerState.AirAttack)
    {
        ...
        if (ComboWindowOpen()) ...
        if (IsHit()) ...
        if (IsGrounded()) ...
    }
    else if (_state == PlayerState.Hit)
    {
        ...
        if (RecoverTimeOver()) ...
    }
    else if (_state == PlayerState.Die)
    {
        ...
    }
    // 계속 증가
}
  • 상태(State)가 서로를 안다
  • 책임이 Player한테 몰린다
  • 읽기도 수정도 어려워서 수정 시 리스크 폭발

상태(State): 어떤 객체가 특정 상황에서 어떻게 행동해야 하는지를 결정하는 행동 규칙

State Pattern 구조

요소책임하면 안 되는 것
Context상태 실행상태 로직 직접 구현
StateMachine상태 교체입력 처리
ConcreteState자기 상태 행동 전달다른 상태 내부 접근
  • 동작 흐름

    User Input -> Player -> StateMachine -> State.Upate()
    
    입력 → 코드 FSM → 물리 → Animator
    • 게임은 코드 FSM 위에서 동작, Animator는 그 위에 얹힌 표현 계층
  • 전환 흐름

    CurrentState?.Exit()
    ↓
    NewState.Enter()

예제 코드

  • State (상태 인터페이스)
using UnityEngine;

// IdleState, MoveState, JumpState, AttackState ....
// 행동 의도만 전달 -> 객체
public interface IState
{
    // 가질 주기 3가지
    void Enter();
    void Update();
    void Exit();
}

Enter() -> 상태에 들어올 때 한 번만 실행

  • ex) 애니메이션 변경, 속도 초기화, 점프 힘 적용, 입력 잠금, 타이머 시작

Update() -> 상태에 머무르는 동안 매 프레임 실행

  • ex) 그 상태의 지속 로직 수행(점수 획득, 시간 누적), 다른 상태로 갈 조건 검사

Exit() -> 상태를 떠날 때 정리 작업 실행

  • ex) 입력 잠금 해제, 코루틴 정지, 이펙트 제거, 타이머 리셋

  • StateMachine (상태 교체)
using UnityEngine;

// 얘가 해야할 책임?
// State를 교체/관리
public class StateMachine
{
    IState _currentState;

    // 유니티 씬상에 존재하는 "어떤 객체"가 상태(state)를 전환/실행
    public void ChangeState(IState state)
    {
        _currentState?.Exit();
        _currentState = state;
        _currentState.Enter();
    }

    // 유니티 이벤트 함수인 Update() 랑 다름..
    public void Update()
    {
        _currentState?.Update();
    }
}

  • Context (Player)
using UnityEngine;

// 입력 처리 / 물리 판정 / 애니메이션 결정
public class PlayerController : MonoBehaviour
{
    Rigidbody2D _rb;
    Animator _animator;

    StateMachine _stateMachine;

    // Player가 사용할 Stete들
    public IdleState Idle {get; private set; }
    public MoveState Move { get; private set; }
    public JumpState Jump { get; private set; }

	void Awake()
    {
    	_stateMachine = new StateMachine();
        
        Idle = new IdleState(this);
        Move = new MoveState(this);
        Jump = new JumpState(this);
    }
    
    void Start()
    {
        _stateMachine.ChangeState(Idle);
    }

    void Update()
    {
        _stateMachine.Update();
    }

    public void ChangeState(IState state)
    {
        _stateMachine.ChangeState(state);
    }
}

  • ConcreteState (IdleState)
using UnityEngine;

public class IdleState : IState
{
    PlayerController _player;

    public IdleState(PlayerController player)
    {
        _player = player;
    }

    public void Enter()
    {
    }

    public void Update()
    {
        // Move, Jump
        if(_player.MoveInput != 0) // 움직이면
        {
            _player.ChangeState(_player.Move); // 상태 변환
            return;
        }

        if(_player.IsJumpInput()) // 점프하면
        {
            _player.ChangeState(_player.Jump); // 상태 변환
        }
    }

    public void Exit()
    {
    }
}
profile
안녕하세요

0개의 댓글