Unity Coroutine, simple guide with code samples

Unity coroutine gives you the power to pause and resume code blocks at your disposal. You can decide how the execution of each code block is to be done and which code needs to wait. You can execute the coroutine code in a way to optimize your game’s performance. In this post, we will see what is a coroutine and how to start and stop a coroutine along with examples, advantages and disadvantages.

What is a coroutine in Unity?

A coroutine is a function that allows pausing its execution and resuming from the same point after a condition is met. You can start a coroutine and wait for its results, before your code moves on to the next line. Or you could let the coroutine handle its business on the side while your code moves on to execute the rest of its functions.

A simple analogy to coroutine in real world is a traffic signal. The traffic on the road needs to wait until the signal turns green and meanwhile the traffic in other roads can move on. Similarly, coroutine can help you pause some part of the code for some time. Unity has introduced Async await for delay in versions 2019 and above. So, it’s totally up to you to use whichever function you like.

A coroutine in Unity looks like this

private IEnumerator SpawnBoxAfterSeconds(float seconds)
   {
       yield return new WaitForSeconds(seconds);
       Instantiate(objectToSpawn);
       Debug.Log("Spawned " + objectToSpawn.name + " after " + seconds + " seconds!");
   }

There might be a few things that may be unfamiliar to you in the code above. Let’s run through them.

IEnumerator

The return type is of IEnumerator. IEnumerator is an enum datatype. An enum only takes predefined constants as input parameters. For example, if you take the compass, the enum will take only east, west, north, and south.

In our code the value of IEnumerator datatype is sent to the system to indicate the status of the coroutine. Depending on the value of the IEnumerator the system decides whether or not to execute the coroutine.

Yield

The first line inside the function is yield return new WaitForSeconds(seconds).

As you might have guessed, this line allows us to wait for 5 seconds. After 5 seconds, it will execute the code that follows it. In this case, it will instantiate a prefab and then print a debug log statement.

The keyword “yield” is what actually sets the value of the IEnumarator, which in turn tells the system to pause the script. It will keep doing this for 5 seconds, and then the code after the yield statement will be executed.

Why Do We Need a coroutine in Unity?

Most of the Functions called in Unity are through the Unity Update method. This means every function is called and executed within that frame. If you need a function to last through multiple frames then you need coroutine. For example, instantiating an object after 5 seconds, as we saw in the above example.

When to use a coroutine in Unity?

Unity coroutine should be used when you are trying to perform a process that requires stopping or performing a sequence of steps. For example, destroy Unity objects one after the other in a sequence. Without coroutine, it might become a complex code block.

How to Start a coroutine in Unity?

To start a Coroutine you have to use a function called StartCoroutine(). This method takes a function as input. You can also pass the function name as string input to the StartCoroutine method.

A Unity coroutine is like a normal function, but it is called differently. Instead of calling it like any other function SpawnBoxAfterSeconds(5f) we need to specify that the coroutine needs to start. So, the code will be StartCoroutine(SpawnBoxAfterSeconds(5f)).

This is true for all coroutines. Failing to do this will not throw an error, but the Unity coroutine function will not work at all.

There are different ways to do this. Let’s see them one by one.

Method 1: Start coroutine with parameters

If you want to pass the arguments to a coroutine then you just call the coroutine inside the StartCoroutine() as shown in the code below

public class ExampleScript : MonoBehaviour
{
   public GameObject objectToSpawn;
   private void Start()
   {
       StartCoroutine(SpawnBoxAfterSeconds(5f));
   }

   

The only drawback of starting the coroutine this way is, you cannot stop the coroutine. We will discuss more about this in the next section on stopping coroutine.

Method 2: Example, using a string input

The second way is to pass the name of the function as a parameter in string format. However, you can pass only a single parameter to the function in this way of starting a coroutine.


