이제 몬스터가 플레이어를 따라가게 만들기 위해 NavMesh를 적용해본다.

패키지메니져에서 AI Navigation을 설치해준다.

바닥면엔 NavMeshSurface 컴포넌트를 추가해 NavMeshAgent 컴포넌트를 적용한 몬스터가 잘 돌아다니도록 만들어준다.
그리고 MinoController.cs에 "_agent.SetDestination(_player.position);"를 추가해준다.
플레이어 포지션을 감지해 자동으로 따라가게 만들려고하는데 문제가 발생했다.
그리고 해결의 결론부터 말하자면 이렇다.
1. 공격하는 애니메이션 플레이 도중에 플레이어가 움직이면 공격하는 애니메이션이 재생되는 상태에서 플레이어를 따라다닌다.
2. Backstep 후 Stomp모션을 하는데 Backstep은 velocity를 적용해 뒤로 점프하는 모션을 넣었는데 점프는 하지만 플레이어한테 향하기 때문에 거리가 안 벌어진다.
처음엔 일딴 메소드 부터 만들었다.
조건은 특정상황에서 애니메이션이 재생될 때 isStoped의 반환이 true / false 인지에 따라 NavMeshAgent의 추척을 멈추거나 다시 재생하는 거였다.
public void TrackRangeInPlayer()
{
if (_attackRange == false && _isIdle == true)
{
if (_bossAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f)
{
_agent.enabled = true;
_agent.SetDestination(_player.position);
}
if (_bossAnimator.GetCurrentAnimatorStateInfo(0).IsName("BossTrack"))
{
_agent.enabled = true;
_agent.SetDestination(_player.position);
}
}
else if (_attackRange == true && _isIdle == true)
{
if (_bossAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f)
{
transform.LookAt(_player.position);
}
}
if (_bossAnimator.GetCurrentAnimatorStateInfo(0).IsName("BossJumpAttack"))
{
_agent.isStopped = true;
if (_bossAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f)
{
_agent.isStopped = false;
}
}
if (_bossAnimator.GetCurrentAnimatorStateInfo(0).IsName("BossBackstep"))
{
_agent.isStopped = true;
}
if (_bossAnimator.GetCurrentAnimatorStateInfo(0).IsName("BossStompAttack"))
{
_agent.isStopped = true;
if(_bossAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f)
{
_agent.isStopped = false;
}
}
}
그리고 팀장님이 이 어지러운 코드를 노드에 넣어서 구동시켜보자고 한다.
3. 플레이어가 몬스터의 뒤로 향해도 플레이어가 공격범위 내에 있으면 플레이어를 보지 않고도 공격모션을 한다.
이 세가지 문제를 정리한 코드는 아래와 같다.
private void Awake()
{
_bossAnimator = GetComponent<Animator>();
_rigidbody = GetComponent<Rigidbody>();
_agent = GetComponent<NavMeshAgent>();
_treeA = new BehaviorTreeBuilder(gameObject)
.Selector()
.Sequence()
.Condition("attackRange", () => _dist <= _attackRange)
.Selector()
.Sequence()
.Condition("backPOS", () => _isForward == true)
.Do(() =>
{
_agent.isStopped = true;
return TaskStatus.Success;
})
.StateAction("BossBackstep", ProcessBackstep)
.StateAction("BossStompAttack")
.End()
.Sequence()
.Condition("keepDEF", () => _keepDEF == true)
.StateAction("BossKickAttack")
.End()
.SelectorRandom()
.StateAction("BossATK1")
.StateAction("BossATK2")
.StateAction("BossATK3")
.StateAction("BossATK4")
.End()
.End()
.End()
.Sequence()
.Condition("out7SEC", () => _dist >= _minJump && _dist < _maxJump)
.Do(() =>
{
_agent.isStopped = true;
return TaskStatus.Success;
})
.StateAction("BossJumpAttack", ProcessJumpAttack)
.End()
.RepeatUntilSuccess()
.Do("BossTrack", () =>
{
_bossAnimator.Play("BossTrack");
_agent.isStopped = false;
_agent.SetDestination(_player.position);
if (_dist > 5f)
{
return TaskStatus.Success;
}
else
{
_agent.isStopped = true;
return TaskStatus.Failure;
}
})
.End()
.End()
.Build();
}
// 플레이어와의 위치 계산
private void _checkDistance()
{
_dist = Vector3.Distance(_player.position, transform.position);
Vector3 directionToPlayer = (_player.position - transform.position).normalized;
Vector3 bossForward = transform.forward;
float dotProduct = Vector3.Dot(directionToPlayer, bossForward);
if (dotProduct > 0.5f)
{
_isForward = false;
}
else
{
_isForward = true;
}
}
Behavior Tree의 조건식과 구현에 더 익숙해진거같다.