Motivation
You should read this article if you really want to know what is going on behind the scenes in coroutines. It will be very useful if you are following the FSM tutorial and you want to get the grimy details on how we can create interruptable coroutines that can be later resumed.
This is an advanced subject and you don't really need to know this if you just want to use coroutines. If you are building systems and frameworks, or you are just curious then read on!
Coroutines
So a coroutine is basically an interruptable routine that can later be resumed. We tell the system when we want to be interrupted by entering a yield statement and we tell the system when we want to be restarted by returning an object derived from YieldInstruction or a reference to another coroutine.
It's obvious that yield is a part of the C# language - so what is it really for? I can tell you it wasn't originally designed to let you wait 2 seconds before destroying your monster's Game Object.
Code Based Enumerators
A while back the designers of the CLR decided that it would be a really good idea if you could enumerate over things that were created by code. So they built it into the framework. They figured that if you wanted to calculate the Fibonacci sequence then it would be nice if you could do a foreach over it. It's also very useful for other things where an enumeration would be useful and it's not practical or necessary to actually create an array or a list from it (or bother to write your own implementation of the IEnumerator interface).
For example say you wanted to do a foreach over every line in a file - well that's what yield was originally all about.
using System.IO;
...
void ProcessFile(string filename)
{
//Loop through all of the lines in a file
foreach(var line in Lines(filename))
{
Debug.Log(line);
}
}
IEnumerator Lines(string filename)
{
var reader = new StreamReader(filename);
while(!reader.EndOfStream)
{
yield return reader.ReadLine();
}
reader.Close();
}
IEnumerator & Some Magic
Basically yield returns the current value for a code based enumerator. If you look at the IEnumerator interface you will see it does two things:
- MoveNext() which returns true if there are more items and false otherwise.
- Current which returns the current value of the enumerator at the current step. You must always call MoveNext() before your first call to Current.
When a routine contains a yield then the compiler starts to do some magic. That coroutine looks like a normal function in your class doesn't it? Well it isn't that at all. Behind the curtain the compiler has constructed a dummy class which implements IEnumerator, this class has its own fields, one field for every local variable you have defined in your function and another to reference the instruction that last executed.
When that magic class's IEnumerator.MoveNext() is called your code starts to run and the compiler automagically gives you the this pointer for your classes instance, rather than the one for the magic class - it also works out how to reference the variables in your function. In other words you've built a state machine disguised as a function.
Enumerators as Coroutines
So what Unity is doing is calling MoveNext on your coroutine function and working out what you yielded by inspecting the Current property of the IEnumerator interface. It then performs its waiting behaviour based on what that value is - so if it's WaitForSeconds(2) then it checks and doesn't call MoveNext again until that time has elapsed etc.
Hijack!
Ok so now we understand coroutines it's kind of easy to hijack them. All we have to do is get the IEnumerator interface and call it ourselves. We could swap IEnumerators in an out and effectively get coroutines to pause and resume or switch states as we saw fit.
Here's an example where we have one coroutine that increments a variable by an increasing amount over time and another one that decrements the variable and uses a different type of yield. Pressing a GUI button switches between them without ever stopping the function that was switched out:
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(GUIText))]
public class Hijack : MonoBehaviour {
//This will hold the counting up coroutine
IEnumerator _countUp;
//This will hold the counting down coroutine
IEnumerator _countDown;
//This is the coroutine we are currently
//hijacking
IEnumerator _current;
//A value that will be updated by the coroutine
//that is currently running
int value = 0;
void Start()
{
//Create our count up coroutine
_countUp = CountUp();
//Create our count down coroutine
_countDown = CountDown();
//Start our own coroutine for the hijack
StartCoroutine(DoHijack());
}
void Update()
{
//Show the current value on the screen
guiText.text = value.ToString();
}
void OnGUI()
{
//Switch between the different functions
if(GUILayout.Button("Switch functions"))
{
if(_current == _countUp)
_current = _countDown;
else
_current = _countUp;
}
}
IEnumerator DoHijack()
{
while(true)
{
//Check if we have a current coroutine and MoveNext on it if we do
if(_current != null && _current.MoveNext())
{
//Return whatever the coroutine yielded, so we will yield the
//same thing
yield return _current.Current;
}
else
//Otherwise wait for the next frame
yield return null;
}
}
IEnumerator CountUp()
{
//We have a local increment so the routines
//get independently faster depending on how
//long they have been active
float increment = 0;
while(true)
{
//Exit if the Q button is pressed
if(Input.GetKey(KeyCode.Q))
break;
increment+=Time.deltaTime;
value += Mathf.RoundToInt(increment);
yield return null;
}
}
IEnumerator CountDown()
{
float increment = 0f;
while(true)
{
if(Input.GetKey(KeyCode.Q))
break;
increment+=Time.deltaTime;
value -= Mathf.RoundToInt(increment);
//This coroutine returns a yield instruction
yield return new WaitForSeconds(0.1f);
}
}
}
We have created a hijack that basically has Unity start our DoHijack coroutine that delegates to which ever routine is currently active. When you click the GUI button to switch between them, notice how the value of increment remains the last value when the coroutine is re-enabled - the coroutine isn't restarting, its just continuing.
There are lots of system level things you can do by hijacking coroutines - the FSM tutorial will certainly be covering some of them!
Project
You can find the sample project for this here.

November 28, 2012 - 9:57 pm
Question about stepping through DoHijack() with the monodevelop debugger:
50 IEnumerator DoHijack()
51 {
52 while(true)
53 {
54 //Check if we have a current coroutine and MoveNext on it if we do
55 if(_current != null && _current.MoveNext())
56 {
57 //Return whatever the coroutine yielded, so we will yield the
58 //same thing
59 yield return _current.Current;
60 }
61 else
62 //Otherwise wait for the next frame
63 yield return null;
64 }
65 }
When I break on line 59 and then step over (f10), the current instruction caret moves to line 63 even though it doesn’t appear to be executing that line (ie if I set a breakpoint on line 63 it wont’ get hit). What is going on here?
November 28, 2012 - 10:15 pm
That happens when yielding in a coroutine – the compiler has generated code that MonoDevelop cannot break on – it does cause the debugger to appear to step to exit code – don’t worry about it, it’s just the magic class doing its return.