ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Unity | 유한상태머신(FSM, Finite State Machine)과 상태패턴(State Pattern)
    다시한번 개발자도전! 2026. 2. 23. 11:25

    1. 상태 머신(FSM)이란 무엇인가?

    • 유한 상태 머신(Finite State Machine, FSM)은 객체가 가질 수 있는 상태(State)를 한정하고, 특정 조건에 따라 다른 상태로 전이(Transition)하는 설계 방식.
    • 플레이어의 AI, 캐릭터 컨트롤러, 복잡한 UI 시스템 등을 관리할 때 필수적으로 쓰임. 핵심은 "객체는 한 번에 오직 하나의 상태만 가질 수 있다"는 점. 예를 들어, 캐릭터가 동시에 '점프'하면서 '사망' 상태일 수는 없는 것과 같음.

    2. 왜 상태 머신을 써야 하는가? 

    단순하게 코드를 짜면 Update() 함수 내부에 수많은 if-else나 switch문이 들어가게 됨. 상태가 3개일 땐 괜찮지만, 10개가 넘어가면 코드가 스파게티처럼 꼬여서 수정하기가 매우 힘들어짐.

    • 가독성 향상: 각 상태의 로직이 별도의 클래스나 메서드로 분리됨.
    • 유지보수 용이: 새로운 상태(예: 비행, 수영)를 추가할 때 기존 상태의 코드를 건드릴 필요가 없음 (개방-폐쇄 원칙 준수).
    • 디버깅 효율: 현재 객체가 어떤 상태인지 명확히 추적할 수 있어 에러 잡기가 쉬움.

    3. 상태 패턴(State Pattern)

    상태 패턴은 상태 머신을 구현할 때 "상태를 별도의 클래스로 캡슐화"하는 방식.

    • SOLID 원칙의 정수: 각 상태가 클래스로 분리되므로 단일 책임 원칙(SRP)을 지키고, 새로운 상태 추가가 쉬워 개방-폐쇄 원칙(OCP)을 만족함.
    • 구조: IState 인터페이스를 만들고, IdleState, MoveState 클래스가 이를 상속받아 자기 로직만 실행함.

    4. 차이점 비교 

    구분 상태 머신 (State Machine) 상태 패턴 (State Pattern)
    성격 추상적인 모델 / 동작 원리 구체적인 구현 코드 구조 / 디자인 패턴
    범위 모든 상태 제어 로직을 통칭함 OOP에서 상태를 객체화하는 특정 기법
    유니티 예시 Animator(Mecanim), Enum 기반 제어 C# 클래스로 분리된 State 클래스들
    핵심 목표 상태 전환 로직 설계 코드의 유연성과 유지보수성 향상

     

     


    👩‍💻상태패턴 구현 

     

    1.상태 인터페이스 정의 

    public interface IState
    {
        void Enter();         // 상태에 들어올 때 
        void Update();        // 매 프레임 실행될 로직
        void HandleInput();   // 입력 감지 및 상태 전환 조건 체크
        void Exit();          // 상태를 나갈 때 
    }

     


    2.상태 머신 컨트롤러

    public class StateMachine
    {
        // 상태들을 저장할 딕셔너리
        private Dictionary<System.Type, IState> states = new Dictionary<System.Type, IState>();
        public IState CurrentState { get; private set; }
    
        // 상태 등록 (한 번만 생성해서 넣어둠)
        public void AddState(IState state)
        {
            states.Add(state.GetType(), state);
        }
    
        public void Initialize<T>() where T : IState
        {
            var type = typeof(T);
            if (states.ContainsKey(type))
            {
                CurrentState = states[type];
                CurrentState.Enter();
            }
        }
    
        public void ChangeState<T>() where T : IState
        {
            CurrentState.Exit();
            CurrentState = states[typeof(T)];
            CurrentState.Enter();
        }
    }

    3.구체적인 상태 구현 

    public class IdleState : IState //대기상태
    {
        private Player player;
    
        public IdleState(Player player) => this.player = player;
    
        public void Enter() =>Debug.Log("대기상태");
        
        public void HandleInput()
        {
            // 이동 입력이 들어오면 MoveState로 전환
            if (player.MoveInput != 0)
                player.StateMachine.ChangeState<MoveState>();
        }
    
        public void Update() { / 가만히 있을 때의 로직 / }
        public void Exit() { }
    }
    
    public class MoveState : IState //이동상태
    {
        private Player player;
    
        public MoveState(Player player) => this.player = player;
    
        public void Enter() => Debug.Log("이동상태");
    
        public void HandleInput()
        {
            // 입력이 끊기면 다시 IdleState로 전환
            if (player.MoveInput == 0)
                player.StateMachine.ChangeState<IdleState>();
        }
    
        public void Update() => player.Move(); // 실제 이동 처리
        public void Exit() { }
    }

     


     

    4.실제 플레이어 사용

    public class Player : MonoBehaviour
    {
        public StateMachine StateMachine { get; private set; }
        public float MoveInput { get; private set; }
    
        void Awake()
        {
            StateMachine = new StateMachine();
    
            // 상태 머신에 상태들을 등록만 해줌 
            StateMachine.AddState(new IdleState(this));
            StateMachine.AddState(new MoveState(this));
    
            StateMachine.Initialize<IdleState>();
        }
    
        void Update()
        {
            MoveInput = Input.GetAxisRaw("Horizontal");
    
            StateMachine.CurrentState.HandleInput();
            StateMachine.CurrentState.Update();
        }
    
        public void Move()
        {
            transform.Translate(Vector3.right * MoveInput * Time.deltaTime * 5f);
        }
    }

     


    실제 작동 모습

     

Designed by Tistory.