Coroutines ++

Coroutines are a great way of:

  • Making things happen step by step
  • Writing routines that need to happen over time
  • Writing routines that have to wait for another operation to complete

See some more usage examples there.

For example you could easily imagine using a coroutine to orchestrate a cut scene sequence – or simply wait for an enemy to animate dying, pause and then respawn.
Coroutines are a very powerful part of Unity, but they are often much misunderstood by newcomers to the subject. This tutorial will help you get a grip on the power and flexibility of coroutines and understand just how they work.

If you just want to delay an operation for a few seconds time you could consider just using Invoke. You can use coroutines of course, but for beginners Invoke is easier.

Threads and Coroutines

First of all lets just be totally clear about this, coroutines are not threads and coroutines are not asynchronous.
A thread is a process that runs asynchronously with the other threads in the program. On a multiprocessor machine, a thread can really be running its code at the same time as all of the other threads. This makes programming threads a complicated thing to get right, because one thread could be changing something at exactly the same time as another thread is reading it. This is even more complicated because the code you write is turned into assembly language by the compiler – which means another thread could actually change something in the middle of the game processing what appears to be a single line of your source code. Because of this you have to go through hoops to ensure that such circumstances don’t occur by either making sure that there is no shared memory or by locking other threads out of shared values while they are being read or changed.

So What is a Coroutine?

Since coroutine is not multi-thread it will be executing on the main thread of the game, so in fact it will be the only thing in the core game code running at that time.

You never need to worry about synchronization or locking values when you are programming a coroutine. You have total control right up until the point your code executes a yield.

So here is the definition of what a coroutine could be:

A coroutine is a function that is executed partially and, presuming suitable conditions are met, will be resumed at some point in the future until its work is done.
The start of a coroutine corresponds to the creation of an object of type coroutine. That object is tied to the MonoBehaviour component that hosted the call.

A bit more on that last line, You can start a coroutine from any object even non MonoBehaviour but it has to be called as a MonoBehaviour member. The lifetime of the Coroutine object is bound to the lifetime of the MonoBehaviour object, so if the latter gets destroyed during process, the coroutine object is also destroyed. We shall demonstrate that topic later on.

Unity processes coroutines every frame of the game for every object that has one or more running. The processing occurs after Update and before LateUpdate for most yield statements, but there are special cases:
Overview
When the coroutine is activated it will execute right up to the next yield statement and then it will pause until it is resumed. You can see where it will resume in the diagram above, based on what you yield.
Let’s look at a really simple coroutine:

private void Start()
{
    Debug.Log("Start method");
    StartCoroutine(TestCoroutine());
    Debug.Log("Start method ends");
}

private IEnumerator TestCoroutine()
{
      Debug.Log("TestCoroutine");
      while(true)
      {
           Debug.Log("Here");
           yield return null;
           Debug.Log("There");
      }
}
private void Update(){Debug.Log("Update");}

This coroutine has a loop that apparently runs forever, it logs as:
Start method
TestCoroutine
Here
Start method ends
Update
There
Here
Update
There
Here

We can see that the coroutine is run until a yield is found. At that point, the coroutine is placed on the coroutine stack of the MonoBehaviour object (the current class in our example), then back to the calling method which is the Start method and on with the program. Update is called and returns, then the program looks in the MonoBehaviour list of coroutine and finds our pending TestCoroutine and gets back where it left off, the yield statement. The same is processed over and over again until the end of the coroutine if the while condition is not met or the end of the MonoBehaviour object.

The code inside the loop is exactly like an Update function. It runs once every frame for this object, just after the script’s Update routine runs (if it has one).

When you call StartCoroutine(TestCoroutine()) the code executes immediately up to the first time it yields, it will then be resumed when Unity processes coroutines for this object. The call is actually short for this.StartCoroutine clearly indicating that the call is done from the MonoBehaviour component.

If you start a coroutine early in the processing of a game object, like creating one in Start, Update or OnCollisionEnter then that coroutine will immediately run up to the first yield, then it will resume during the same frame if you yield return null. This can sometimes lead to strange effects if you don’t account for it. Note that this isn’t a problem if you make Start or OnCollisionEnter a coroutine, only if you start a coroutine from within them.

If you expect the Start to wait for the end of the coroutine before it finishes, consider the following:

private IEnumerator Start()
{
    Debug.Log("Start method");
    yield return StartCoroutine(TestCoroutine());
    Debug.Log("Start method ends");
}

private IEnumerator TestCoroutine()
{
      Debug.Log("TestCoroutine");
      int index = 0;
      while(index < 2)
      {
           Debug.Log("Here");
           yield return null;
           Debug.Log("There");
           index++;
      }
}
private void Update(){Debug.Log("Update");}

Start method
TestCoroutine
Here
Update
There
Here
Update
There
Start method ends
Update
Update

