Understanding coroutines

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.

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?

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.

1 Like

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:

public IEnumerator MyRoutine()
{
   while(true)
   {
     //do stuff
     
     yield return null;
   }
}

Personally I like the ‘goto’:

public IEnumerator MyRoutine()
{
Loop:
   //do stuff

   yield return null;
   goto Loop;
}

Another routine will start parrallel to the already running one.

Every time you call an iterator function, a brand new enumerable is generated.

1 Like

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: Advanced/Intermediate 3D Artificial Intelligence Tutorial Series - Learn Content & Certification - Unity Discussions (Section 2.4)

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.

1 Like

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.

2 Likes