FSM(Finite State Machine)
FSM이란
- 유한 상태 기계라는 뜻으로 보통 게임 개발에서 중요한 개념 중 하나이다.
- 객체가 가질수 있는 상태(state), 상태가 변경될 때 필요한 조건(trigger), 상태 변화가 일어날 때 수행되는 행동(action)을 정의하는 모델이다.
- 유니티에선 FSM을 통해 게임케릭터와 시슴템의 상태를 관리하고 상태에 따른 동작을 쉽게 제어할 수 있다.
FSM의 장점
- 각 상태가 명확하게 정의 되어있어 시스템의 상태를 정확하게 파악할 수 있다.
- 상태를 추가하거나 변경할 때 기존 코드를 크게 수정하지 않아도 되어 유지보수가 용이하다.
- 상태 전환 로그를 통해 문제 해결 발생 시점을 빠르게 파악할 수 있다.
FSM의 단점
- 상태가 너무 많아 지게 된다면 상태전이 관리로 인해 로직이 복잡해 질 수 있다.
- 현재 상태는 하나만 존재하므로 병렬 상태 처리가 힘들다.
- 높은 수준의 AI구현 까지는 힘들다.
- 상태 전이가 복잡한 경우 디버깅이 힘들다.
- 너무 복잡하게 된다면 오히려 FSM의 단점이 부각된다!
코드 예시
public interface IState
{
public void Enter();
public void Exit();
public void HandleInput();
public void Update();
public void PhysicsUpdate();
}
public abstract class StateMachine
{
protected IState currentState;
public void ChangeState(IState state)
{
currentState?.Exit();
currentState = state;
currentState?.Enter();
}
public void HandleInput()
{
currentState?.HandleInput();
}
public void Update()
{
currentState?.Update();
}
public void PhysicsUpdate()
{
currentState?.PhysicsUpdate();
}
}
- IState 인터 페이스를 통해 구현부를 선언
- 추상 클래스로 StateMachine을 선언하고 상태 변화를 입력받고 실행해주는 함수를 선언(ChangeState)
- 입력과 Update를 통해 실시간 갱신
- PhysicsUpdate 물리연산 관련 로직
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerBaseState : IState
{
protected PlayerStateMachine stateMachine;
public PlayerBaseState(PlayerStateMachine stateMachine)
{
this.stateMachine = stateMachine;
groundData = stateMachine.Player.Data.GroundData;
}
public virtual void Enter()
{
}
public virtual void Exit()
{
}
public virtual void HandleInput()
{
}
public virtual void PhysicsUpdate()
{
}
public virtual void Update()
{
}
}
- PlayerBaseState 클래스를 선언해 IState를 상속 받고 각각 상태에 맞춰 로직을 추가해준다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerGroundState : PlayerBaseState
{
public PlayerGroundState(PlayerStateMachine playerStateMachine) : base(playerStateMachine)
{
}
public override void Enter()
{
base.Enter();
}
public override void Exit()
{
base.Exit();
}
public override void Update()
{
base.Update();
}
public override void PhysicsUpdate()
{
base.PhysicsUpdate();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerIdleState : PlayerGroundState
{
public PlayerIdleState(PlayerStateMachine playerStateMachine) : base(playerStateMachine)
{
}
public override void Enter()
{
stateMachine.MovementSpeedModifier = 0f;
base.Enter();
StartAnimation(stateMachine.Player.AnimationData.IdleParameterHash);
}
public override void Exit()
{
base.Exit();
StopAnimation(stateMachine.Player.AnimationData.IdleParameterHash);
}
public override void Update()
{
base.Update();
}
}
- GroundState를 상속받아 그 중 가만히 있는 상태를 선언해주면된다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : Monobehaviour
{
private PlayerStateMachine stateMachine;
private void Awake()
{
stateMachine = new PlayerStateMachine(this);
}
private void Start()
{
Cursor.lockState = CursorLockMode.Locked;
stateMachine.ChangeState(stateMachine.IdleState);
}
private void Update()
{
stateMachine.HandleInput();
stateMachine.Update();
}
private void FixedUpdate()
{
stateMachine.PhysicsUpdate();
}
- 이런식으로 스테이트 머신을 선언하고 각각의 스테이트 머신을 선언해주면 된다.
- 상태를 바꾸고싶다면 ChangeState로직을 활용하여 상태를 바꿔주면 된다.
오늘의 회고
- 이번 프로젝트가 포격게임인데 AI로직을 사용하려 하는데 생각보다 실사용이 많이 어려워서 좀 더 공부하면서 코드를 가다듬어야겠다.
- 그래도 이런식으로 구조에 대해 고민을 많이 해보니까 확실히 상속이라던가 추상에 대해 실력이 늘고 있는것같아서 기분이 좋다. 이번 최종 프로젝트 역시 엄청 재미있게 하고있다. 난이도가 어렵긴 한데 공부하는 느낌이 제대로 들어서 팀원분들 모두 적극적으로 재미있게 하고있다.