Go to Top

Wondering how coroutines are actually made to work? What is happening behind the scenes of those yield instructions? Read on...

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!

If you want to know when coroutines are called and how to use them to create powerful sequences then you should read the Coroutines article, that's really got some useful tools in it too. This article is a lot of geeky detail!

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.

, , ,

6 Responses to "Advanced Coroutines (inner workings)"

  • mick
    November 28, 2012 - 9:57 pm Reply

    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?

  • whydoidoit
    November 28, 2012 - 10:15 pm Reply

    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.

  • Coroutines in Unity | ttlaoc
    October 3, 2013 - 12:38 pm Reply

    […] good post about this topic explains the internal working of the coroutines and yield (what’s doing the […]

  • sajjad
    October 9, 2013 - 3:09 pm Reply

    Good Post. When I Press The Button For First Time Execution Is Right And “CountUp” Method Running But When I Press Button Second Time There Is a 5 Second Delay To Switch Between Method And “CountDown” Execution.it’s Mean That After 5 Second The Counter Decrease.Interested Thing is That After First And Second Pressing Of Button There Is No Delay Between Method Switching.Only For The Second Time.
    I Tested This For Many Times With Same Results .Can You Explain This Behavior?Thanks.Sorry For My English

  • Titi
    February 16, 2014 - 3:17 pm Reply

    Great post ! One question, why do we need to check if the enumerator is the same after calling MoveNext() on it ?
    Thank you.

Leave a Reply