public class EnemyChasingState : EnemyBaseState
{
private readonly int LocomotionHash = Animator.StringToHash("Locomotion");
private readonly int SpeedHash = Animator.StringToHash("Speed");
private const float CrossFadeDuration = 0.1f;
private const float AnimatorDampTime = 0.1f;
public EnemyChasingState(EnemyStateMachine stateMachine) : base(stateMachine) { }
public override void Enter()
{
stateMachine.Animator.CrossFadeInFixedTime(LocomotionHash, CrossFadeDuration);
}
public override void Tick(float deltaTime)
{
if (!IsInChaseRange())
{
stateMachine.SwitchState(new EnemyIdleState(stateMachine));
return;
}
else
{
MoveToPlayer(deltaTime);
FacePlayer();
stateMachine.Animator.SetFloat(SpeedHash,
stateMachine.Controller.velocity.normalized.magnitude, AnimatorDampTime, deltaTime);
}
}
public override void Exit()
{
if(stateMachine.Agent.isOnNavMesh)
{
stateMachine.Agent.ResetPath();
}
stateMachine.Agent.velocity = Vector3.zero;
}
private void MoveToPlayer(float deltaTime)
{
if (stateMachine.Agent.isOnNavMesh)
{
if (Vector3.Distance(stateMachine.Agent.destination, stateMachine.Player.transform.position) > 0.05f
|| stateMachine.Agent.remainingDistance == 0)
{
stateMachine.Agent.SetDestination(stateMachine.Player.transform.position);
}
Move(stateMachine.Agent.desiredVelocity.normalized * stateMachine.MovementSpeed, deltaTime);
}
stateMachine.Agent.velocity = stateMachine.Controller.velocity;
}
}
I created an Enemy opponent that approaches the player and dodges immediately when they get too close (i.e. within attacking range). These series of actions are set up to repeat themselves using a behaviour tree.
When the enemy approaches a player, it’s behaviour is defined by this class called EnemyChasingState. This chasing state class uses the enemy’s NavMeshAgent component stored in a managing state machine component to determine the path to travel. (the NavMeshAgent component is called by using “stateMachine.Agent”).
the MoveToPlayer(deltaTime) function is called every frame (Tick(deltaTime)) to keep track of the player while chasing the player.
At first, I added constraints using if statements to prevent the NavMeshAgent destination from being recalculated every frame.
if (stateMachine.Agent.isOnNavMesh)
{
if (Vector3.Distance(stateMachine.Agent.destination, stateMachine.Player.transform.position) > 0.05f
|| stateMachine.Agent.remainingDistance == 0)
{
stateMachine.Agent.SetDestination(stateMachine.Player.transform.position);
}
}
However, it doesn’t change the outcome. The enemy stops before it gets close enough to the player to intersect the enemy’s attacking range, which would allow the enemy to continue the behaviour tree loop of actions.
The enemy does approach the player and dodges in it’s first attempt, but after that, all further attempts to approach and dodge the player fails because the enemy stops approaching its attacking zone.
I tried setting the Stopping Distance to 0 and disabling Auto Braking in the Nav Mesh Agent, but it doesn’t seem to do anything.
Essentially, after dodging the first time, the enemy approaches the player, but then stops a certain distance away from the player. When that happens, the NavMeshAgent.remainingDistance property is set to 0 and the desiredVelocity is set to Vector3.zero. whereas the actual distance from the player is further than the enemy’s attacking range to continue looping the behaviours.
I’ve tried every test I could think of but I can’t figure out what is stopping the NavMeshAgent prematurely. The EnemyChasingState is the only state that makes use of the NavMeshAgent component for locomotion (through stateMachine.Agent), and in any case where it exits the chasing state, the NavMeshAgent.ResetPath() is called to stop the nav mesh agent from tracking anything.
I don’t know if there is some kind of way to debug the NavMeshAgent source code directly. I don’t really know how the component gets its values. Has anyone encountered this problem?