   private void Start()
   {
       StartCoroutine("SpawnBoxAfterSeconds",5f);
   }

   

The way you start the coroutine also decides how you are going to stop it. So, let’s see how to stop them.

Method 3: Passing the coroutine to a IEnumerator

You can assign the coroutine to an IEnumerator and then pass the IEnumerator to the StartCoroutine() function.

Here is how to do it

private IEnumerator SpawnBoxIEnumerator;
private void Start()
   {
       SpawnBoxIEnumerator = SpawnBoxAfterSeconds(3f);
       StartCoroutine(SpawnBoxIEnumerator);
   }

Stopping coroutine in Unity

A coroutine is stopped when the game object with the script is destroyed, which happens during scene change. So, you don’t have to stop a coroutine when changing scenes. Only time you need to stop it is when you have to interrupt the coroutine from executing or disable the script.

That’s right. coroutines do not stop when the script is disabled, you need to destroy or disable the game object to stop them.

If you want to stop a coroutine from inside the script then, there are different ways to stop the coroutine and it depends on how you started it.

Method 1: Using the coroutine’s name as string

The most common way is to use the StopCoroutine() method with the coroutine’s name. Here is how to do it.

StopCoroutine(“SpawnBoxAfterSeconds”);

You do not need to specify any parameters for stopping the coroutine. It will simply stop the Unity coroutine with that name. This method will only work if you had started the coroutine by passing the function name as a string like in the 2nd method of Starting a coroutine.

Using this method will stop all the coroutines with that function name. So, if you do this:

private void Start()
   {
      StartCoroutine("SpawnBoxAfterSeconds");
      StartCoroutine("SpawnBoxAfterSeconds");
      StartCoroutine("SpawnBoxAfterSeconds");

      StopCoroutine("SpawnBoxAfterSeconds");
   }

It would stop all 3 coroutines. This may or may not be what you intend to do, so use it with this point in mind.

As mentioned above, stopping a coroutine by passing a string parameter only works if you start it in the same way.

Method 2: Using an object of type coroutine

The second method involves modification to how you start the coroutine. You need to store the coroutine into a variable of type coroutine when you start it. That way you can stop the coroutine by passing that variable instead. Here’s an example:

public class ExampleScript : MonoBehaviour
{
   public GameObject objectToSpawn;
   private Coroutine spawnBoxCoroutine;
   private void Start()
   {
       spawnBoxCoroutine = StartCoroutine(SpawnBoxAfterSeconds(5f));
      
       StopCoroutine(spawnBoxCoroutine);
   }

   private IEnumerator SpawnBoxAfterSeconds(float seconds)
   {
       yield return new WaitForSeconds(seconds);
       Instantiate(objectToSpawn);
       Debug.Log("Spawned " + objectToSpawn.name + " after " + seconds + " seconds!");
   }
}

As you can see in the example above, we created a variable of type Unity coroutine. When we call StartCoroutin(), it stores the coroutine into that object. This way we have a reference to it. In the next line, we call StopCoroutine() and instead of passing a string with the name of the function, we pass the Coroutine object.

Note: Since we are creating a reference to the object, you can also do this even if you start the coroutine like this spawnBoxCoroutine = StartCoroutine(“SpawnBoxAfterSeconds”).

Method 3: Using an IEnumerator

Another way of stopping a Unity coroutine is by passing the IEnumerator variables inside StopCoroutine(). Here’s an example.

public class ExampleScript : MonoBehaviour
{
   public GameObject objectToSpawn;
   private IEnumerator SpawnBoxIEnumerator;
   private void Start()
   {
       SpawnBoxIEnumerator = SpawnBoxAfterSeconds(3f);
       StartCoroutine(SpawnBoxIEnumerator);
      
       StopCoroutine(SpawnBoxIEnumerator);
   }

   private IEnumerator SpawnBoxAfterSeconds(float seconds)
   {
       yield return new WaitForSeconds(seconds);
       Instantiate(objectToSpawn);
       Debug.Log("Spawned " + objectToSpawn.name + " after " + seconds + " seconds!");
   }
}

Method 4: Using a break statement

You can use the break statement to end any type of loop and coroutines are no exception. You need to use the break with yield in case of a coroutine. If you use the break statement as your first line, then the coroutine will end immediately.

Here is the sample code

private IEnumerator Using_break()
   {
       yield break;
       
   }

Stopping multiple coroutines at once

In Unity, there are situations where you may need to stop all active coroutines simultaneously. While Unity provides the StopAllCoroutines() function to achieve this, it can only be called from a MonoBehaviour script. Here’s an example of how to stop all coroutines in Unity at once:

using System.Collections;
using UnityEngine;

public class CoroutineExample : MonoBehaviour
{
    private Coroutine coroutine1;
    private Coroutine coroutine2;
    private Coroutine coroutine3;

