NavmeshAgent.remainingDistance remains at 0 but the agent still moves and behaves correctly

Hello,

I’m having a rather weird bug with Unity’s navmesh system (I’m using Unity Free 4.2). Basically, the navmesh system itself works correctly, the unit goes from A to B and avoids the obstacles. However, sometimes if I try to log the remainingDistance variable as it moves it stays at zero the whole time. This is slightly annoying because I want to know whether the unit is moving so that I can draw its selectionbox at the right position (this is for a RTS game), and of course I’m using the “remainingDistance” variable to know when the unit has reached its destination.
What really confuses me is that for two identical units (same prefab, same script, same everything as far as I can tell), one will have the correct variable and the other one won’t. Even weirder, the units who are “bugged” still behave normally if I don’t let them stop. What I mean is, I tell unit 1 to go from A to B. It will bug out and say that remainingDistance is 0 despite it clearly moving (let me reiterate that even when I say a unit is “bugged”, it still moves around as expected so it’s not like the navmesh system itself doesn’t work). If I wait until it reaches B and then tell it to go to C, the same will happen -except the position will be recalculated once, at the time of the click-. But if I give it another order mid-moving, then suddenly it starts working normally. So if I keep giving it new orders it somehow works, but if it ever stops once, then it’s going to bug again until I give two orders in a row.

Now there are obviously hacky ways to solve it, such as treating any click as a double-click or issuing the command twice if the unit was not moving before. But it’s a dirty hack and I’d rather solve the problem than hide it with a hack. If it’s possible to solve it, that is.

Thanks in advance

I’ve encountered the same problem and found another solution:

The NavMeshAgent has a property called pathPending which becomes false once the calculation of the path has finished. So inside the Update method I check for pathPending and only continue once pathPending is false.

The reason for this issue is that pathfinding can be a tedious process, so to avoid lag the unity path system is using multithreading, to expand the calculation over multiple frames, and take advantage of the multiple cpu-cores even smartphones have nowadays. The issue now is that if you put agent.remainingDistance() in the update method, it will return 0 or the value prior to the agent.destination() call.

To avoid this, start the method with

if(agent.pathPending)
      return;

Then the call will be ignored until the path is calculated.

Before you use the agent.SetDestination(POS) method, you may call the agent.ResetPath method, because I also encountered this problem, but I solved the problem. I’m Chinese.

It does not behave correctly. And ResetPath doesn’t solve the problem (for me).
Waiting a couple of frames (1 is not enough) after setting a new destination works.
The problem shows up in the tutorial “John Lemon’s Haunted Jaunt: 3D Beginner”:
The ghosts will keep turning around because remainingDistance < stoppingDistance.

Fix for WaypointPatrol.cs:

using System.Collections;
using System.Collections.Generic;
  
using UnityEngine;
using UnityEngine.AI;
  
public class WaypointPatrol : MonoBehaviour
{
  
	public NavMeshAgent navMeshAgent;
	public Transform[] waypoints;
	public bool LogTurns = false;
  
	int m_CurrentWaypointIndex = 0;
  
	int wait=0, waitTix=3;
  
	void Start()
	{
		navMeshAgent.SetDestination( waypoints[m_CurrentWaypointIndex].position );
		wait = waitTix;
	}
  
	void Update()
	{
		if (--wait > 0) return;
  
		if (navMeshAgent.remainingDistance < navMeshAgent.stoppingDistance)
		{
			m_CurrentWaypointIndex = (m_CurrentWaypointIndex + 1) % waypoints.Length;
  
			if (LogTurns)
				Debug.Log(this.gameObject.name + " new destination Index: "+m_CurrentWaypointIndex);
  
			navMeshAgent.SetDestination( waypoints[m_CurrentWaypointIndex].position );
			wait = waitTix;
		}
	}
}

As a complete noob this took a while to figure out - but I learned a lot : )

BTW: I’m using the Unity-2019-2.0f1

Unfortunately, I get the same error. My goal is to have an enemy patrol between certain points in the scene with the Navmesh Agent. There is no problem if there is only one waypoint in the scene, but when I add two or more waypoints, the enemy object gets confused and confused about where to go.

When I print the Remaining Distance and Stopping Distance to the console, it turns out to be 0 even if the enemy object moves, that is, it goes towards the waypoint.

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.AI;

public class ZombiePatrolingState : StateMachineBehaviour
{
    float timer;
    public float patrolingTime = 10f;

    Transform player;
    NavMeshAgent agent;

    public float detectionArea = 18f;
    public float patrolSpeed = 1f;

    List<Transform> waypointList = new List<Transform>();

    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        // --- Inıtialization --- //

        player = GameObject.FindGameObjectWithTag("Player").transform;
        agent = animator.GetComponent<NavMeshAgent>();

        agent.speed = patrolSpeed;
        timer = 0;

        // --- Get all waypoints and Move to First Waypoint --- //

        GameObject waypointCluster = GameObject.FindGameObjectWithTag("Waypoints");
        foreach (Transform t in waypointCluster.transform)
        {
            Debug.Log(t);
            waypointList.Add(t);
        }

        Vector3 nextPosition = waypointList[Random.Range(0, waypointList.Count)].position;
        agent.SetDestination(nextPosition);

    }

    override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        // --- If agent arrived at waypoint, move to next waypoint --- //
        if(agent.remainingDistance <= agent.stoppingDistance)
        {
            Debug.Log("Remaining Distance: " + (agent.remainingDistance));
            Debug.Log("Stopping Distance:" + (agent.stoppingDistance));
            agent.SetDestination(waypointList[Random.Range(0, waypointList.Count)].position);
        }

        // --- Transition to Idle State --- //

        timer += Time.deltaTime;
        if(timer > patrolingTime)
        {
            animator.SetBool("isPatroling", false);
        }

        // --- Transition to Chase State --- //

        float distanceFromPlayer = Vector3.Distance(player.position, animator.transform.position);
        if (distanceFromPlayer < detectionArea)
        {
            animator.SetBool("isChasing", true);
        }



    }

    // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        // Stop the agent
        agent.SetDestination(agent.transform.position);

    }
}