• VOiD1 Gaming

Understanding Coroutines in Unity

Learn the basics of Coroutine and get to know why to use coroutine, what is coroutine, when to use it and how to use it.

If you are a game developer, especially from the Unity side then you must have come across the most used special function, Coroutine. And it also makes sense that while starting to use it first, it seems really weird, it has some odd syntax and has a different workflow. But this is a highly worthful function to master that can save a lot of your time coding difficult mechanics and improve the efficiency to the next level. So, with that being said, you might have already guessed the importance of using it, hence, let’s start understanding the basics of the coroutine and along with that answer the most asked questions, how to use it, why to use it, and when to use it.


Why use a Coroutine?

If you are a beginner and have just started your journey in Game Development using Unity then there are higher chances you might be doing your entire game stuff in the Update loop. This includes checking all the states each frame, checking all the conditions each frame, checking the entire game logic including events, animations, series of actions, etc. Well, this is an un-optimized method as it involves many such events & logic to check each frame which we don’t even require to do so. Also, if we ever need to do a series of actions in a sequence then it’s really hard to design through an Update loop.


Take, for example, we need a series of functions to call one after one and we need to wait for every function to finish before it moves to the next one, then the question is how we will do it and how tough it would be to design such mechanics? Without any doubt, not as easy as a coroutine. This is the place where Coroutine comes to play where we need to sequence the logic and have full flexibility to handle it as per our needs. It also helps us to decide whether the logic should be running all per frame or wait till a certain time or till it finishes. Moreover, as a user, we get full access to pause the execution, delay the execution, wait for a condition to satisfy, and a lot more other things that are not possible to achieve through a normal function. Therefore, Coroutine can be super useful while dealing with such instances and is a highly effective way than using the conventional normal functions and the Update method. Now, as we have understood the importance and the use case of Coroutine, let’s check what exactly a coroutine is, when, and how to use it while making games.


What is Coroutine?

Coroutines unlike regular functions are a special type of function that doesn’t need to execute the logic in a single frame, instead, coroutines provide full flexibility to delay the execution to multiple frames, delay for a specific period, suspend the operation at any time, pause the operation till any time, or even start a new set of operation while keeping the other one still paused. So, this is a way to sequence various functions and logic in terms of tasks for the program to process. Coroutines are lightweight for performing asynchronous tasks unlike the concept of threading.


Coroutine uses a return type as IEnumerator and takes the help of yield keyword to handle all the order of execution. With the help of the keyword yield, we can progressively wait until a condition is met, wait until a condition value is unchanged, wait till the next frame, and wait till a certain time.


And that’s all how coroutine works, next, let’s checkout when to use a coroutine.



When to use a Coroutine?

We have already discussed a few bright examples that show up the application of the coroutine. However, we would be looking at some more examples to get in-depth insights into the topic.

  1. From a very general perspective, it quite makes sense that Coroutines are really helpful to get good control over the function running, therefore, it can be used primarily as a TODO list for the events to take place in an order, which we can term as sequencing events.

  2. A coroutine can be used to stop the execution anytime as per the requirement.

  3. It can be thoroughly used to loop a particular function in the real-time per frame, per second, or per given condition.

  4. A coroutine can be used to resume the execution only after an event has happened.

  5. It can be used to execute an entire function once or multiple times without overlapping.

  6. A coroutine can be super helpful to animate various things and this is far more efficient in the terms of way of doing and performance.

  7. A coroutine is a lifesaver when it comes to waiting for a particular task to be complete and with the due condition, the logic should be lightweight. Using the help of coroutine we can wait for an event to finish within a few simple steps without having any computational pressure.

So, as we have now understood what is coroutine and when to use it, let’s dive deeper into the field and check how to use a coroutine.


How to use a Coroutine?

Writing a Coroutine might feel odd at first but as we go on using it rigorously in our applications it would become a daily habit to use it for various purposes. There are a few easy steps to define a coroutine and it’s quite straightforward,

  1. A Coroutine must have a return type as IEnumerator.

  2. A Coroutine should yield something.

  3. It can be only called using StartCoroutine(FuntionName()) or else StartCoroutine(“FuntionName”), instead of a normal function call.

These are the basic steps to write a Coroutine and as it can be seen, it’s quite simple. Let’s write a simple example adhering to the above-mentioned rule.



Writing a Coroutine

As per the above rule, a Coroutine can be written as,

public IEnumerator CoroutineTest()
    {
        Debug.Log("Hello World");
        yield return null;
    }

Here, unlike the general return statement, it would not stop the execution right there, instead, the yield keyword would help to return the control back to the coroutine. And when we use a null, we basically return nothing and give a green signal to the program execution to resume to the next frame. So, we would be only printing the Hello World Statement when we call the coroutine. But the question is, How to call a Coroutine? Let’s check that first.


Calling a Coroutine

Now, in order to call the above function, we can use the method name as a reference or else method name in terms of string,

StartCoroutine(CoroutineTest()); // Using Method Name as a reference
StartCoroutine("CoroutineTest"); // Using String Name

This will allow starting the coroutine or you can say call our special function to execute the logic inside it. But now, what if we want to stop it? Well, that can be easily tackled too.