The calls for the end of the Start method is postponed until the coroutine is done. This requires the yield return for the coroutine and the Start method to return IEnumerator. Despite the fact it is possible and legit, Unity tends to not recommend that practice (without giving any reasons so it might be internal).

To Infinity and Beyond

Now one more thing – the apparently infinite loop in our test coroutine turns out to be not so infinite!

That loop will never execute again if you make a call to stop the coroutines on the game object and it will not run again if the object is destroyed. It will also be paused if the script is disabled directly or by using SetActive(false) on the game object. This clearly shows the link between the MonoBehaviour and the coroutine. This has been a common mistake from users wondering why the coroutine would not run in some situation. Consider the following:

void OnCollisionEnter(Collision col)
{
    if(col.gameObject.CompareTag("Rocket"))
    {
        StartCoroutine(Explosion());
        Destroy(this.gameObject);
    }
}

You would expect to start the explosion coroutine but then you get rid of the calling object and nothing happens. The coroutine is not running anymore since the MonoBehaviour calling was destroyed and the coroutine object too.
One way is to disable the renderer:

void OnCollisionEnter(Collision col)
{
    if(col.gameObject.CompareTag("Rocket")){
        StartCoroutine(Explosion(OnDestroyCallaback));
        this.gameObject.GetComponent<Renderer>().enabled = false;
        this.gameObject.GetComponent<Collider>().enabled = false;
    } 
}
private void OnDestroyCallback(){
   Destroy(this.gameObject);
}
IEnumerator Explosion(Action action)
{
    while(condition){
         // do plenty of stuff
         yield return null;
    }
    if(action != null){ action(); }
}

The renderer and collider are off, the explosion is happening and when over, the callback is called to destroy the object.

Second solution, you find a MonoBehaviour that is meant to live long enough, most likely a dedicated manager:

void OnCollisionEnter(Collision col)
{
    if(col.gameObject.CompareTag("Rocket"))
    {
        Manager mb = FindObjectOfType<Manager>();        
        mb.StartCoroutine(Explosion());
        Destroy(this.gameObject)
    } 
}

Manager is a MonoBehaviour script that will take responsability for the coroutine.

I Yield Sir

So I made a statement in the last section that you might be questioning. I said:

Unity processes coroutines every frame of the game for every object that has one or more running.

You may be thinking “Oh no it doesn’t, what if you use something like yield return new WaitForSeconds(1), then it doesn’t process it for another 1 second!”

Well actually Unity does process that coroutine every frame, checking to see if the right amount of time has elapsed – it doesn’t process your code, but it does process the coroutine which is the wrapper it’s made around your script.

So we know that we can effectively pause our code by yielding some value, here’s what you can yield:

  • null – the coroutine executes the next time that it is eligible
  • WaitForEndOfFrame – the coroutine executes on the frame, after all of the rendering and GUI is complete
  • WaitForFixedUpdate – causes this coroutine to execute at the next physics step, after all physics is calculated
  • WaitForSeconds – causes the coroutine not to execute for a given game time period
  • WWW – waits for a web request to complete (resumes as if WaitForSeconds or null)
  • Another coroutine – in which case the new coroutine will run to completion before the yielder is resumed

You can also issue the command yield break; which immediately stops the coroutine.
Because of WaitForEndOfFrame coroutines can be used to get information from render textures when all cameras have completed rendering and the GUI has been displayed.
Using yield return new WaitForSeconds(x) will never resume if the Time.timeScale is set to 0.
Of course the great thing about all of this is that you can write code that needs to execute over a period of time, or wait for some external event to occur, and keep it all nicely together in a single function making your code far more readable than if you had to write multiple functions or lots of code to keep checking the state of things. That’s really the point of coroutines.

Calling and stopping

We saw one way how to call a coroutine, actually, there is another one, Unity lately added a new way that allows to stop more efficiently.

public Coroutine StartCoroutine(IEnumerator routine);
public Coroutine StartCoroutine(string methodName, object value = null);

public void StopCoroutine(string methodName);
public void StopCoroutine(IEnumerator routine);
public void StopAllCoroutines();

IEnumerator MyCoroutine(){yield return null;}
IEnumerator MyCoroutineOneParam(int i){yield return null;}
IEnumerator MyCoroutineManyParams(int i, int j){yield return null;}

The second example makes use of a string parameter and one object parameter. This was useful before Unity allowed to stop a coroutine with IEnumerator. The only way was to call with a string and stop with a string. This process only allows one parameter (or then a bunch of them wrapped in an object, painful). I would not recommend it anymore.

Instead you can call the method directly inside if you plan on just running the coroutine, or use a IEnumerator reference:

StartCoroutine(MyCoroutineOneParam(10));

