[Bug or Feature] SetDestination where destination is already reached => hasPath = false

Hi,

we currently try to fix an edge case and now it seems like that the Unity AI API is not returning results like we would expect them.

  1. When we use SetDestination(destination) where destination is the current position of the target (because it “perfectly” reached the target already) THEN agent.hasPath will be set to FALSE. Shouldn’t it at least set it to TRUE and have a one-corner path with destination in it?
  2. When we use SetDestination(destination) and access agent.destination in the same procedure call (so e.g. one line after SetDestination) then agent.destination will still have the same value as before the SetDestination call. Also with setting via agent.destination = newDestination the error is still there. Shouldn’t it return the newly set destination?

Since BOTH issues are present we can’t workaround each other…

Are these (from our perspective) issues actually issues or is the API intended like this?

If this is intended: How could we work around this issue?

var hasPath = agent.hasPath && !agent.pathPending;

if (!hasPath) return false;

var lastCorner = agent.path.corners[agent.path.corners.Length - 1];
var distance = Vector3.Distance(transform.position, lastCorner);

return (distance <= agent.radius) || (agent.remainingDistance < agent.radius);

Currently we get this false negative on hasPath when the agent is already on its target position. However we also can’t compare with agent.destination (see issue 2).

I’d be happy if someone could give a hint to work around this issue or confirm that this is an issue. Then i’d file a bug report.

Best regards
Bennet

Hello, i think this is not a issue, why should you have a path if you are at destination ?

I have writted a little helper for NavmeshAgent, maybe it can help you to do what you want ?

namespace Assets.Scripts
{
    [RequireComponent(typeof(NavMeshAgent))]
    [ExposeClassInEditor]
    public class AgentManager : MonoBehaviour
    {
        public Vector3? destination;
        public PathState status;
        public bool isDestinationReached;
        [HideInInspector]
        public NavMeshAgent agent;

        public void Start()
        {
            agent = GetComponent<NavMeshAgent>();
            this.Freeze();
            isDestinationReached = true;
            status = PathState.Valid;

            agent.speed = GameManager.instance.Data.agentData.speed;
            agent.stoppingDistance = GameManager.instance.Data.agentData.stoppingDistance;
            agent.acceleration = GameManager.instance.Data.agentData.acceleration;
            agent.angularSpeed = GameManager.instance.Data.agentData.angularSpeed;
        }

        public void Freeze()
        {
            agent.updatePosition = false;
            agent.updateRotation = false;
            agent.velocity = Vector3.zero;
            agent.Stop();
        }

        public void Resume()
        {
            agent.updatePosition = true;
            agent.updateRotation = true;
            agent.Resume();
        }

   
        [ExposeFunctionInEditor]
        public void SetDestination(Vector3 destination)
        {
            agent.ResetPath();
            this.destination = destination;
            status = PathState.Calculating;
            agent.SetDestination(destination);     
            this.isDestinationReached = false;
        }
        public void Update()
        {
            UpdateDestinationReached();
            UpdatePathStatus();
        }

        private void UpdateDestinationReached()
        {
            if (destination == null)
            {
                isDestinationReached = true;
                return;
            }


            if(!isDestinationReached)
            {
                if (!agent.pathPending)
                {
                    if (agent.remainingDistance <= agent.stoppingDistance)
                    {
                        if (!agent.hasPath && this.status != PathState.Calculating )
                        {
                            isDestinationReached = true;
                            destination = null;
                            this.Freeze();
                           // this.destination = null;
                        }
                    }
                }
            }


        }

        private void UpdatePathStatus()
        {
            if (destination != null)
            {
                if (!agent.pathPending && !isDestinationReached)
                {
                    switch (agent.pathStatus)
                    {
                        case NavMeshPathStatus.PathComplete:

                                if (agent.hasPath || isDestinationReached)
                                {
                                    this.status = PathState.Valid;
                                    this.Resume();
                                }else
                            {
                                this.status = PathState.InError;
                                this.isDestinationReached = true;
                                this.Freeze();
                            }
                       

                            break;
                        case NavMeshPathStatus.PathPartial:
                            this.status = PathState.InError;
                            break;
                        case NavMeshPathStatus.PathInvalid:
                            this.status = PathState.InError;
                            break;
                    }
                }
            }
        }

    }

    public enum PathState
    {
        Calculating,
        InError,
        Valid,
    }

}

I would need this just because of the problem described above.

Just assume that you want to know if an agent has arrived at destination X, ok? Normally you would do SetDestination and then basically check with remaingDistance + a bit of checking of pathPending. This works quite well if your actual position != X. However if the agents position = X then all this does not work anymore. You could start comparing agent.destination vs actual transform position BUT then you can get a false positive - the destination by default is the agents position, so each new agent will return that he arrived at the final destination in this case.