Unity

Unity 유한상태머신 (FSM)에 대하여

anvil925 2025. 4. 7. 23:17
728x90
반응형

Unity 유한상태머신 (FSM)에 대하여

게임 개발을 하다 보면, 특히 AI나 캐릭터의 행동 패턴을 구현할 때 "상태"를 기반으로 한 시스템이 필요해지는 경우가 많습니다. 이럴 때 자주 활용되는 개념이 바로 유한상태머신(Finite State Machine, FSM) 입니다. 이번 글에서는 Unity에서 FSM을 어떻게 구현하고 활용할 수 있는지에 대해 정리해보겠습니다.


FSM(Finite State Machine)이란?

FSM은 하나의 객체가 여러 상태 중 하나의 상태에만 속하고, 조건에 따라 다른 상태로 전이(transition)되는 구조를 가진 논리 모델입니다. 상태마다 서로 다른 동작을 정의할 수 있으며, 이를 통해 복잡한 행동 로직을 깔끔하게 구성할 수 있습니다.

예를 들어, 적 캐릭터 AI의 FSM은 다음과 같이 구성될 수 있습니다:

  • Idle: 대기 상태
  • Patrol: 경로를 따라 이동
  • Chase: 플레이어를 발견하고 추격
  • Attack: 사정거리 내에 들어온 플레이어를 공격
  • Dead: 체력이 0이 되어 사망

각 상태는 전이 조건에 따라 서로 전환될 수 있습니다.


상태 다이어그램 예시

아래는 간단한 FSM 상태 다이어그램 예시입니다:

[Idle] ---> (플레이어 감지) ---> [Chase] ---> (사거리 안) ---> [Attack]
   ^              |                                   |
   |              |(플레이어 사라짐)                |(플레이어 죽음)
   |              v                                   v
 [Patrol] <--- (시간 경과) <--- [Return] <--- [Dead]

이러한 다이어그램은 상태 간 흐름을 직관적으로 파악하는 데 큰 도움이 됩니다.


Unity에서 FSM을 구현하는 방법

Unity에서는 FSM을 구현하는 방법이 다양합니다. 대표적으로는 다음과 같은 방식이 있습니다.

1. Enum + Switch문 기반 FSM

가장 간단한 방식으로, 현재 상태를 enum으로 관리하고 Update()에서 switch문을 활용해 상태별 로직을 처리합니다.

public enum EnemyState { Idle, Patrol, Chase, Attack, Dead }

public class EnemyFSM : MonoBehaviour
{
    public EnemyState currentState;

    void Update()
    {
        switch (currentState)
        {
            case EnemyState.Idle:
                HandleIdle();
                break;
            case EnemyState.Patrol:
                HandlePatrol();
                break;
            // 기타 상태들...
        }
    }
}

2. 상태를 클래스화한 방식 (OOP FSM)

상태를 각각 클래스(또는 ScriptableObject)로 분리해 SOLID 원칙에 가깝게 구현하는 방식입니다. 확장성과 유지보수에 매우 유리합니다.

public abstract class State
{
    protected EnemyFSM fsm;
    public State(EnemyFSM fsm) => this.fsm = fsm;

    public abstract void Enter();
    public abstract void Execute();
    public abstract void Exit();
}

public class PatrolState : State
{
    public PatrolState(EnemyFSM fsm) : base(fsm) {}

    public override void Enter() { Debug.Log("Patrol 시작"); }
    public override void Execute() { /* 경로 이동 로직 */ }
    public override void Exit() { Debug.Log("Patrol 종료"); }
}

public class EnemyFSM : MonoBehaviour
{
    State currentState;

    void Update()
    {
        currentState?.Execute();
    }

    public void ChangeState(State newState)
    {
        currentState?.Exit();
        currentState = newState;
        currentState.Enter();
    }
}

이 방식은 상태 전환을 유연하게 처리할 수 있어 복잡한 AI 로직이나 보스 몬스터의 동작 구현에 적합합니다.


FSM을 사용할 때의 장점

  • 코드가 깔끔해짐: 상태별로 기능이 나눠져 가독성과 유지보수가 쉬움
  • 확장성 좋음: 새로운 상태를 추가해도 기존 로직과 충돌이 없음
  • 디버깅 용이: 현재 상태만 확인하면 문제를 빠르게 파악 가능

Unity Animator와의 차이점

Unity의 Animator도 일종의 FSM 구조를 사용합니다. 애니메이션의 상태 전이 및 조건 설정을 시각적으로 설정할 수 있지만, 논리적인 FSM은 직접 구현해야 더욱 복잡한 게임 로직을 처리할 수 있습니다.

  • Animator FSM: 애니메이션 전이용 시각화 도구
  • 코드 FSM: 캐릭터의 행동 전반을 제어하는 논리 시스템

마무리: 언제 FSM을 쓰면 좋을까?

FSM은 특히 적 AI, NPC 행동, 보스 패턴, UI 상태 제어 등에 매우 유용합니다. 단순한 로직이라면 enum + switch로도 충분하고, 복잡한 경우에는 클래스 기반 FSM으로 구조화하는 것을 추천합니다.

FSM 구조를 잘 잡아두면 향후 확장할 때 큰 도움이 되니, 처음부터 설계를 신중히 하면 좋습니다.

필요하다면 FSM 구조 템플릿이나 실제 게임 예시 코드도 다음 글에서 다뤄볼 예정입니다! 

728x90
반응형