2

I've changed the Title to reflect the addition of clarifying info.

I'm following a [Unity Tutorial][1] and when it came time to test the player click controls Unity gave me an error:

"SetDestination" can only be called on an active agent that has been placed on a NavMesh.

As far as I can tell my agent is active and on the navMesh so this is more than a little confusing. I've tried rebaking the navMesh and repositioning the agent neither of which has worked.

All of the questions I've found thus far have amounted to the asker not having a navMesh at all so... yeah... not very helpful. Any suggestions on how to resolve this while be appreciated.

EDIT: I just added a quick Debug.Log(agent.isOnNavMesh); to my code & low & behold it evaluates to true. Peek confusion.

private void Start()
    {
        Debug.Log(agent.isOnNavMesh);       //Evaluates *true*
        agent.updateRotation = false;

        inputHoldWait = new WaitForSeconds(inputHoldDelay);

        destinationPosition = transform.position;
    }

EDIT-2 Put the same Debug.Log(agent.isOnNavMesh); into my public void OnGroundClick fxn & it evaluates to false after tha click. Begin intrigued confusion.

This is called by a Unity Event System:

public void OnGroundClick(BaseEventData data)
{
    Debug.Log(agent.isOnNavMesh);       //Evaluates *FALSE*
    PointerEventData pData = (PointerEventData)data;
    NavMeshHit hit;

        //Click World Position, hit info, sample distance, navMesh areas to use
    if (NavMesh.SamplePosition(pData.pointerCurrentRaycast.worldPosition, out hit, navMeshSampleDistance, NavMesh.AllAreas))
    {
        destinationPosition = hit.position;
    }
    else
    {
        destinationPosition = pData.pointerCurrentRaycast.worldPosition;
    }

    //give the agent it's destination
    agent.SetDestination(destinationPosition);
    agent.isStopped = false;
}

EDIT-3 I put Debug.Log(agent.isOnNavMesh); in to private void Update(), it evaluated to true and continues to do so even after a Click calls to public void OnGroundClick.

Disabling then enabling the agent at the start of OnGroundClick does not affect the situation

Though I'm still at a loss I'm at least closer to a solution & there's more info than "this doesn't work, please help!" now.

Here is the Code in it's full context:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.AI;

public class PlayerMovement : MonoBehaviour
{
    public Animator animator;                               //Reference to animator
    public NavMeshAgent agent;                              //Reference to NavMeshAgent
    public float inputHoldDelay = 0.5f;                     //Delay player ability to control character while interacting with interactable object
    public float turnSpeedThreshold = 0.5f;                 //minimum speed before character will turn
    public float speedDampTime = 0.1f;                      //Character rate acceleration
    public float slowingSpeed = 0.175f;                     //Character rate of neg accel
    public float turnSmoothing = 15;                        //Character rotational speed


    private WaitForSeconds inputHoldWait;                   //Coroutine Wait timer to delay player input while interacting with interactable object
    private Vector3 destinationPosition;                    //Player designated destination for the agent to pursue


    private const float stopDistanceProportion = 0.1f;
    private const float navMeshSampleDistance = 4f;


    private readonly int hashSpeedParam = Animator.StringToHash("Speed");


    private void Start()
    {
        Debug.Log(agent.gameObject.name);   //Is the "Player" object
        Debug.Log(agent.isOnNavMesh);       //Evaluates *true*
        agent.updateRotation = false;

        inputHoldWait = new WaitForSeconds(inputHoldDelay);

        destinationPosition = transform.position;
    }


    private void OnAnimatorMove()
    {
        //Velocity = Distance over Time, where Distance = change in position between frames & Time = time between frames
        agent.velocity = animator.deltaPosition / Time.deltaTime;
    }


    private void Update()
    {
        Debug.Log(agent.isOnNavMesh);       //Evaluates *true*
        //If path pending, do nothing
        if (agent.pathPending)
            return;

        float speed = agent.desiredVelocity.magnitude;

        if (agent.remainingDistance <= agent.stoppingDistance * stopDistanceProportion)
        {
            Stopping(out speed);
        }
        else if (agent.remainingDistance <= agent.stoppingDistance)
        {
            Slowing(agent.remainingDistance, out speed);
        }
        else if(speed > turnSpeedThreshold)
        {
            Moving();
        }

        animator.SetFloat(hashSpeedParam, speed, speedDampTime, Time.deltaTime);
    }