Stopping a Coroutine

Stopping a Coroutine is as simple as starting it. We can store the coroutine as a variable and later use the variable to extract all the information we need. For now, let’s store the coroutine inside a variable and then use it to stop the coroutine.

var currentcoroutine = StartCoroutine(CoroutineTest()); // Stored the reference

StopCoroutine(currentcoroutine); // Stopped the Coroutine using the reference

There are also other ways to manually stop the coroutine that’s currently running, we can use the yield break statement or again use the coroutine name as a string.

Using the yield break statement we can write as,

public IEnumerator CoroutineTest(float index)
    {
        if (index == 6)
        {
            // Coroutine ends or breaks if the above condition satisfies
            yield break;
        }
        // If index is not equat to 6 then the Coroutine continues
    }

Using the Coroutine as a string we can write as,

StopCoroutine(“CoroutineTest”);

Before we continue next, let’s analyze the difference between using a string to stop a coroutine and a reference to stopping the coroutine. If we use a string to stop the coroutine it will help the user to stop all the coroutines with the same name of the string provided, hence, if we need to stop many coroutines having the same name then this is definitely worth using it. But as it uses string it has some performance implications added to it, whereas, using a reference to stop the coroutine on the other hand gives the user more control over what to stop exactly, which requires holding references to many coroutines but definitely it’s more controlled and efficient than the other approach.

Now, what if we need to stop all the coroutines at once? Do we need to store individual references and then stop them one by one? Well, Unity got you covered.


Stopping all the Coroutines

We can stop all the coroutines easily by a single statement, i.e.,

StopAllCoroutines();

And that’s sufficient to stop all the running coroutines instantly, however, with a condition. That is, the StopAllCoroutine will stop all the coroutines started by a particular behavior. This means if Script A has a coroutine running and we call StopAllCoroutine in Script A then it will stop all the coroutines inside Script A, it would not stop any coroutine running in other scripts, say Script B or Script C. Moreover, if Script A coroutines Start the Script B Coroutine then calling StopAllCoroutine inside Script A will stop both the Coroutines as Script A is the behavior responsible, while, if we use StopAllCoroutines in Script B then nothing will Stop as the coroutine in Script B is running due to Script A. Therefore, the StopAllCoroutines should be used at the origin point and doesn't mean it will stop all the coroutines in the game at once.


Now one more interesting question here would be, can a coroutine be stopped when we destroy or disable a Game object? Well, the answer to it is Yes, as the instance of the game object that started the coroutine is no more exists, the coroutine will stop instantly. However, again if the coroutine is called by a different script them disabling the game object that holds the coroutine is not enough as the coroutine started from a different script and is now attached to the script it has been called from.

Next, let's check the yield statement a bit more and learn about various yield statements available in Unity to work.



yield

The yield statement helps Unity to know that the given function is an iterator and it’s further going to iterate the execution over various frames, and yield return will specify Unity how long it’s going to wait before continuing ahead. With that being said, Unity has a couple of yield returns that can be used to achieve certain situations for which we are desiring to use Coroutines.


yield return null

We have used this in the above example which instructs Unity to wait until the next frame before continuing ahead. This can be coupled with the while statement to create a loop of executing logic every frame instead of executing all at once.

public IEnumerator CoroutineTest()
    {
        Debug.Log("Hello World");
        yield return null;
    }

yield return new WaitForSeconds(waittime)

WaitForSeconds as mentioned is a scaled time that allows the user to specify an exact time to wait before the code moves ahead. So adding a time would make the program wait till the time specified before it moves to the next code.

public IEnumerator CoroutineTest()
    {
        Debug.Log("Hello");
        yield return new WaitForSeconds(5);
        Debug.Log("World");
    }

Here, the first debug statement would print the Hello string first and then wait for 5 seconds before it prints the World String. This is super useful when you want to animate, tween objects, sequence various functions with a time constraint, etc.


yield return new WaitForSecondsRealtime(waittime)

WaitForSecondsRealtime uses the same methodology as wait for seconds with a slight modification, that is, it uses unscaled time, which means, the game is independent of its local time and it works even after you alter the timescale or any in-game time parameter. The syntax for using this method is quite simple as WaitForSeconds,

public IEnumerator CoroutineTest()
    {
        Time.timeScale = 0;
        Debug.Log("Hello");
        yield return new WaitForSecondsRealtime(5);
        Debug.Log("World");
    }

For demonstrating the right use case we have used the Timescale as 0 but you can still see that the game waits for 5 sec in real-time and then it executes the second debug statement. This same case would be a little different for the normal WaitForSeconds as it is dependent on timeScale.


yield return new WaitUntil(wait for a delegate)

WaitUntil is quite a useful method that pauses the execution until a delegate tends to true or a condition satisfies a given ruleset. This becomes super-handy to use with lambda expressions which make code a single liner and effective.

public bool shouldprint = false;
    public IEnumerator CoroutineTest()
    {
        yield return new WaitUntil(() => shouldprint == true);
        Debug.Log("Hello World");
    }

yield return new WaitWhile(wait for a delegate)

