TIL: Unity 보스 상태 패턴 적용

김보근·2025년 2월 26일

Unity

목록 보기
102/113

TIL: Unity 보스 상태 패턴 적용

오늘은 보스 AI의 상태 패턴을 구현했다. 보스의 행동을 다양한 상태로 나누어 관리하기 위해 StateMachine을 활용했고, 각 상태별로 코루틴을 사용하여 애니메이션과 행동을 제어하도록 구성했다.

1. BossThinkState (사고 상태)

보스의 행동을 결정하는 상태로, Enter()에서 Think() 코루틴을 실행하여 랜덤한 행동을 선택한다.

IEnumerator Think()
{
    yield return new WaitForSeconds(0.1f);

    int ranAction = Random.Range(0, 5);
    switch (ranAction)
    {
        case 0:
        case 1:
            stateMachine.SetState(new BossMissileShot(stateMachine, animator, boss));
            break;
        case 2:
        case 3:
            stateMachine.SetState(new BossRockShotState(stateMachine, animator, boss));
            break;
        case 4:
            stateMachine.SetState(new BossTauntState(stateMachine, animator, boss));
            break;
    }
}

0.1초 대기 후, 5가지 행동 중 하나를 랜덤으로 선택하여 상태를 변경한다.

2. BossTauntState (점프공격 상태)

점프공격을 수행하며, Taunt() 코루틴을 통해 보스가 일정 시간 이동 후 공격을 수행하도록 설정했다.

IEnumerator Taunt()
{
    boss.tauntVec = boss.target.position + boss.lookVec;
    boss.isLook = false;
    boss.agent.isStopped = false;
    boss.bc.enabled = false;
    animator.Play("Taunt");
    boss.agent.SetDestination(boss.tauntVec);
    
    yield return new WaitForSeconds(1.5f);
    boss.meleeArea.enabled = true;
    
    yield return new WaitForSeconds(1.5f);
    boss.meleeArea.enabled = false;
    
    yield return new WaitForSeconds(3f);
    boss.isLook = true;
    boss.bc.enabled = true;
    stateMachine.SetState(new BossThinkState(stateMachine, animator, boss));
}

점프공격 후 3초 대기 후 다시 BossThinkState로 전환된다.

3. BossRockShotState (바위 투척 상태)

보스가 커다란 바위를 생성하여 던지는 상태.

IEnumerator RockShot()
{
    boss.isLook = false;
    animator.Play("BigShot");
    GameObject bossRock = GameManager.Instance.objectpool.Get(6);
    bossRock.transform.rotation = boss.transform.rotation;
    bossRock.transform.position = boss.transform.position;
    
    yield return new WaitForSeconds(3f);
    boss.isLook = true;
    stateMachine.SetState(new BossThinkState(stateMachine, animator, boss));
}

4. BossMissileShot (미사일 발사 상태)

보스가 두 개의 미사일을 연속으로 발사하는 상태.

IEnumerator MissileShot()
{
    animator.Play("Shot");
    GameObject instantBossMissileA = GameManager.Instance.objectpool.Get(5);
    BossMissile bossMissileA = instantBossMissileA.GetComponent<BossMissile>();
    instantBossMissileA.transform.position = boss.missilePortA.transform.position;
    bossMissileA.target = boss.target;

    yield return new WaitForSeconds(0.3f);
    
    animator.Play("Shot");
    GameObject instantBossMissileB = GameManager.Instance.objectpool.Get(5);
    BossMissile bossMissileB = instantBossMissileB.GetComponent<BossMissile>();
    instantBossMissileB.transform.position = boss.missilePortB.transform.position;
    bossMissileB.target = boss.target;

    yield return new WaitForSeconds(2f);
    stateMachine.SetState(new BossThinkState(stateMachine, animator, boss));
}

0.3초 간격으로 두 개의 미사일을 발사한 후, 2초 대기 후 상태를 변경한다.

느낀 점 & 개선할 점

코루틴을 활용하여 자연스러운 상태 전환을 구현할 수 있었다.

상태 패턴을 적용하니 보스의 행동을 모듈화하여 관리할 수 있어 유지보수가 쉬워졌다.

StateMachine.SetState()를 호출할 때 새로운 상태 인스턴스를 매번 생성하는 방식인데, 객체를 캐싱하여 재사용하는 방식으로 최적화할 수 있을 것 같다.

Think()에서 랜덤 값으로 상태를 정하는데, 특정 패턴이 반복될 가능성이 있다. 일정 확률이 아니라, 모든 행동을 균형 있게 수행하도록 보완하면 좋을 것 같다.

오늘 구현한 내용을 바탕으로 다음 개선 작업을 진행해볼 예정이다!

profile
게임개발자꿈나무

0개의 댓글