Skip to content

Coroutines

A coroutine is a piece of code that runs across many frames instead of finishing in one — the tool for "wait, then do something," "wait, then do something else, then wait again," or "keep adjusting a value a little bit every frame until it reaches a target." A health bar draining smoothly instead of snapping, a delay before an enemy attacks, a door that waits for a fade to finish before it opens — all the same shape.

Starting one

csharp
using JoystickEngine;

public class Example : GameEntity
{
    protected override void OnCreate()
    {
        RunCoroutine(SayHelloLater());
    }

    private System.Collections.IEnumerator SayHelloLater()
    {
        yield return 2.0f;   // pause here for 2 seconds
        Log.Info("Hello, two seconds later");
    }
}

A coroutine is an ordinary C# method that uses yield return — reads top to bottom like normal sequential code, even though its body actually runs across many separate frames. yield return is where it pauses and where it later resumes, picking up exactly where it left off.

RunCoroutine returns a handle you can hold onto if you might need to stop it early — see Stopping a coroutine below.

What you can yield return

Three things, and that's the whole vocabulary:

csharp
yield return null;          // pause for exactly one frame
yield return 1.5f;          // pause for 1.5 seconds
yield return SomeOther();   // run a nested coroutine to completion, then continue
You yield returnWhat happens
nullResumes on the very next frame. Use this inside a loop that needs to check or update something every frame while it runs.
a floatResumes once that many seconds have passed. Use this for a plain delay.
another coroutine (an IEnumerator)Runs that one to completion first, then resumes this one. Use this to break a long coroutine into smaller, reusable pieces.

Example: animating a value over time

csharp
private System.Collections.IEnumerator FadeOut(UIImage image, float duration)
{
    float elapsed = 0.0f;
    Vector4 startColor = image.Color;

    while (elapsed < duration)
    {
        elapsed += Time.DeltaTime;
        float t = System.Math.Min(elapsed / duration, 1.0f);
        image.Color = new Vector4(startColor.X, startColor.Y, startColor.Z, startColor.W * (1.0f - t));
        yield return null;
    }
}

This is the general shape for smoothly moving any value toward a target: loop, step the value a little closer each frame, yield return null, repeat until you arrive. Time covers Time.DeltaTime and why UI code usually wants it over the alternative.

Stopping a coroutine

csharp
private CoroutineHandle? m_Handle;

protected override void OnCreate()
{
    m_Handle = RunCoroutine(SomeLongRunningThing());
}

public void Cancel()
{
    if (m_Handle != null)
        StopCoroutine(m_Handle);
}

Stopping one that's already finished, or stopping the same one twice, is safe and does nothing. StopAllCoroutines() stops every coroutine this entity is currently running.

If you also override OnUpdate

Starting a coroutine works whether or not your script overrides OnUpdate — you don't need one just to use RunCoroutine. But if your script does override OnUpdate and also uses coroutines, add one line:

csharp
protected override void OnUpdate(float ts)
{
    base.OnUpdate(ts);   // keeps this entity's coroutines advancing

    // ... your own per-frame logic ...
}

Without it, your override replaces the coroutine-advancing behavior instead of adding to it — ordinary C# inheritance, not a coroutine-specific quirk — and this entity's coroutines will stop making progress. A script that never overrides OnUpdate at all doesn't need to think about this.

See also

  • GameEntityRunCoroutine/StopCoroutine/StopAllCoroutines are members of this base class, same as Enabled and everything else on that page.
  • UI ScriptingMoving and resizing elements is the same "step a value toward a target every frame" shape, applied to a Rect Transform instead of a color.