안녕하세요,
오늘은 학교 유니티 엔진시간에 배웠던 FSM에 대해서 다시 공부하기 위해 블로그를 작성하게 되었습니다.
우선 FSM이 무엇일까요?
FSM은 유한상태기계로, 객체의 행동을 상태 단위로 관리하는 것을 말합니다.
쉽게 말해서, 케릭터가 한번에 한가지 행동만을 한다고 가정하고, 현재 상태에서 다른 행동을 실행하게 하는 구조입니다.
학교에서 진행한 FSM 수업에서는 상태가 대기, 공격, 이동으로 총 3가지였습니다.
객체가 처음엔 대기 상태에 진입하여 대기하다가, 이동 전용 오버랩 서클을 이용하여 플레이어와의 거리가 특정 거리 안으로 좁혀지면 이동 상태로 전환합니다. 그 후에 공격 전용 오버랩 서클을 이용하여 다시 한번 특정 공격 범위 안으로 좁혀질 경우 공격 상태로 바꾸고, 만약 이동상태에서 이동오버랩 서클로 거리를 측정했을때 플레이어가 범위를 벗어났다면 다시 대기상태로 전환합니다.
이제, 실제 FSM을 구축해보겠습니다.
먼저 FSM 행동 상태를 나타낼 추상클래스가 필요합니다.
스크립트 이름은 EnemyState로 생성해주었습니다. 추후에 IdleState(대기상태), ChaseState(이동상태), AttackState(공격상태)를 만들었을 때 이 스크립트들이 EnemyState를 상속받도록 할 것 입니다.
다음은 EnemtState의 스크립트입니다.
using UnityEngine;
public abstract class EnemyState
{
public virtual void Enter()
{
//상태에 들어갈때 하는 행동
}
public virtual void UpdateState()
{
//매 프레임마다 호출
//상태가 활성 상태일 때 실행되는 로직 담당
}
public virtual void Exit()
{
//상태 빠져나갈 때 해야할 행동
}
}
메서드를 필요할때만 유연하게 구현할 수 있도록 virtual로 선언했습니다.
이제, 각 상태 스크립트를 작성하겠습니다. Idle, Chase, Attack 모두 처음엔 비슷한 구조라 Idle로 예를 들겠습니다.
다음은 EnemyState를 상속받는 IdleState 스크립트입니다.
using UnityEngine;
public class EnemyIdleState : EnemyState
{
public override void Enter()
{
base.Enter();
}
public override void UpdateState()
{
// 여기서 플레이어와의 거리를 측정하고 추적거리가 되면 추적상태로 변경함
}
public override void Exit()
}
Chase, Attack State도 비슷하게 작성해주었습니다.
이제, 상태 전환 로직을 중앙에서 관리해주는 EnemyStateMachine을 만들 차례입니다.
using System.Collections.Generic;
using UnityEngine;
public enum EnemyStateType
{
Idle,
Chase,
Attack
}
//상태 전환 로직을 중앙에서 관리
public class EnemyStateMachine
{
private Dictionary<EnemyStateType, EnemyState> _stateDictionary = new();
public EnemyState currentState; //현재상태
private EnemyBrain enemy;
public EnemyStateMachine(EnemyBrain brain)
{
enemy = brain;
}
public void AddState(EnemyStateType stateType, EnemyState state)
{
_stateDictionary.Add(stateType, state);
}
public void Initialized(EnemyStateType startType)
{
currentState = _stateDictionary[startType];
currentState.Enter();
}
//다른 상태로 전환
public void ChangeState(EnemyStateType newState)
{
if (currentState != null)
{
currentState.Exit();
}
currentState = _stateDictionary[newState];
if (currentState != null)
{
currentState.Enter();
}
}
}
AddState로 _stateDictionary 딕셔너리에 상태 enum과 상태 스크립트(ex IdleState)를 짝지어 넣어줄겁니다.
Initialized로 상태를 초기화 할 수 있고, ChangeState로 상태를 전환할 수 있습니다.
이제 EnemyBrain, 실제로 동작할 수 있게 도와주는 스크립트를 만들겠습니다.
using UnityEngine;
public class EnemyBrain : MonoBehaviour
{
private EnemyStateMachine enemyStateMachine;
[SerializeField] private LayerMask _playerLayer;
private float chaseRange = 5f;
private float attackRange = 1f;
private void Awake()
{
enemyStateMachine = new EnemyStateMachine(this);
enemyStateMachine.AddState(EnemyStateType.Idle, new EnemyIdleState());
enemyStateMachine.AddState(EnemyStateType.Chase, new EnemyChaseState());
enemyStateMachine.AddState(EnemyStateType.Attack, new EnemyAttackState());
}
private void Start()
{
enemyStateMachine.Initialized(EnemyStateType.Idle);
}
// 오버랩으로 범위감지
private void Update()
{
if (Physics2D.OverlapCircle(transform.position, attackRange, _playerLayer))
{
UpdateState(EnemyStateType.Attack)
}
else if (Physics2D.OverlapCircle(transform.position, chaseRange, _playerLayer))
{
UpdateState(EnemyStateType.Chase)
}
else
{
UpdateState(EnemyStateType.Idle);
}
enemyStateMachine.currentState.UpdateState();
}
private void UpdateState(EnemyStateType state)
{
}
private void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, attackRange);
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, chaseRange);
}
}
이렇게 기본적인 FSM 상태변화 구조가 만들어졌습니다.
이제 플레이어를 따라가고, 대기하고, 공격하는 구현은 내일 공부할때 (2편에서) 마저 하도록 하겠습니다.
감사합니다
'공부블로그' 카테고리의 다른 글
| [공부 블로그] Base 키워드 (0) | 2025.10.16 |
|---|---|
| [공부 블로그] 유니티 FSM에 대하여 - 2 (0) | 2025.09.13 |
| [공부 블로그] 유니티 오브젝트 거리 측정하기 & 원리 (0) | 2025.09.04 |
| [공부 블로그] ref와 out의 차이 (2) | 2025.09.01 |
| [공부 블로그] SOLID 원칙에 대하여 (0) | 2025.08.21 |