Lets Talk Coroutines


What are coroutines?

sudo async

Coroutines Vs Async

Coroutine Basics

public class Coroutines: Monobehaviour
{
    public IEnumerator Coroutine()
    {
        Debug.log("Started");
        yield return new WaitForSeconds(5);
        Debug.log("5 seconds later!");
    }
}

As a loop!

public class Coroutines: Monobehaviour
{
    public IEnumerator Coroutine()
    {
        while(true)
        {
            yield return null; // DON'T FORGET THIS!!!
        }
    }
}

Yielding another coroutine

One cool thing we can do inside coroutines is wait on another coroutine to execute before continuing our code. For example

public IEnumerator Interact()
    {
        // Move close enough to interact with object
        // And wait until we're in position to do the interact
        yield MoveToPosition(interactPos);

        Interact();
    }

public IEnumerator MoveToPosition(Vector3 targetPos)
    {
        while(distanceFromTargetPos > interactRange)
        {
            MoveCharacter();

            yield return null; // DON'T FORGET THIS!!!
        }
    }

Start as coroutine

Note on yielding

You’ll often see people define the yield points with something like yield return new WaitForSeconds(x);. While this is usually fine, if you’re working in a loop it’s better to predefine you’re waits if you can. For example

public IEnumerator Coroutine()
    {
        // make one wait 
        WaitForSeconds wait = new WaitForSeconds(1);
        while(true)
        {
            // yield on that wait
            yield return wait;
        }
    }

Managing coroutines

A lot of the time you can get away with just StartCoroutine() and StopAllCoroutines(), but sometime’s you’ll be having multiple coroutines running on a single object. You’ll want to cancel one of them while leaving the other running, so how do we do that? We can actually store these corutines in a Coroutine variable! StartCoroutine() will return this variable

Coroutine cr = StartCoroutine(CR);

Then when we want to stop it, we just pump it through StopCoroutine()

StopCoroutine(cr);



One of the more common uses I’ve had for this is animation transitions, or some other value, where say we want to run a close animation while the open animation is still playing. IE, we want to stop the open animation and start the close animation.

private Dictionary<Window, Coroutine> windowCRsDict = new();

public void Open(Window window)
{
    if(windowCRsDict.TryGet(window, out Coroutine CR))
    {
        StopCoroutine(CR);
        windowsCRsDict.Remove(window);
    }

    Coroutine openCR = StartCoroutine(Animate(window, true));
    windowsCRsDict.Add(window, openCR);
}

public void Close(Window window)
{
    if(windowCRsDict.TryGet(window, out Coroutine CR))
    {
        StopCoroutine(CR);
        windowsCRsDict.Remove(window);
    }

    Coroutine openCR = StartCoroutine(Animate(window, false));
    windowsCRsDict.Add(window, openCR);
}

private void Animate(Window window, bool opening)
{

    // animate!
    yield return null;

    // remove the window from the dict, since the animation is finished
    // we don't need to run a StopCoroutine because it's just gonna end after this anyway
    if(windowsCRsDict.Contains(window))
    {
        windowsCRsDict.Remove(window);
    }
}

Storing IEnumerator object

There is another way of saving/referencing coroutines, . I’ve never used this method myself, but I’m sure you could find a good case for it!

Coroutines and static methods

Yes, you can call coroutines from static classes/methods!

public static class StaticCoroutine
{

}

You just have have a refernce to a monobehavior that’s going to run it.