IEnumerator coroutineObj = MyCoroutineManyParam(10,20);
StartCoroutine(coroutineObj);
StopCoroutine(coroutineObj);

Using the first case is not limited to one parameter, any call will do. Notice the missing () in the call of StartCoroutine. We are no longer using the method directly but a reference to it.

The second case is a little more tricky, a call for the method is made and it returns a IEnumerator object (more on this in the next article on coroutines). coroutineObj is a reference to that IEnumerator object, we pass it to StartCoroutine which actually starts the coroutine. With this solution, we are able to stop a single coroutine. Note that it is stopped not paused, it won’t start again from where it left off.

Summary

  1. Coroutines are a really good way of making a sequence of operations happen over time or when some external process is completed
  2. Coroutines are not threads and are not asynchronous
  3. Nothing else is running when your coroutine is executing
  4. Your coroutine will resume when the conditions of your yield statement are met
  5. Coroutines are inactive when the script is disabled or the object is destroyed
  6. yield return new WaitForSeconds is dependent on game time which is affected by Time.timeScale

Practical Uses of Coroutines

First thing out of your mind, you should not think of using coroutines instead of Update, it is simply not their purpose despite doing a similar job, you won’t save resources nor memory. Keep them for temporary work, Update will do anything that should be done continuously.

Hopefully we’ve established what coroutines are and when they run. Our advanced tutorial will examine the technology behind them.

Let’s use coroutines to do something. A couple of simple helper functions can really help us create easy cut sequences using a coroutine.

We can write a coroutine that moves an object to a target location and rotation. We can write a coroutine that waits for an animation to be a certain percentage complete. Then using those two tools we could easily script out a whole cut sequence in a single function where it will be easy to read!

The thing to watch for when moving things in a coroutine is to ensure that there isn’t some fight breaking out between different coroutines or Update functions trying to manipulate the position of the object at the same time.Ensure that you only have one coroutine affecting an object at one time and disable any Update functions that would move the object.

Here’s an example of a coroutine to wait for an animation to be partially complete:

//Wait for an animation to be a certain amount complete
IEnumerator WaitForAnimation(string name, float ratio, bool play)
{
    //Get the animation state for the named animation
    var anim = animation[name];
    //Play the animation
    if(play == true)
    {
        animation.Play(name);
    }
    //Loop until the normalized time reports a value  
    //greater than our ratio.  This method of waiting for
    //an animation accounts for the speed fluctuating as the
    //animation is played.
    while(anim.normalizedTime + float.Epsilon + Time.deltaTime < ratio)
    {
        yield return new WaitForEndOfFrame();
    }
}

Nonetheless, you would get much better and more accurate result using the animation event system.

You could write a coroutine to wait for an animation like this:

IEnumerator Die()
{
       //Wait for the die animation to be 50% complete
       yield return StartCoroutine(WaitForAnimation("die",0.5f, true));
       //Drop the enemies on dying pickup
       DropPickupItem();
       //Wait for the animation to complete
       yield return StartCoroutine(WaitForAnimation("die",1f, false));
       Destroy(this.gameObject);

}

Another example waiting for the end of a downloading to remove a “Loading…” UI.

private IEnumerator Start()
{
     DisplayUIMessage("Loading...");
     yield return StartCoroutine(DownloadItem(url, StartLevel));
}
private void StartLevel(){
    // Set UI Off
    // start level
}
private IEnumerator DownloadItem(string url, Action callback)
{
     if(string.IsNullOrEmpty(url) == true){yield break;} // wrong url, stop the coroutine
     WWW www = new WWW(url);  // Start the downloading
     yield return www;   // Wait for the end of the downloading
     if(www.error != null){   // Something went wrong
          // Handle error
          yield break;
     }
     // Take care of the www object
     if(callback != null){
         callback ();
     }
}

Issue with coroutines

One major issue is that you cannot pass ref parameter. Consider you have a value that you wish to alter within the coroutine and then use it outside. For instance, you have a method that download an assets from the server and assign to a byte array. Later you will manipulate that array.

public static IEnumerator LoadAsset(string url, System.Action<byte[]> result)
{
    if(string.IsNullOrEmpty(url)) { yield break; }
    string path = AppController.ServerURL + url;
    byte[]bytes = null;
    WWW www = new WWW(path);
    yield return www;
    if(string.IsNullOrEmpty(www.error) == false)
    {
        Debug.Log (url + " " + www.error);
        result(null);
        yield break;
    }
    result(www.bytes);
}

// Call for that method:
byte[] myArray = null;
StartCoroutine(LoadAssets(url, result => myArray = result));

We cannot just pass the array since assigning it inside the coroutine would result in not affecting the outside version (think that the parameter would just be a copy of the original reference). Using ref is not allowed on coroutine. So we use Action.

A bit more complex but working just fine. We can even pass multi values no matter what.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s