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;
}
}
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
!