오늘은 보스 AI의 상태 패턴을 구현했다. 보스의 행동을 다양한 상태로 나누어 관리하기 위해 StateMachine을 활용했고, 각 상태별로 코루틴을 사용하여 애니메이션과 행동을 제어하도록 구성했다.
보스의 행동을 결정하는 상태로, 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가지 행동 중 하나를 랜덤으로 선택하여 상태를 변경한다.
점프공격을 수행하며, 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로 전환된다.
보스가 커다란 바위를 생성하여 던지는 상태.
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));
}
보스가 두 개의 미사일을 연속으로 발사하는 상태.
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()에서 랜덤 값으로 상태를 정하는데, 특정 패턴이 반복될 가능성이 있다. 일정 확률이 아니라, 모든 행동을 균형 있게 수행하도록 보완하면 좋을 것 같다.
오늘 구현한 내용을 바탕으로 다음 개선 작업을 진행해볼 예정이다!