    private void Stopping(out float speed)
    {
        agent.isStopped = true;
        transform.position = destinationPosition;
        speed = 0.0f;
    }


    private void Slowing(float distanceToDestination, out float speed)
    {
        agent.isStopped = true;
        transform.position = Vector3.MoveTowards(transform.position, destinationPosition, slowingSpeed * Time.deltaTime);

        float proportionalDistance = 1f - distanceToDestination / agent.stoppingDistance;
        speed = Mathf.Lerp(slowingSpeed, 0, proportionalDistance);
    }


    private void Moving()
    {
        Quaternion targetRotation = Quaternion.LookRotation(agent.desiredVelocity);
        transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, turnSmoothing * Time.deltaTime);
    }


    public void OnGroundClick(BaseEventData data)
    {
        agent.enabled = false;              //Disabling then enabling the agent...
        agent.enabled = true;               //does not change anything.

        Debug.Log(agent.gameObject.name);   //Is the "Player" object
        Debug.Log(agent.isOnNavMesh);       //Evaluates *FALSE*

        PointerEventData pData = (PointerEventData)data;
        NavMeshHit hit;

            //Click World Position, hit info, sample distance, navMesh areas to use
        if (NavMesh.SamplePosition(pData.pointerCurrentRaycast.worldPosition, out hit, navMeshSampleDistance, NavMesh.AllAreas))
        {
            destinationPosition = hit.position;
        }
        else
        {
            destinationPosition = pData.pointerCurrentRaycast.worldPosition;
        }

        //give the agent it's destination
        agent.SetDestination(destinationPosition);
        agent.isStopped = false;
    }
}
Ruzihm
  • 19,749
  • 5
  • 36
  • 48
R Kyle Butler
  • 23
  • 1
  • 5
  • Was hoping to avoid bogging it down with code, but if it helps I just added it. The Update is nothing special the "OnGroundClick" how ever is accessed by the event system. – R Kyle Butler Jun 12 '19 at 21:48
  • While in `Update()` `Debug.Log(agent.isOnNavMesh)` always evaluates to true, even during a click event that calls `OnGroundClick()`. – R Kyle Butler Jun 12 '19 at 21:57
  • Put `Debug.Log(agent.gameObject.name);` into the `Start()` and `OnGroundClick()` both are the *Player* object as it should be. Yes all of these methods are in the same *PlayerMovement.cs* script. – R Kyle Butler Jun 12 '19 at 22:10
  • Sadly did not work. – R Kyle Butler Jun 12 '19 at 22:27
  • Can you add `Debug.Log("update: " + GetInstanceID());` in `Start` and `Debug.Log("OnGroundClick: " + GetInstanceID();` in `OnGroundClick` and let me know if each one prints exactly once if you load the scene and click on the ground, and that they show the exact same instance id? My only guess now is that they are different instances of `PlayerMovement`, and the unity event is set up to call the wrong instance. – Ruzihm Jun 13 '19 at 05:25
  • 1
    You're right it is two different instances of player! Sweet, I can work with this! – R Kyle Butler Jun 13 '19 at 17:29
  • 1
    Thats got it! You should Post the solution with the debug suggestions! I now have a new angle to pursue before asking for asking for help. – R Kyle Butler Jun 13 '19 at 17:32

1 Answers1

0

You have two instances of the PlayerMovement component referring to two different instances of the agent!

One instance is the one agent.isOnNavMesh is true and works properly in Start and Update. The other instance refers to a different agent where agent.isOnNavMesh is false but this is the one whose OnGroundclick is being called.

We can tell because GetInstanceID() returns different values in Update vs. OnGroundClick.

The solution here is to make sure that the OnGroundClick that is registered in the event system is the one in the correct instance of PlayerMovement!

Ruzihm
  • 19,749
  • 5
  • 36
  • 48