Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding The Execution of Coroutines

Tags:

c#

unity3d

I'm working in Unity, but this is really just a C# question so I hope this is the right section to post this. Anyways, I still have trouble visualizing the execution of scripts in my head, especially when there are more than one running and they all have different functions.

It's gotten more complicated recently as I've added Update, FixedUpdate, and Coroutine to my list of "When's this running"? Update and Fixed Update I get the gist.

As for Coroutine, I basically understand it's a type of function that gives me a more precise control of timing. It's the only way I know I can do "yield return new WaitForSeconds(i);"

My question is more about their execution order. For example if I call a coroutine from update(which runs once per frame) and that coroutine has a waitforseconds(10), then will pause execution of all script? Is there like one central clock running everything? Will Update not run again until the wait is up? What if I've got another script with an update function containing a different coroutine waiting as well and both run at the same time?

Maybe I'm being to vague. Hard to explain. I've read a couple of pieces of information online about coroutine, but nothing that really explains it in a way I can visualize it.

like image 758
Mason Dixon Ormous Avatar asked Dec 25 '22 20:12

Mason Dixon Ormous


1 Answers

The first thing you need to know is that functions that use the yield keyword and return an IEnumerable are turned into iterators. It's syntactic sugar for writing a class that implements IEnumerator.

They're usually used in conjunction with foreach loops:

IEnumerable<string> GetFruits()
{
    yield return "Apple";
    yield return "Pear";
}

foreach (string fruit in GetFruits())
    Console.WriteLine(fruit);

What happens here is that GetFruits returns a generator object that implements IEnumerator<string>. Its MoveNext method runs part of the original GetFruits code each time it is called. Each call executes code up to the next yield statement, and uses the 'return value' of that yield to set the Current property of the generator.

The foreach loop results in code that calls MoveNext and that stores Current into the loop variable, which makes iteration much more readable, something like the following:

IEnumerator<string> fruitsGenerator = GetFruits().GetEnumerator();
while (fruitsGenerator.MoveNext())
{
    string fruit = fruitsGenerator.Current;
    Console.WriteLine(fruit);
}

But you're not limited to using iterators in loops. You can store a reference to a generator and call its MoveNext method, say, once per second. Or whenever a user presses a button. Or you could use the Current value to determine when MoveNext should be called again.

And that's exactly what Unity is doing. A coroutine is essentially a generator object (with some extra information, such as how much time is left until it should be called again). When a coroutine yields a WaitForSeconds object, Unity updates the waiting-time of that coroutine and will not call that coroutine's MoveNext method again until the waiting time is over.

Each 'update cycle', Unity calls Update on your game-objects and MoveNext on your coroutines, unless a coroutine is still in a 'waiting' state. A coroutine that's waiting is simply being skipped - it doesn't block any other code.

like image 179
Pieter Witvoet Avatar answered Dec 27 '22 11:12

Pieter Witvoet