    private void Start()
    {
        // Start the coroutines
        coroutine1 = StartCoroutine(Coroutine1());
        coroutine2 = StartCoroutine(Coroutine2());
        coroutine3 = StartCoroutine(Coroutine3());

        // Stop the coroutines after 5 seconds
        StartCoroutine(StopCoroutinesAfterDelay(5f));
    }

    private IEnumerator Coroutine1()
    {
        while (true)
        {
            Debug.Log("Coroutine 1 is running...");
            yield return new WaitForSeconds(1f);
        }
    }

    private IEnumerator Coroutine2()
    {
        while (true)
        {
            Debug.Log("Coroutine 2 is running...");
            yield return new WaitForSeconds(2f);
        }
    }

    private IEnumerator Coroutine3()
    {
        while (true)
        {
            Debug.Log("Coroutine 3 is running...");
            yield return new WaitForSeconds(3f);
        }
    }

    private IEnumerator StopCoroutinesAfterDelay(float delay)
    {
        yield return new WaitForSeconds(delay);

        // Stop all coroutines
        StopAllCoroutines();

        Debug.Log("All coroutines stopped.");
    }
}

In this script, we define three coroutines (Coroutine1, Coroutine2, and Coroutine3) that log a message to the console at different intervals. In the Start() method, we start these coroutines by calling StartCoroutine() and store their references in variables.

After 5 seconds, we call the StopAllCoroutines() function inside the StopCoroutinesAfterDelay() coroutine to stop all running coroutines. Finally, a message is logged to the console to indicate that all coroutines have been stopped.

Here is the output of the above code

All coroutines stopped at once

Wait options in Unity coroutine

Wait For Seconds Method

In most of the examples above, we used a method called WaitForSeconds(). This is used very commonly with coroutine. So, let’s try to understand it with the code below.

public class ExampleScript : MonoBehaviour
{
   public GameObject objectToSpawn;
   private IEnumerator SpawnBoxIEnumerator;

   private void Start()
   {
       StartCoroutine(SpawnBoxAfterSeconds(3f));
       Debug.Log("Complete!");
   }

