Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Understanding coroutines

Discussion in 'Scripting' started by Lahzar, Sep 12, 2015.

  1. Lahzar

    Lahzar

    Joined:
    May 6, 2013
    Posts:
    87
    Hey guys. I am having some troubles with understand how coroutines work. All I know is that they work as threads, which allows for calculations over a timeframe instead of just one frame.

    Essentially I want to run a coroutine every 0.5 seconds. The setup I have now works, but I know it can be improved. I have a boolean which is set to true when the routine is started, and set to false when the routine is finished.

    The coroutine usually takes < 0.025 seconds to complete, before it waits for half a second. Then it restarts. Second problem is how can I improve this. First problem is that I barely understand how this works. Thrid problem is the lag.

    The coroutine performs a check on 100 objects inside a foreach loop. When I try to run this it lagged. Then I added this code inside the loop, and the lag was gone.
    Code (CSharp):
    1. if(itemNr % 23 == 0) yield return new WaitForSeconds(0.01f);
    From what Ive heard about coroutines I shouldn't need to add this line in a coroutine?

    My coroutine chain of events:
    bool = false -> start coroutine -> set bool to true -> do X checks -> wait a bit -> repeat checks and wait until Y checks have been performed -> wait for 0.5 seconds -> set bool to false -> yield return false -> repeat




    TLDR:
    What is the difference between yield return true, false and null? Can someone explain to me what yield return actually does, as if I was a third grader? What is the best/slickest (not necessarily easiest) way to repeat a coroutine every time it finishes? If I try to start a coroutine which is already running, what will happen?
     
  2. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,384
    Yield return 0, false and null all wait one frame and continue from the next line.

    You can use co routines for timed things without dealing with deltatime calcs by taking advantage of WaitForSeconds as well as making an FSM with them.

    To repeat a co routine inside itself you can use while (true) inside it and put all your logic there, then when it finishes it will start again because the condition true is constant.

    Else, you can just StartCoroutine(ThisName()) again when it reaches the end of it. You need at least one yield return inside it to prevent infinite loops.

    Starting a coroutine always starts a new one. Think of then as instances. If you use the latter to do so, make sure it's not in a while(true) loop or the starter will never end and you'll create new routines indefinitely without any ending.
     
    Last edited: Sep 12, 2015
    Lahzar likes this.
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    It'll wait one frame. Though always use 'null' since true/false/0/etc. are value types, and will have to get boxed in memory and cause unnecessary garbage. Long story short... it will cause hiccups in your game.

    The yield thing comes from a .Net feature called 'iterator functions':
    https://msdn.microsoft.com/en-us/library/dscyy5s0.aspx

    An iterator function allows you to define a function that programmatically defines an enumerable list of data. While some code is enumerating over that data, the code is generating the next piece of data on demand. The 'yield' statement is saying "this is the next value to return in the list". And because it's coded... you can have conditions for if you yield it, or what is actually yielded.

    Unity now took this idea and figured to themselves.

    What if we allow you to write a list of instructions. And we 'foreach' it one tick every frame. This means that the code between yield statements occurs in a frame and then we can wait for the next frame.

    And with yield instructions like 'WaitForSeconds' we can have quick easy representations for common actions... instead of yielding null for a duration of time, unity interprets this WaitForSeconds object as meaning to do that.

    Loop... you could do a while(true) loop:

    Code (csharp):
    1.  
    2. public IEnumerator MyRoutine()
    3. {
    4.    while(true)
    5.    {
    6.      //do stuff
    7.      
    8.      yield return null;
    9.    }
    10. }
    11.  
    Personally I like the 'goto':

    Code (csharp):
    1.  
    2. public IEnumerator MyRoutine()
    3. {
    4. Loop:
    5.    //do stuff
    6.  
    7.    yield return null;
    8.    goto Loop;
    9. }
    10.  
    Another routine will start parrallel to the already running one.

    Every time you call an iterator function, a brand new enumerable is generated.
     
    Lahzar likes this.
  4. Lahzar

    Lahzar

    Joined:
    May 6, 2013
    Posts:
    87
    Thanks alot @LaneFox and @lordofduct! This helped shorten my code alot, and made it alot easier to read. I still have some small questions like why is there a yield return null, true AND false, if they all do the same? But these can all be answered by a quick google search I would imagine, and if they can't it's not a big deal.

    The specific code I used was a line of sight check system, and if you want to find out more about it, the full thing is available here for free: http://forum.unity3d.com/threads/ad...tificial-intelligence-tutorial-series.350062/ (Section 2.4)
     
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Because you can yield return ANYTHING.

    There's nothing that unity doesn't allow to be returned, because the IEnumerator enumerates over the type 'object', which all types in .Net/Mono can be treated as 'object'.

    It's a matter of what unity determined giving meaning to.

    They put meaning to a few types, which usually inherit from YieldInstruction. Things like:

    WaitForSeconds
    WaitForEndOfFrame
    WaitForFixedUpdate
    Coroutine
    WWW


    the sort

    Where as everything else, no matter it be boolean, int, GameObject, whatever... they all get treated the same, which is the default bahaviour of the coroutine. And that is... wait one frame.
     
    Lahzar likes this.
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Not exactly at the third grade level, but I found this explanation dramatically aided my understanding of coroutines. It's quite technical, but it helped me to be aware of what's under the hood.

     
    giggioz and Lahzar like this.