Coroutines

Coroutines

When working in Unity and you find yourself asking the following questions:

  • How can I make sure task A finishes before I can do [task B]?
  • How do I delay a method by X seconds?
  • How can I wait for a condition to be met before continuing?
  • How can I wait for a response from a web request?
  • How to I prevent locking up the main thread whilst performing a computationally-intensive operation?

This is where Coroutines can help!

What are Coroutines?

Unlike normal C# functions which are executed to completion within a single frame, Coroutines can:

  • Pause execution (yield) and return control to Unity
  • Continue execution in the next frame

Syntax

  • A Coroutine must return: IEnumerator
  • Within the body, you must have: a yield statement
  • To start a Coroutine:
    StartCoroutine(COROUTINE_FUNCTION())
    OR
    StartCoroutine("NAME_OF_COROUTINE")_

How do you Use Coroutines?

How to wait for X seconds:

    private void Start()
    {
        StartCoroutine(TaskA(3));
    }

    private IEnumerator TaskA(int delay)
    {
        Debug.Log("Task A started!");
        yield return new WaitForSeconds(delay);
        Debug.Log("Task A finished after " + delay + "seconds");
    }

    private void TaskB()
    {
        Debug.Log("Task B!");
    }

Result:

How to run a computationally-intensive operation without locking up the main thread?

    private int _counter = 0;
    private void Start()
    {
        StartCoroutine(LongRunningTask());
    }

    void Update()
    {
        if(_counter < 20)
        {
            Debug.Log("_counter: " + _counter);
        }

    }

    private IEnumerator LongRunningTask()
    {
        // simulate a long running task
        while(_counter < 20)
        {
            _counter++;
            // return execution to main thread and 
            // continue from here next frame
            yield return null;
        }
    }

Result:

  • You’ll notice that 1 is printed twice. That’s because:
    • Start is called just before the first time Update is called
    • When Update is called, any previously yielded Coroutines will be processed afterwards

Source

Wait until a condition is met

    private IEnumerator WaitUntilCounterIs(int value)
    {
        yield return new WaitUntil(() => _counter == value);
        Debug.Log("Counter is now at: "+ value);
    }

Result:

  • The Debug.Log will only be called once _counter equals value
  • The above would be the same performance as running while(_counter != value)

Considerations

  • Coroutines do not run on separate threads, they run on the main thread
  • You can stop a Coroutine using StopCoroutine(name Of Coroutine/reference)
  • Where possible, you can use C# Tasks for asynchronous functionality