   private IEnumerator SpawnBoxAfterSeconds(float seconds)
   {
       yield return new WaitForSeconds(seconds);
       Instantiate(objectToSpawn);
       yield return new WaitForSeconds(2f);
       Debug.Log("Spawned " + objectToSpawn.name + " after " + Time.time + " seconds!");
   }

Console log

As you can see, inside the Start function, we start the coroutine and then print “Complete!”. When the coroutine started it waited for 3 seconds, because that’s the amount we passed to the WaitForSeconds() function. And then instantiated a game object.

Then it waited for 2 additional seconds and printed another message stating that it spawned Cube after 5 seconds. It says 5 seconds because that is the amount of time that has passed since the game started (3 + 2 seconds). We got the time by using Time.time, which is a variable that returns the amount of time since the game started.

We have another problem!

Inside the start function, we printed “Complete!”, but the coroutine was not complete when the message got printed. Let’s fix that in this coming section by waiting for the Coroutine to finish.

Waiting for coroutine to finish

In order to wait for a coroutine to finish processing before we can move on, we need to start the coroutine from another coroutine. You can convert the Unity Start() into a coroutine.

Here is how to do it.

private IEnumerator Start()
{
   yield return StartCoroutine(SpawnBoxAfterSeconds(3f));
   Debug.Log("Complete!");
}

private IEnumerator SpawnBoxAfterSeconds(float seconds)
{
   yield return new WaitForSeconds(seconds);
   Instantiate(objectToSpawn);
   yield return new WaitForSeconds(2f);
   Debug.Log("Spawned " + objectToSpawn.name + " after " + Time.time + " seconds!");
}

We converted the Start function into a coroutine with a return type IEnumerator and added the keywords yield return before we started the coroutine. Yield as we mentioned, is what tells unity to move on to the next code. So, until the SpawnBoxAfterSeconds coroutine hasn’t been completed, we do not move on to the next line. Note that this could cause the game to get stuck in an infinite loop if not written correctly.

Below is the result of the above code. As you can see, “Complete!” gets called right after the cube is spawned.

WaitForSeconds is affected by Time.timescale in Unity. If you want the coroutine to run even when the timescale is set to zero then you can use the function below.

yield return new WaitForSecondsRealtime(5);

This uses the system time and is not affected by change in time in Unity.

Make Unity coroutine Wait Until a condition is true

You can use WaitUntil method to pause a coroutine in Unity till a condition is met. WaitUntil takes a Boolean as input parameter and pauses the coroutine execution until the parameter is true.

Here is how to use it

yield return new WaitUntil(() => game_paused();

Make Unity coroutine Wait While a condition is true

WaitWhile is very similar to WaitUntil. The Difference between them is WaitUntil will pause till the parameter is false whereas WaitWhile will pause when the parameter is true.

Here is how to use WaitWhile

yield return new WaitWhile(() => game_playing );

Make coroutine wait till end of frame

You can make the coroutine wait till the end of the current frame using the function WaitForEndOfFrame.

Here is a code sample

yield return new WaitForEndOfFrame();

Make coroutine wait will next frame

As you have seen in the above examples you can pass many functions to the yield statement depending on how you want the coroutine to wait before proceeding. If you want the coroutine to just wait till the next frame then you can pass null to the yield statement.

Here is how to use it

yield return null;

yield return null is very useful to make a block of code wait till all the other code blocks are executed in a given frame. For example, if you want to take a screenshot in Unity, after all elements are loaded you can use yield return null to wait till the end of the frame and then take a screenshot.

Advantages and Uses of Unity coroutine

Coroutines are mainly used, when necessary, rather than for convenience. It is easy to use, and it’s quite handy in many situations. However, the sad truth is that coroutines are expensive when overused or when not tracked. Make sure that your coroutines always end. And if they do not work in a way that they end when a condition is satisfied (maybe you want to constantly calculate something all the time), then stop the coroutines manually through code yourself. Cleanup is very important when using a lot of coroutines.

Advantages

  • The advantages of coroutines are that they work wonders in smaller games. You wouldn’t need to worry about the performance overhead, and you can use them as you please. Don’t get me wrong, you can use them even in large-scale games, but they need to be used carefully and properly.
  • They are convenient when you want to wait (as shown in above examples), you can wait for a few seconds, you can wait for the end of the frame, and you can even bypass Unity time. This means that if your Time.timescale is set to 0 when your game is paused, or is part of your game’s mechanics, you can use yield return yield return WaitForSecondsRealtime(2f).
  • Another advantage is that you can run a separate block of code on the side while your script goes through all its lines of code. What I mean by this is that when you call a coroutine, a piece of functionality can run without your main code having to stop and wait for the process to finish. An example was made in the Wait for seconds example, where the print happened even though the coroutine hadn’t finished its process yet. Which can come in handy.

Disadvantages

  • A disadvantage would be that coroutines return IEnumerator. So, you cannot return a float or Game Object if you need them to. Although it is possible in C#, it would require further implementation that is not provided by Unity by default.
  • As mentioned above, they can be expensive if used too much, because they also create garbage and are not completely running on a separate thread in the backend.
  • Another disadvantage arises if coroutines are used for the wrong reason, or implemented wrong. For example, if you have some code running that depends on a variable being set inside a coroutine, you may get a null result because the coroutine didn’t set the variable in time.

That’s it about Unity coroutine. Now it’s your turn to try it out and let us know in the comment section below, if you need any help.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from VionixStudio

Subscribe now to keep reading and get access to the full archive.

Continue reading