Understanding Coroutines

So there is whole topic on the Coroutine in Unity. Are they easy to understand? Are they a different thread? Well, let me try to explain it! ?

First of all, we should have an understanding what this thing called “Coroutine” mean and it does.

For me, I really like to explain that by comparing to regular function.

What are these “Coroutines“? ?️‍♂️

A regular function has to be executed from the beginning to the end immediately, within the same frame.

Coroutine, on the other hand, has this magic property that makes it more flexible. It can start in one frame and finish in the next one, or even after that one too. Work of a coroutine can be spread across multiple frames, and it can work even through your entire game!

All of that depends on your needs, and how you will code it, but it’s helpful to have a basic understanding.

Oh, and are they different threads? Well, not really. You can think about them in that way, but that is not the truth. Coroutines run on the main Unity thread, but they are just called at different times.

Invoking ?

There are two ways to call Coroutine.

You can either pass name of the function to the StartCoroutine() method or just pass the method itself there.

// String call
StartCoroutine("CoroutineExample");

// Method call
StartCoroutine(CoroutineExample());

Depending on which version you took, there are also ways to stop coroutines themself.

For name passed with string, you can also pass string to the StopCoroutine() method, which will stop ALL coroutines with that name.

// Start new coroutine with its name of "CoroutineExample".
StartCoroutine("CoroutineExample");

// Stop all coroutines invoked with their name of "CoroutineExample".
StopCoroutine("CoroutineExample");

The second option is to grab the reference to created coroutine and use that to stop it.

// Start new coroutine and save reference to it.
var coroutine = StartCoroutine(CoroutineExample());

// Stop coroutine with reference to it.
StopCoroutine(coroutine);

Coroutine itself ?

So now we should learn a little bit more about internals of the Coroutine.

Each Coroutine method should have yield return. This is a little different to the regular return as it don’t finish the function and allows it to continue execution on the next frame for example.

/// <summary>
/// This is example code for the coroutine method.
/// </summary>
/// <returns>Reference to the coroutine.</returns>
IEnumerator CoroutineExample()
{
    // Print a message to the console.
    Debug.Log("Starting new coroutine");

    // Endless loop
    while (true)
    {
        // Print a message to the console.
        Debug.Log("I'm Coroutine and I'm alive!");

        // Go to the next frame.
        yield return null;
    }
}

We can also tell Coroutine how long it should wait until continue running. There are special objects that let us do that and we have to yield return them.

For example this Coroutine will print log every two seconds to the console.

/// <summary>
/// This is example code for the coroutine method.
/// </summary>
/// <returns>Reference to the coroutine.</returns>
IEnumerator CoroutineExample()
{
    // Print a message to the console.
    Debug.Log("Starting new coroutine");

    // Endless loop
    while (true)
    {
        // Print a message to the console.
        Debug.LogFormat("[{0:D2}:{1:D2}] I'm Coroutine and I'm alive!", (int)(Time.time / 60), (int)(Time.time % 60));

        // Wait 2 seconds before continuing.
        yield return new WaitForSeconds(2f);
    }
}
Result of the coroutine run

We can also use yield return to invoke another Coroutine! But by doing this we will basically stop our main Coroutine until invoked Coroutine will finish doing its things.

/// <summary>
/// This is example code for the coroutine method.
/// </summary>
/// <returns>Reference to the coroutine.</returns>
IEnumerator CoroutineExample()
{
    // Print a message to the console.
    Debug.Log("Starting new coroutine");

    // Endless loop
    while (true)
    {
        // Print a message to the console.
        Debug.LogFormat("[{0:D2}:{1:D2}] I'm Coroutine and I'm alive!", (int)(Time.time / 60), (int)(Time.time % 60));

        // Starting another coroutine will pause this one until new coroutine will finish.
        yield return StartCoroutine(AnotherCoroutine());

        // Wait 2 seconds before continuing.
        yield return new WaitForSeconds(2f);
    }
}

/// <summary>
/// This is example code for another coroutine method.
/// </summary>
/// <returns>The coroutine.</returns>
IEnumerator AnotherCoroutine()
{
    // Print a message to the console.
    Debug.LogFormat("[{0:D2}:{1:D2}] I'm Another Coroutine and I'm alive!", (int)(Time.time / 60), (int)(Time.time % 60));

    // Wait 1 seconds before continuing.
    yield return new WaitForSeconds(1f);

    // Print a message to the console.
    Debug.LogFormat("[{0:D2}:{1:D2}] I'm Another Coroutine and I'm saying goodbye!", (int)(Time.time / 60), (int)(Time.time % 60));
}
Result of the coroutine run

Yeah, there are a lot of ways that you can use that or combine it with something.

Few examples:

  • Recovering Health after getting damage
  • Downloading files or making web requests
  • Animations and transitions
  • Core game logic based on time
  • And more

Do you have any questions related to the Coroutines? Let me know in the comment section below!

If you want to get notified on future content, sign up for the newsletter!

And I hope to see you next time! ?

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x