Tutorial: Coroutines (pt. 1) – Waiting for Input.

If you’re new to Unity, you may have heard some phrases being tossed around like “coroutine” and “yield”.

What is a coroutine you ask? Wait a minute, I can tell a method to pause for a frame and then continue doing whatever it was it was doing? Huh? I can specify an amount of time (in seconds) that I can pause the function for, too?

I know there are some people out there probably calling b/s but I’m being serious here. Yes, you can do all of the aforementioned with a coroutine.

When I first started I will admit I didn’t know what a coroutine was and even now I have come to learn that I have only seen the tip of that proverbial iceberg. But from what I do know – I am going to share what I have found out in some basic, but somewhat practical examples (both in C# and Unity’s Javascript dialect) and as time goes by I’ll make them more and more “real world” examples in the following versions of this tutorial.

Let’s get started with the super basic stuff, shall we? I spent some time looking through Unity’s docs on coroutines and for the beginner I was left a little boggled until I started playing around with the code they provided.

There is a very powerful statement – yield. The YieldInstruction can be paired with other coroutines such as WaitForSeconds (waits for [float] seconds before continuing) and WaitForFixedUpdate (waits for the next physics step) which are already in the Unity API. In short, consider yield like a game of Red Light, Green Light that you might be familiar with. The default statement yield will tell the coroutine function “Red Light!” and it will immediately stop what it is doing until the next frame where Unity will then say “Green Light!” and the function will resume what it is doing.

function RedLightGreenLight()
{
print("Green Light!");
print("...");
print("...");
print("Red Light!");
yield WaitForSeconds(2.0);
//2 seconds will pass here
print("Green Light!");
}

If you run the following code, you’ll see 4 print statements appear in the console immediately, and then 2 seconds later we’ll see the fifth statement print to the console. I used yield paired with WaitForSeconds, because though yield by itself would work, it would occur too fast for anyone to see and might confuse.

Now, that may be all well and good, but I don’t know many instances where that would be very useful. Wheres that practical example you were promised? Well we’ll get to that now.

In an empty scene you will want to Cmd+Shift+N to create a new GameObject. I call it “Level Manager”. Create a new Script (in either C# or Javascript, I will cover both languages) and call it “LevelManager” and then drag it from your Project pane and attach it to your Level Manager.

Your scene should look something like this:

Scene Heirarchy and Inspector

Scene Heirarchy and Inspector

On a side note, I use a Level Manager in every project I have with Unity. I use it as a general starting point so that nothing executes until Level Manager says it is alright to. I do this because when you have several objects and scripts extending MonoBehaviour, they can all begin executing code immediately in either or both of 2 methods (Start and Awake). If you do something like this:

//C# Code
// Use this for initialization
void Awake ()
{
//This is where it all begins.
print("waking up...");
}

void Start ()
{
print("starting...");
}

When you run the code you will see:

Awake() is called *before* Start()

So what I do is for anything that needs initiating values, I do that in Awake, and then my Level Manager begins the level in its Start method.

Okay, sorry for the little detour, in any case we should have our scene completely set up for this short example. Now I’m going to demonstrate using the same coroutine to work in parallel with other processes and to hold up everything until it finishes. I’ll be straight, I don’t use Unity Javascript because I don’t like the way it handles certain aspects of programming, so when I write for Unity I always use C#. But I know that a lot of beginners, especially those who are users of Flash and Actionscript can be quite intimmidated by C#’s complexity. (I was at first, too, when I started learning just a few months ago.) So I will show C# examples first, then JS.

This first class is going to show you how to run code in parallel as it is the most simplistic way to run a coroutine in C#:

using UnityEngine;
using System.Collections;
public class LevelManager : MonoBehaviour
{
private bool _keyPressed = false;
// Use this for initialization
void Awake ()
{
//This is where it all begins.
print("waking up...");
}
void Start ()
{
print("starting...");
FirstFunction();
}
void FirstFunction()
{
//by default in our settings this is the space bar, so we will use this just to make it simple.
StartCoroutine(WaitForKeyPress("Jump"));
//to demonstrate that coroutines can work in parallel, we will make it do something right after the coroutine is told to begin.
print("Coroutine did begin.");
}
/*
A little knowledge here for people who are saying "wtf is an IEnumerator?"
If you check this msdn docs on this interface you will see in this case it is used to read through enumeration data. This line is in the ref docs: "Enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection." So basically every frame we're stepping through another index of an enumeration. We don't really need to know more, how or why in this case because Unity is going to handle all of this for us. All we need to know is how to set it up...
*/

public IEnumerator WaitForKeyPress(string _button)
{
while(!_keyPressed)
{
if(Input.GetButtonDown (_button))
{
StartGame();
break;
}
print("Awaiting key input.");
yield return 0;
}
}
private void StartGame()
{
_keyPressed = true;
print("Starting the game officially!");
}
}

When you run this, your application will be continuously iterating through this coroutine’s while statement until the condition is met. The condition is when the use hits a button, in this case the “Jump” button which is the Space Bar by default in Unity’s Input settings. So when you test the Scene and hit Space, you should see the following in your console:

console readout

Notice that the print statement is running along side the iteration.

Here you can clearly see that the line directly after you begin the coroutine is executed along side of the iteration. It’s kind of like opening up a function that works like Update() for a specified amount of time, then stops being called. Cool, huh? You bet it is.

Now, onto a more procedural example:

using UnityEngine;
using System.Collections;
public class LevelManager: MonoBehaviour {
private bool _keyPressed = false;
void Awake () {
//This is where it all begins.
print("waking up...");
}
// Use this for initialization
void Start () {
print("starting...");
//in order to lock a coroutine down and keep it from running in parallel, we must wrap a coroutine inside of the first one.
StartCoroutine(FirstFunction());
}
public IEnumerator FirstFunction()
{
//by default in our settings this is the space bar, so we will use this just to make it simple.
yield return StartCoroutine(WaitForKeyPress("Jump"));
//this time the coroutine won't fire this print statement off until it completes
print("Coroutine did finish.");
}
public IEnumerator WaitForKeyPress(string _button)
{
while(!_keyPressed)
{
if(Input.GetButtonDown(_button))
{
StartGame();
break;
}
print("Awaiting key input.");
yield return 0;
}
}
private void StartGame()
{
_keyPressed = true;
print("Starting the game officially!");
}
}

This works the same way as the previous example, just run and hit the Space bar.

Notice that this time that print statement didn't fire until after the coroutine finished.

Notice that this time that print statement didn't fire until after the coroutine finished.

What you’re seeing here is the coroutine making everything come to a hault until it finishes. This might be the more practical way of waiting for key input, but that all depends on the context.

So those were our C# examples of how you might use a coroutine to run until a specific condition is met, in this case a desired key was pressed.

Now for the JS people, you will see that there are some obvious changes and simplifications to the syntax of a coroutine. It is much easier!

Parallel example:

private var _keyPressed : boolean = false;
function Awake()
{
print("waking up...");
}
function Start()
{
print("starting...");
//you will see that this is very similar to the C# version, but in Unity's version of Javascript we don't have to call StartCoroutine.
WaitForKeyInput("Jump");
//this is the parallel version, it will run this print statement in parallel with it's iteration step.
print("started coroutine");
}
//notice here we don't even have to declare an IEnumerator return type. Nifty.
function WaitForKeyInput(_button : String)
{
while(!_keyPressed)
{
if(Input.GetButtonDown(_button))
{
StartGame();
break;
}
print("awaiting key input");
yield;
}
}
function StartGame () {
_keyPressed = true;
print("game officially started.");
}

When you run this, you will see the same result as our C# parallel example, the code below the inital call to the coroutine is called along side of the iterations. The obvious differences between the languages are that you don’t have to use StartCoroutine or declare an IEnumeration return type on our coroutine method. Much easier, wouldn’t you say?

For the more procedural example:

private var _keyPressed : boolean = false;
function Awake()
{
print("waking up...");
}
//notice that though this is the built in Start method, we can use yield here.
function Start()
{
print("starting...");
//you will see that this is very similar to the C# version, but in Unity's version of Javascript we don't have to call StartCoroutine.
yield WaitForKeyInput("Jump");
//this is the procedural example, the print statement wont be made until after the coroutine completes
print("finished coroutine");
}
//notice here we don't even have to declare an IEnumerator return type. Nifty.
function WaitForKeyInput(_button : String)
{
while(!_keyPressed)
{
if(Input.GetButtonDown(_button))
{
StartGame();
break;
}
print("awaiting key input");
yield;
}
}
function StartGame () {
_keyPressed = true;
print("game officially started.");
}

When you run this, you will see the same results as the 2nd C# example. The same differences between C# and JS apply that were in the last example. If you notice, though, you will see I called yield from inside Start on this example. In C# you can make Start return an IEnumerator to make it a coroutine aswell if you felt so inclined. Personally I would never do that, but you *can*.

As an FYI, you can call yield anywhere except for Update and FixedUpdate (or anything similar to those) but you can start a coroutine from those methods.

So there you have a basic, yet practical example of a coroutine in 2 languages. Now just for kicks, I’ll show you the less efficient way to handle this, on a native loop within Unity.

using UnityEngine;
using System.Collections;
public class StupidLoop : MonoBehaviour {
private bool _keyPressed = false;
// Update is called once per frame
void Update ()
{
if(!_keyPressed)
{
if(Input.GetButtonDown("Jump"))
{
StartGame();
}
}
print("looping...");
}
void StartGame()
{
_keyPressed = true;
print("Game Started");
}
}

I know what you’re saying. You’re saying “Well still, that isn’t too bad, in fact that is less than calling up all these coroutines…” Well sure, it looks innocent right NOW. But there are some massive flaws and that will get this code to look ugly REALLY fast. Let’s think about what is wrong with this:

  • Since Update automatically fires once every frame, for as long as this object is a. enabled or b. exists, we have no control what-so-ever as to when we start looking for that key press unless we write some crazy if statement that checks every frame whether or not it even needs to look. Already our conditional statement is looking a bit convoluted.
  • In order to keep it simple we’d basically have to leave this component exactly how it is now and not add any code. If we do this a lot we will have a load more objects in memory than we need. That’s just not good programming practice.
  • Sure we could have another component controlling whether or not it should be looking for the key press by setting that boolean to be false again after we set it to true, and then enabling/disabling it when we need to, but why? More work in the long run.

To reiterate – coroutines are wonderful, use them. They only run when we want them to, and then they get out of our way. They should definitely be one of your go-to methods of getting things done in Unity. Hopefully now you have a basic understanding of how to use them, when you should use them and why they are so darn nifty.

In the next part of this tutorial I am going to get down and dirty with combining delegates with coroutines across multiple objects and building an in game transition, but until then I hope this helped gain you a better understanding of coroutines.

Cheers!

==

Leave a Reply

Post Navigation