Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Slastraf · Feb 28, 2018 at 02:24 PM · aicoroutinenavmeshagentnavigationcoroutine errors

Update SetDestination only every 0.2s with coroutines ?

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 ?

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

5 Replies

· Add your reply
  • Sort: 
avatar image
2
Best Answer

Answer by Chik3r · Feb 28, 2018 at 02:41 PM

Hi @Slastraf
Your code but with what you want:

  void Start() {
          navAgent = GetComponent<NavMeshAgent> ();
          StartCoroutine(recalcPath());
  }
      IEnumerator recalcPath ()
      {
          while(true){ //makes unity freeze 
              // updatePath = Vector3.Distance(transform.position, target.position) < .3f) ? false : true // Uncomment this line if you want it to stop at a certain distance (.3f). 
              if (updatePath == true) {
                  yield return new WaitForSeconds (.2f); // Wait for .2 seconds or 200 miliseconds
                  navAgent.SetDestination (target.position);
                  Debug.Log ("NavAgent called");
              }
          yield return null; //So that unity doesn't crash
          }
      }

Also, Unity was freezing with the coroutine because whenever "updatePath" was false there would be no yield return, so it would wait forever.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Slastraf · Feb 28, 2018 at 02:57 PM 1
Share

this also worked, but was closest to the original script. tough I would add what Harinezumi added with the memory efficient use of wait

avatar image
3

Answer by Bunny83 · Feb 28, 2018 at 02:52 PM

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.

So one solution is to do something like this:

 while(true)
 {
     if (updatePath == true)
     {
         navAgent.SetDestination (target.position);
         Debug.Log ("NavAgent called");
         yield return new WaitForSeconds (2f);
     }
     else
         yield return null;
 }

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:

 while(true)
 {
     while(!updatePath)
         yield return null;
     navAgent.SetDestination (target.position);
     Debug.Log ("NavAgent called");
     yield return new WaitForSeconds (2f);
 }

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.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
1

Answer by dishant27 · Feb 28, 2018 at 02:37 PM

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:

 IEnumerator recalcPath ()
      {
                      yield return new WaitForSeconds (0.2f);
                      navAgent.SetDestination (target.position);
                      Debug.Log ("NavAgent called");
                      if(updatePath)
                      {
                              StartCoroutine(recalcPath());
                             yield return null;
                       }
     }

Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Chik3r · Feb 28, 2018 at 02:43 PM 0
Share

But when it reaches some distance it would continue recalculating. And Slastraf wants it to stop at a distance.

avatar image dishant27 Chik3r · Feb 28, 2018 at 02:47 PM 1
Share

When the updatePath variable is false, it'll end. So, when he wants to stop, he can set updatePath = false anywhere in the script.

avatar image Slastraf dishant27 · Feb 28, 2018 at 02:59 PM 0
Share

true, but you would need to call recalcPath () somewhere again, if the player is in range agian

avatar image Bunny83 · Feb 28, 2018 at 02:59 PM 0
Share

This is generally a bad implementation. Constantly starting a new coroutine is not a good idea. Just use a loop. A coroutine is not a method but a statemachine. Each time you start a new coroutine you will create a new instance of that statemachine. Also the yield return null; after your StartCoroutine is pretty pointless as nothing happens after it. You just keep the coroutine alive for one additional frame.


Apart from that he starts the coroutine in Start. So he wants the coroutine to keep running. In your case he has to actually start a new coroutine when he wants it to continue,

avatar image
1

Answer by Harinezumi · Feb 28, 2018 at 02:42 PM

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");
     }
 }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by Slastraf · Feb 28, 2018 at 03:29 PM

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.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

135 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Way to find closest point in a specific area on navmesh. 0 Answers

2 NavMeshAgent 0 Answers

CS0246: The type or name space name ''NavMeshSurface'' plz some help 0 Answers

Allow navmesh agents to be pushed off navmesh 1 Answer

How can I make the NavMeshAgent travel only in straight lines? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges