프로젝트 기록

[Warning-of-the-Monsters] 인간 상태 머신 설계

binary는 호남선 2025. 5. 24. 23:04

 

 본 게임에서 인간 캐릭터들이 몬스터를 공격하거나 도망가는 등 다양한 행동을 한다. 이런 복잡한 행동을 깔끔하게 구현하기 위해 가장 널리 쓰이는 패턴 중 하나가 바로 상태 머신(State Machine) 이다. HumanStateMachine과 관련된 주요 클래스와 상태들을 분석하며, 이 시스템이 어떻게 동작하고 구조적으로 어떤 장점을 가지는지를 살펴보자.

 

핵심 구성 요소

클래스 역할
HumanController 캐릭터의 상태, 속도, 타겟 등을 제어하는 중심 클래스
HumanStateMachine 현재 상태를 관리하고 상태 전이를 담당
IHumanState 상태들의 인터페이스 (Enter/Update/Exit 메서드 정의)
WalkHumanState, RunHumanState, BattleHumanState 각각 걷기, 도망, 전투 상태를 구현한 클래스

각각의 상태는 Enter(), Update(), Exit() 세 가지 메서드로 구성되어 있고, HumanStateMachine은 이 상태 간의 전이를 조절한다.


캐릭터의 3가지 행동 상태

1. WalkHumanState - "거점으로 향하는 중"

  • 상태 진입 시(Enter) :
    • 애니메이션을 "걷기"로 설정.
    • 스테이지의 목표 지점(거점)으로 이동 경로 설정.
  • 상태 갱신 중(Update) :
    • TargetMonster가 생기면 즉시 전투 상태로 전환.
if (_human.TargetMonster != null) {
    _stateMachine.ChangeState(_human.BattleHumanState);
}
  • 상태 종료 시(Exit):
    • 이동 경로 초기화.

2. BattleHumanState - "몬스터와 전투 중"

  • 상태 진입 시(Enter) :
    • "전투" 애니메이션으로 전환.
    • TargetMonster의 위치로 이동 설정.
    • 회피 우선순위를 최상으로 설정하여 충돌 방지.
  • 상태 갱신 중(Update) :
    • 타겟 몬스터가 사라지면 WalkHumanState로 전환.
    • 일정 시간이 지나고 쿨타임이 지나면 PerformAttack()으로 공격 실행.
if (Time.time >= _lastAttackTime + _human.Cooldown && !_human.isSurprising)
{
    PerformAttack();
}
  • 공격 메서드(PerformAttack):
    • 랜덤한 피로도 값을 몬스터에게 적용.
    • 파티클 및 공격 애니메이션 실행.
  • 상태 종료 시(Exit) :
    • 경로 초기화 및 회피 타입 원복.

3.  RunHumanState - "공포에 질려 도망 중"

  • 상태 진입 시(Enter) :
    • 애니메이션 속도와 이동 속도 증가.
    • 스폰된 지점으로 이동.
  • 상태 갱신 중(Update) :
    • 도착 지점에 다다르면 원래는 풀로 반환 처리 (현재 주석 처리됨).
    • 놀란 상태에서 도망가다 풀로 반환하는 것으로 변경됨
private void SetBattleState()
{
    if (FearLevel >= MaxFear) // 갱신된 값이 최댓값보다 크면
    {
        controller.stateMachine.ChangeState(controller.RunHumanState); // 도망 상태로 전환
        ReturnHumanToPool(3.0f); // 인간을 풀로 반환
    }
}
 
  • 상태 종료 시(Exit) :
    • 경로 초기화.

상태 전이의 트리거

상태 전이는 다음과 같은 상황에서 발생한다:

  • 몬스터를 감지하면 → 걷기 상태에서 전투 상태로.
  • 공포 수치가 최대치에 도달하면 → 전투 상태에서 도망 상태로.
  • 타겟 몬스터가 사라지면 → 전투 상태에서 다시 걷기 상태로.

전이는 모두 HumanStateMachine.ChangeState()를 통해 이루어진다:

public void ChangeState(IHumanState newHumanState)
{
    _currentHumanState?.Exit();
    _currentHumanState = newHumanState;
    _currentHumanState?.Enter();
}

 

HumanController: 상태 머신의 중심 허브

HumanController는 각 상태 객체들을 보유하고 있으며, 상태 머신과 상호작용하는 주요 클래스이다.

WalkHumanState = new WalkHumanState(this);
RunHumanState = new RunHumanState(this);
BattleHumanState = new BattleHumanState(this);
  • OnEnable()에서 기본 상태를 걷기로 초기화하고 애니메이션/속도를 세팅.
  • Update()에서 현재 상태의 Update()를 지속적으로 호출.

상태 전환의 핵심 조건: 공포 시스템 (Fear)

Human.cs에는 인간의 공포 수치를 관리하는 시스템이 있고, 일정 수치 이상이 되면 도망 상태로 전환된다.

공포 증가 시 Surprise 애니메이션이 실행되며, 타겟 몬스터도 클리어되고 도망 로직이 활성화된다.

private void SetBattleState()
{
    if (FearLevel >= MaxFear) // 갱신된 값이 최댓값보다 크면
    {
        controller.animator.SetBool("IsBattle", false);
        controller.ClearTargetMonster();
        controller.targetMonsterList.Clear();
        controller.stateMachine.ChangeState(controller.RunHumanState); // 도망 상태로 전환
        StageManager.Instance.ChangeGold(_coin);    // 스테이지 보유 재화 갱신
        ReturnHumanToPool(3.0f); // 인간을 풀로 반환
    }
}

상태 머신 사용의 이점

  • 유지보수 용이: 각 상태가 독립적으로 동작하므로 버그 수정과 확장이 쉬움
  • 디버깅 편리: 현재 상태만 확인하면 캐릭터의 AI를 추적 가능
  • 유연한 확장: 새로운 상태를 쉽게 추가 가능

이처럼 상태 머신을 사용하여 복잡한 AI 동작을 간단하게 구현하였다. 전투-도망-걷기와 같은 다양한 행동은 각각 독립적인 클래스에서 정의되어 있고, HumanController를 중심으로 통합되어 유기적으로 작동한다.