Go to Top

The down and dirty on coroutines. When they run and why they sometimes don't! Also learn about how to make cut sequences and long running operations using this powerful technology

Motivation

You should read this tutorial if you are not too sure about coroutines and maybe have had a problem getting them to work the way that you want.

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

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.  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 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.

Threads will be the subject of a subsequent article.

So What is a Coroutine?

Ok so we've established that a coroutine is definitely not a thread.  This means that only one coroutine will be executing at a time and 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 synchronisation 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 is:

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.

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:

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:

IEnumerator TestCoroutine()
{
      while(true)
      {
           Debug.Log(Time.time);
           yield return null;
      }
}

This coroutine has a loop that apparently runs forever, it logs the current time and then it yields.  When it is resumed, it goes back around the loop, logs the time again and yields once more.

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.

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.  

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 SetActiveRecursively(false) on the game object.

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 its 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.

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

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) 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();

	}

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(gameObject);

}

The video will show you a lot more and the example project for it is available here.

Video

You should really watch this video if you want to know how to make very powerful coroutine tools, apply easing functions and take your coroutine exploits to the next level.

This video is quite long, but it has some really cool stuff in it!   Learn how to build a whole cut sequence using coroutines then see how to apply easing functions to yield statements.

By the end of the video you will know how to build advanced coroutine tools and use them to create great effects.  The download for the project has all of the stuff shown in the video.

, , , , ,

13 Responses to "Coroutines++"

  • morton
    November 25, 2012 - 3:18 am Reply

    very useful, thank you.

    in the video you use ToArray() in the foreach statement. could you please explain why?
    would the expression (ball != null) otherwise cause problems?

    • whydoidoit
      November 25, 2012 - 5:42 am Reply

      So firstly the foreach loop is running over quite a period of time – in the previous section of the video I make it so that the balls are destroyed when they fall off the edge, and I remove them from allBalls when that happens.

      Now a foreach loop does not like the collection it is iterating over to change, so as we are yielding in it, it is very likely it WILL change due to something being destroyed and removed from the list. To fix that I make a copy of the collection by using .ToArray() – so that just makes a copy of the list when the foreach loop starts, nothing will be removed from that copy. Now that we have a copy the loop is happy – but Unity will be setting the contents of it to null when they are destroyed (its one of those weird useful/unuseful features that are not well documented) – so if the ball has been deleted it will start returning null.

      Now here’s a couple of points:

      1. When, later in the video, I set timeScale = 0 actually no balls would have been deleted (because they aren’t moving)! I decided to leave it in as belt and braces.

      2. We could have taken a single copy of the array and continually used that – but when timeScale was running we might have had a newly spawned ball that wasn’t affected by the later code – again when timeScale became 0 that would not have happened.

      I hope that clears it up? Let me know :)

      • morton
        November 25, 2012 - 9:28 am Reply

        yes, all clear. thanks a lot.

  • moonlite
    November 27, 2012 - 11:41 pm Reply

    What do it means by “Nothing else is running when your Coroutine is executing”? As in all other function will not run when Corountine is executing??

    • whydoidoit
      November 28, 2012 - 12:39 am Reply

      I mean that no other code is running at the same time as your coroutine is in operation. Other code will run when you yield.

  • Pravin
    January 31, 2013 - 11:40 am Reply

    Wonderful and detailed explanation along with a practical example. You have covered most of the nitty-gritty of co-routines. Thx.

  • Chris
    February 15, 2013 - 11:16 am Reply

    Is it possible to make a co routine wait until a button is pressed before continuing? I’m attempting to do it at the moment, but with no success. This is what I’ve tried;

    using UnityEngine;
    using System.Collections;

    public class example : MonoBehaviour {
    public bool check = false;

    void Start(){
    StartCoroutine(Do());
    }

    IEnumerator Do() {
    SomethingElse();
    yield return StartCoroutine(Something());
    SomethingElse();
    }

    IEnumerator Something(){
    if (Input.GetButton(“Jump”)){
    check = true;
    }
    while(check == false){
    yield return new WaitForEndOfFrame();
    }
    }

    public void SomethingElse(){
    print(“Run”);
    }

    }

  • fafase
    February 15, 2013 - 8:01 pm Reply

    Hei Chris,

    you can simplify the Something function to this:

    IEnumerator Something(){
    while(!Input.GetKeyDown(KeyCode.Space)){
    yield return null;
    }
    }

    I guess it will do what you are expecting. You should get a first print of Run then a second on pressing Space.
    The idea is that as long as the Input returns false (notice the ! in front of Input) you will yield and get back to it. Once you press, the Input returns true and you jump over the yield and finish the function that returns to the calling function and all is done.

    Fafase

    • Chris
      February 16, 2013 - 8:22 am Reply

      thanks fafase, it worked like a charm :)

  • Kris Kirov
    February 15, 2013 - 8:21 pm Reply

    Kind Sir.
    This tutorial was most welcome !Thank you

  • Karsnen
    May 2, 2013 - 1:04 am Reply

    Amazing tutorials. Overall UnityGems.

    Thanks Mike.

  • Karsnen
    May 11, 2013 - 11:00 pm Reply

    Hello Mike,

    Thank you for your wonderful tutorials. I am extensively using it for my mobile development.
    Being a sole developer, this is just a concern. I am pretty sure there is nothing serious but I wanted to hear it from you.

    I use the easing function library extensively and most of the routines look like this.

    IEnumerator MoveObject(Vector3 Target, float Timer, EasingType ease = EasingType.Sine, Func EasingFunction = null)

    and I call them like this

    StartCoroutine(MoveObject(new Vector3(6,0,0), 10f, EasingType.Sine));

    So is there any worry I should be having as my target is mobile, you know like garbage like and what not. Right now I am replacing updates with routines.

    Thank you.

Leave a Reply