Hello, I am trying to have a more smoother game by not calculating a new AI path every update frame. I am trying to use coroutines because I heard it is more efficient/effective than InvokeRepeating(). At the same time I want to make the agent stop calculating a path when it is in a certain distance, or out of sight, but it seems to work with Agent.isStopped .
Heres the shortened code:
void Start() {
navAgent = GetComponent<NavMeshAgent> ();
StartCoroutine(recalcPath());
}
IEnumerator recalcPath ()
{
while(true){ //makes unity freeze
if (updatePath == true) {
yield return new WaitForSeconds (2f);
navAgent.SetDestination (target.position);
Debug.Log ("NavAgent called");
}
//do not use StartCoroutine (recalcPath ()); it makes unity also freeze
}
}
By using it as I did, the unity editor just freezes and I need to force close it.
How is it normally made ?
Just think logically. It “updatePath” is false the if statement will not be executed and you end up with an empty while loop:
while(true){
}
The most important thing you have to keep in mind is that when you use an infinite loop in a coroutine that you always need to yield every iteration (or at least every other iteration). Otherwise your main thread will be caught inside the loop and it can never get out of it.
When updatePath is false we just yield null so the coroutine does check updatePath every frame. If updatePath is true it will set the destination and yield for 2 seconds.
If you don’t need the SetDirection to react immediately to a change of updatePath you can also do
while(true)
{
if (updatePath == true)
{
navAgent.SetDestination (target.position);
Debug.Log ("NavAgent called");
}
yield return new WaitForSeconds (2f);
}
Here we simply yield outside the if statement. However that means when updatePath is set to true it can take up to 2 seconds until the first SetDestination will be called.
Another way is to implement a blocking loop inside the while loop like this:
When updatePath is true the inner while loop will be skipped and every 2 seconds we will call SetDestination. If updatePath is false the inner while loop will block the coroutine by simply yielding null. So the inner while loop will run once every frame until updatePath turns true.
Unity is freezing because for while(true) condition which always true and your code runs into never ending infinite loop. You can change your recalcPath method:
It seems updatePath is not true when you enter the while loop, so it will never get updated, and so you will never call the yield instruction, which results in an infinite loop on the same frame, so Unity freezes.
You can rewrite it the following way:
IEnumerator recalcPath () {
YieldInstruction wait = new WaitForSeconds(0.2f); // more memory-efficient to reuse the wait
while (true) {
yield return wait;
navAgent.SetDestination(target.position); // probably you should check that target is not null
Debug.Log ("NavAgent.SetDestination() called");
}
}
Most efficient and easiest to use version would be this:
IEnumerator recalcPath ()
{
YieldInstruction wait = new WaitForSeconds(0.2f); // more memory-efficient to reuse the wait
while(true)
{
if (updatePath)
navAgent.SetDestination (target.position);
yield return wait;
}
}
updatePath should be false at the start, and change the boolean if a new path should be calculated or not. If the agent needs to stop use Agent.isStopped = true but make sure to set the latter false again.
The problem with “yield return null;” is that the function or method will be stopped and therefore cannot be handled in update, because it needs to be called just once. In update you can se tthe booleans.