Coroutines in Unity — All you need to know about them

Augusto Micheli Debard
4 min readJul 21, 2022

You may have already stumbled upon Coroutines in Unity, typically used to execute some code after a given amount of time, detaching from Unity’s main Update() loop.

The goal of this guide is to get to know Coroutines better, because we need to take a bit of extra care when using them, as they can become hard to debug, and even cause performance issues if we don't really know what we are doing.

But also Coroutines lets us do things that otherwise would look really ugly if we keep ourselves stuck in the Update() loop.

When to use Coroutines?

The general rule is:

  • When we don't want to execute some piece of code instantly in the current frame.

Also, Coroutines are needed for generic asynchronous operations in Unity such as HTTP transfers, asset loads, file I/O, and such.

Why is that?

Because Coroutines lets us stop their execution at any given point, return the control during the current frame, and continue from the same point in the next frame (also possible after some time/condition).

Example

A Coroutine is just a special method with the ability to yield returnsome type of YieldInstruction.

To declare a Coroutine, you need to create a method that has IEnumerator as its return value, and at least a yield return statement:

IEnumerator MyCoroutine() 
{
Debug.Log("Hi");
yield return null;
Debug.Log("Next frame!");
}

yield return null indicates Unity to just stop execution there and continue in the next frame.
To start the above coroutine, you need to call:
StartCoroutine(IEnumerator routine), and pass the coroutine as an argument.

For example, we can start the coroutine in the Start() method:

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

A really common type of YieldInstruction is
WaitForSeconds(float seconds), that just indicates Unity to stop the execution and resume after the given scaled time has passed. (scaled time is modified by your game’s current Time.timeScale)

For example, the following coroutine, when started with StartCoroutine(), will wait for 2.5 seconds before executing the Respawn() method:

IEnumerator SetRespawn() 
{
yield return new WaitForSeconds(2.5f);
Respawn();
}

If you want to wait for real seconds, you can use:
WaitForSecondsRealtime(float time)

Stoping and keeping track of Coroutines

It’s important to keep in mind some key facts about Coroutine’s lifecycle:

  • They run attached to the MonoBehaviour where they were started.
  • Existing Coroutines still run if the attached MonoBehaviour’s enabled is set to false.
  • If the related GameObject’s SetActive() is set to false , the Coroutines stop.
  • Also calling Destroy() on either the related GameObject or MonoBehaviour, will stop related Coroutines.

Unity also provides us with 2 useful methods to force a Coroutine to stop:

Spreading heavy methods across several frames

An interesting use case for Coroutines is spreading the execution of some particular method across several frames, this could be any method that includes iteration across a large collection, and performing resource-intensive tasks such as Instantiate().

For example, imagine the case where we suddenly need to spawn a lot of different enemies.
Calling a lot of Instantiate() in the same frame will surely produce a performance spike and we don't want that.

Without using Coroutines, our method would look like this:

void SpawnEnemies()
{
List<Enemy> enemies = GetEnemiesToSpawn();
foreach(var enemy in enemies)
{
Instantiate(enemy.prefab);
}
}

Now, we can optimize this by making it a Coroutine, and including a small delay between instantiations:

IEnumerator SpawnEnemiesCoroutine()
{
List<Enemy> enemies = GetEnemiesToSpawn();
foreach(var enemy in enemies)
{
Instantiate(enemy.prefab);
yield return new WaitForSeconds(.2f);
}
}

In this case, I used a 0.2-second delay because I don't mind seeing the enemies not spawning all at the same moment, but even a smaller delay of a couple of frames will help to smooth out the initial performance spike.

This same pattern can be applied to any other case where we iterate a collection, and maybe instead of using Instantiate, we are calculating distances, pathfinding, or some complex math.

Different and custom YieldInstruction types

Apart from just skipping a frame with yield return null; and waiting some time with WaitForSeconds, there are other YieldInstruction types we can use such as:

  • WaitForEndOfFrame useful if you need to read the final rendered display, or use it like a LateUpdate() for Coroutines
  • WaitForFixedUpdate if you need to stick to the Physics Update.
  • WaitUntil to wait until a given condition is true
  • WaitWhile makes the coroutine stop until the condition is false (just the opposite of WaitUntil)
  • WaitForSecondsRealtime already mentioned this one, uses unscaled time.

Also, you can define your own YieldInstruction inheriting from CustomYieldInstruction.

You can read more about each one by clicking the attached links.

--

--

Augusto Micheli Debard

Software Developer from Argentina, mostly working with Unity.