WaitWhile on the other hand waits for a particular delegate to be false or a condition to be false before moving ahead. Basically, some condition is still true, our coroutine tends to wait till it changes to false. The syntax is similar to the WaitWhile, so,

public int index = 0;
   public IEnumerator IncreaseIndex()
   {
        index += 1;
        yield return new WaitForSeconds(1f);
   }
   public IEnumerator CoroutineTest()
   {
        yield return new WaitWhile(() => index > 10);
        Debug.Log("Hello World");
   }

Here, we are using two coroutines, once for incrementing the index and the other coroutine for waiting till the index satisfies the condition and then print the Debug Statement.


yield return new WaitForEndOfFrame()

WaitForEndOfFrame as the name suggests instructs Unity to wait until all the cameras and UI elements have been rendered before displaying the frame. This can be used in various aspects and one possible example is taking a screenshot of the scene when Unity has processed all the rendering stuff of GUI, cameras, and UI.

public IEnumerator CoroutineTest()
   {
        yield return new WaitForEndOfFrame();
        TakeScreenshot();
   }

Check out How to take a Screenshot in Unity.


yield return new WaitForFixedUpdate()

WaitForFixedUpdate basically waits for the fixed update to be called. This is helpful to sync with the fixed update logic of unity and can be used for various physics calculations.

public IEnumerator CoroutineTest()
   {
        yield return new WaitForFixedUpdate();
        Debug.Log("Hello World");
   }

Yield return StartCoroutine(Couroutine)

We can also yield a new coroutine inside a coroutine using the following code.

public IEnumerator CoroutineTest()
   {
        Debug.Log("CoroutineTest Started here");
        yield return StartCoroutine(NewCoroutine());
        Debug.Log("CoroutineTest Ended here");
   }

   public IEnumerator NewCoroutine()
   {
        Debug.Log("All Coroutine ends here");
        yield return null;
   }

Using Unity Callbacks as Coroutines

Unity Callbacks can also be transformed into coroutine and can be used as per our preference. Unity callbacks like Start and Update loop have a void as return type and can be converted to coroutines. For example, let’s convert the Start function into a coroutine and wait for a certain time till we initialize the values.

private IEnumerator Start()
   {
        yield return new WaitForSeconds(5);
        Debug.Log("Initialize values hereafter waiting 5 seconds");
   }

Coroutines used to Wait for a Download

Coroutines are a lifesaver when it comes to sending web requests and waiting for the process to finish before continuing ahead. This saves a lot of time and reduces the complicacy in sync with the data, handling the states and using the data.

using UnityEngine.Networking;

 private IEnumerator WaitTillDownloadEnds(string url)
   {
        var webrequest = UnityWebRequest.Get(url);

        yield return webrequest.SendWebRequest();

        // Do operations once the server returns data
   }

Unity has this UnityWebRequest class to easily communicate via HTTP over the internet. The SendWenRequest function returns a special type of Asyncoperation which itself holds the YieldInstruction that clearly shows that this can be yielded on, and here the role of coroutines comes into play. This is super useful when it comes to networking, getting data from the internet like leaderboards, real-time communications, etc.



Advantages & Disadvantages of using a Coroutine

Coroutines come with their own upside and downside depending on the way we use them. We can definitely conclude that Coroutine is a really very helpful and clean way of doing various things but sometimes it might be just the opposite. So, in this section let’s get a quick insight into the advantages and disadvantages of using a Coroutine.


Advantages

  • It gives us more flexibility to use and handle the game logic

  • It gives us the ability to pause the game logic, wait till a condition is met, and can be also used to animate certain things more efficiently.

  • It can still run even if you disable the Monobehaviour Game Object which is not responsible for running it.

  • It is far more efficient than using the Update function for all the logic

  • Using Coroutine helps us to ease many logical things that are a bit hard to perform inside the Update function

  • A coroutine can run a separate piece of code/logic on the side even if the main script is still going on.

  • A coroutine can be run individually and interact with other elements in the game without being processed every time by a Mono behavior.

  • It doesn’t block the main thread.

  • A coroutine can be extended to several frames.


Disadvantage

  • A coroutine can be expensive if not handled properly, it can lead to garbage collection and memory heap with increasing time.

  • It is often essential to time things properly as the coroutine might not have reached the point of initialization or the piece of code we are referring to from the other places.

  • A coroutine can often overlap with the game logic which will possibly lead to bugs

  • An open-end coroutine can be performance-hungry and lead to degradation of the game performance.

  • Keeping essential code that needs to run every frame inside a coroutine might lead t bug

  • It’s not a separate thread and still runs in the main thread.

So that’s it, we are now full of knowledge on using Coroutine in our game. At the beginning of the article, we answered major questions, that is, why to use coroutine, what is a coroutine, and when to use coroutine. Furthermore, we expanded our base to understand how to write a coroutine and the logic around it. And lastly, we discussed the Advantages and Disadvantages of using Coroutines in our game. With that being said, we covered broader aspects of using Coroutines, hopefully, this adds up value to your work and proves to be really helpful in your projects.


With Love From VOiD1 Gaming



Recent Posts

See All