You’ve probably used coroutines in Unity if you happened by this post. If not, go ahead and try them out; I’ll wait here. In this post we’ll talk about how they work, and then we’ll extend them to be even more useful. In C#, the yield keyword and IEnumerator can be used together to execute and create functions that are evaluated lazily. This is done by storing the state of the program’s stack frame between calls to the function (It’s actually a bit more complicated then that. See reddit reply by tracekill here). Read more about the yield keyword and IEnumerator here. In this example we generate the Fibonacci sequence in a lazy way: If you like you can use print statements to confirm that this is evaluated lazily. So now that you can see how IEnumerator and yield are used together, you can start to see how they could be used to model execution over multiple frames. So how does Unity make the coroutine 'magic' happen? When you make a call to StartCoroutine(IEnumerator) you are handing the resulting IEnumerator to the underlying unity engine. StartCoroutine() builds a Coroutine object, runs the first step of the IEnumerator and gets the first yielded value. That will be one of a few things, either "break", some YieldInstruction like "Coroutine", "WaitForSeconds", "WaitForEndOfFrame", "WWW", or something else unity doesn't know about. The Coroutine is stored somewhere for the engine to look at later. As you probably know, game logic is executed in "frame" steps, for example, Update will be called on MonoBehaviours with an Update function once per frame. At various points in the frame, Unity goes through the stored Coroutines and checks the Current value in their IEnumerators. MoveNext returns false if the last thing yielded was "break" of the end of the function that returned the IEnumerator was reach. If this is the case, unity removes the IEnumerator from the coroutines list. One common misconception cleared up: Coroutines do not execute in parallel to your code. They run in the same thread as everything else in your scripts, so editing the same values in Coroutines and Update is safe. There is one more interesting thing here: StartCoroutine returns a YieldInstruction subclass called "Coroutine". Your coroutine can yield one of these in order to wait for another coroutine to finish before resuming execution. The way the engine handles these ones is a bit special. The IEnumerator that yielded to the Coroutine is stored in the Coroutine object and when the Coroutine object's original IEnumerator has finished as discussed above, this IEnumerator is run and then added to the list. There is no practical way to get a return value out of a coroutine at the moment or deal with errors that occur in coroutines you are waiting on. You would currently need to create a class that hold the type of value you like, pass it into the IEnumerator and have the coroutine function edit it. Lets make that a little nicer and support error handling. Lets start off by seeing how we could wrap a coroutine and peek at each of the values as it runs. Cool, but not too useful. Lets say we check each object to see if it's a specific type we care about. If it is, we end the routine and store the type somewhere. Lets wrap it up in an object so that we can ask for the return value. And then we make an extension method to MonoBehaviour so that we can start them naturally. Voila: use as follows: Currently there is no nice way to deal with errors in coroutines; they just get spit out to the console. Lets catch errors in our internal routine and store them in the Coroutine<T> object. When someone tries to access the result, we'll throw an exception so they could handle it inline. Well thats all for now. Get the Full Script here. Follow me on twitter @horsman or Twisted Oak Studios at @TwistedOakGames
Coroutines – More than you want to know
The Yield keyword and IEnumerator
// This function represents a generator of the fibonacci sequence
IEnumerator Fibonacci(){
int Fkm2 = 1;
int Fkm1 = 1;
yield return 1; // The first two values are 1
yield return 1;
// Now, each time we continue execution, generate the next entry.
while(Fkm1 + Fkm2 < int.MaxValue){
int Fk = Fkm2 + Fkm1;
Fkm2 = Fkm1;
Fkm1 = Fk;
yield return Fk;
}
}
void Start () {
// Call the Fibonacci function, which
// immediately returns an IEnumerator
// (No code in Fibonnacci is run)
IEnumerator fib = Fibonacci();
// Generate the first 10 Fibonacci numbers
for(int i = 0; i < 10; i++){
// MoveNext runs the Fibonacci function
// with the stored stack frame in the Ienumerator
if(!fib.MoveNext())
break;
// Current returns the value that was yielded
Debug.Log((int)fib.Current);
}
}
Coroutines in Unity
Disclaimer: this is just a good guess that would work, perhaps not the exact way the UnityEngine does it
The Coroutine YieldInstruction
Extending Coroutines: Return Values and Error Handling
public IEnumerator InternalRoutine(IEnumerator coroutine){
while(true){
if(!coroutine.MoveNext()){
yield break;
}
object yielded = coroutine.Current;
Debug.Log(yielded);
yield return coroutine.Current;
}
}
...
// Use like:
StartCoroutine(InternalRoutine(SomeOtherRoutine()));
public IEnumerator InternalRoutine(IEnumerator coroutine){
while(true){
if(!coroutine.MoveNext()){
yield break;
}
object yielded = coroutine.Current;
if(yielded != null && yielded.GetType() == typeof(TypeICareAbout)){
returnVal = (TypeICareAbout)yielded;
yield break;
}
else{
yield return coroutine.Current;
}
}
}
public class Coroutine<T>{
private T returnVal;
public Coroutine coroutine;
public IEnumerator InternalRoutine(IEnumerator coroutine){
...
}
}
public static class MonoBehaviorExt{
public static Coroutine<T> StartCoroutine<T>(this MonoBehaviour obj, IEnumerator coroutine){
Coroutine<T> coroutineObject = new Coroutine<T>();
coroutineObject.coroutine = obj.StartCoroutine(coroutineObject.InternalRoutine(coroutine));
return coroutineObject;
}
}
IEnumerator Start () {
var routine = StartCoroutine<int>(TestNewRoutine()); //Start our new routine
yield return routine.coroutine; // wait as we normally can
Debug.Log(routine.returnVal); // print the result now that it is finished.
}
IEnumerator TestNewRoutine(){
yield return null;
yield return new WaitForSeconds(2f);
yield return 10;
}
public class Coroutine<T>{
public T Value {
get{
if(e != null){
throw e;
}
return returnVal;
}
}
private T returnVal;
private Exception e;
public Coroutine coroutine;
public IEnumerator InternalRoutine(IEnumerator coroutine){
while(true){
try{
if(!coroutine.MoveNext()){
yield break;
}
}
catch(Exception e){
this.e = e;
yield break;
}
object yielded = coroutine.Current;
if(yielded != null && yielded.GetType() == typeof(T)){
returnVal = (T)yielded;
yield break;
}
else{
yield return coroutine.Current;
}
}
}
}
...
IEnumerator Start () {
var routine = StartCoroutine<int>(TestNewRoutineGivesException());
yield return routine.coroutine;
try{
Debug.Log(routine.Value);
}
catch(Exception e){
Debug.Log(e.Message);
// do something
Debug.Break();
}
}
IEnumerator TestNewRoutineGivesException(){
yield return null;
yield return new WaitForSeconds(2f);
throw new Exception("Bad thing!");
}
4 Responses to “Coroutines – More than you want to know”
Twisted Oak Studios offers consulting and development on high-tech interactive projects. Check out our portfolio, or Give us a shout if you have anything you think some really rad engineers should help you with.
Archive
script is not working at all: see the error here:
http://cl.ly/image/1h0f1r3U2r1G
Thanks a bunch for this!
I wasn’t much amused seeing that one cannot use the coroutine reference returned by the Monobehaviour.StartCoroutine() method to call a Cancel on it or something. I don’t get why this is not implemented by default. Anyways this saved me from rethinking all my coroutine processes, so thanks again 
“The way the engine handles these ones is a bit special. The IEnumerator that yielded to the Coroutine is stored in the Coroutine object and when the Coroutine object’s original IEnumerator has finished as discussed above, this IEnumerator is run and then added to the list.”
Can you please explain this?
Thanks
The link to the Unity documentation is broken, the new link is: http://docs.unity3d.com/412/Documentation/ScriptReference/index.Coroutines_26_Yield.html