Skip to content

Coroutines

Shmellyorc edited this page Aug 28, 2026 · 2 revisions

Coroutines

Coroutines let you write sequential code that happens over time. Instead of using complex state machines or timers, you write code that looks like it's running in order, but can pause and resume.

Basic Usage

Define a coroutine as an IEnumerator method:

IEnumerator MyCoroutine()
{
    Console.WriteLine("Start");
    yield return new WaitForSeconds(1f);
    Console.WriteLine("After 1 second");
    yield return new WaitForSeconds(0.5f);
    Console.WriteLine("After 1.5 seconds");
}

Run it through the CoroutineManager:

CoroutineManager.Instance.Run(MyCoroutine());

Advanced Usage

The lerpFunc parameter is the interpolation function for your value type. For common types, you can use the built-in helper methods directly:

// Float interpolation
new Tween<float>(
    from: 0f, to: 100f, duration: 1f, type: EaseType.QuadOut,
    lerpFunc: MathHelper.Lerp,
    onUpdate: value => position.X = value
);

// Vect2 interpolation
new Tween<Vect2>(
    from: Vect2.Zero, to: new Vect2(100, 50), duration: 1f, type: EaseType.SineInOut,
    lerpFunc: Vect2.Lerp,
    onUpdate: value => entity.Position = value
);

// Color interpolation
new Tween<Color>(
    from: Color.Red, to: Color.Blue, duration: 2f, type: EaseType.QuadInOut,
    lerpFunc: Color.Lerp,
    onUpdate: value => sprite.Color = value
);

// Rect2 interpolation
new Tween<Rect2>(
    from: new Rect2(0, 0, 100, 100), to: new Rect2(50, 50, 200, 200), duration: 1f, type: EaseType.Linear,
    lerpFunc: Rect2.Lerp,
    onUpdate: value => uiRect = value
);

You can also use MathHelper.SmoothStep or Vect2.SmoothStep for smooth Hermite interpolation instead of linear.

Delays

Delay execution for a specified duration:

yield return new WaitForSeconds(2f);          // 2 seconds (scaled by time scale)
yield return new WaitForSecondsRealtime(2f);  // 2 real seconds (ignores time scale)
yield return new WaitForFrames(30);           // 30 frames
yield return new WaitForNextFrame();          // One frame
yield return new Delay(0.5f);                 // Simple delay alias

Conditionals

Wait until a condition becomes true or false:

yield return new WaitUntil(() => isReady);                    // Wait until true
yield return new WaitWhile(() => isAnimating);                // Wait while true
yield return new WaitUntilOrTimeout(() => hasLoaded, 5f);     // Wait with timeout
yield return new WaitWhileOrTimeout(() => isProcessing, 3f);  // Wait while with timeout

Waiting for Values

// Wait for a non-null value
var waitForPlayer = new WaitUntilNotNull<Player>(() => playerInstance);
yield return waitForPlayer;
var player = waitForPlayer.Value;

// Wait while a value is non-null
var waitForDeath = new WaitWhileNotNull<Enemy>(() => currentEnemy);
yield return waitForDeath;

Tweens (Animations)

Tweens smoothly animate values over time with 33 easing functions.

Basic Tween

var tween = new Tween<float>(
    from: 0f,
    to: 100f,
    duration: 1f,
    type: EaseType.QuadOut,
    lerpFunc: (a, b, t) => MathHelper.Lerp(a, b, t),
    onUpdate: value => position.X = value
);

CoroutineManager.Instance.Run(tween);

Common Tween Types

// Callback when complete
var callbackTween = new CallbackTween<float>(
    from: 0f, to: 100f, duration: 1f, EaseType.QuadOut,
    lerpFunc, onUpdate: value => x = value,
    onComplete: () => Console.WriteLine("Done!")
);

// With initial delay
var delayed = new DelayedTween<float>(
    from: 0f, to: 100f, duration: 1f, EaseType.QuadOut,
    lerpFunc, onUpdate: value => x = value,
    delay: 0.5f
);

// Loop multiple times
var looped = new LoopTween<float>(
    from: 0f, to: 100f, duration: 1f, EaseType.QuadOut,
    lerpFunc, onUpdate: value => x = value,
    loops: 3
);

// Infinite loop
var infinite = new LoopTween<float>(
    from: 0f, to: 100f, duration: 1f, EaseType.QuadOut,
    lerpFunc, onUpdate: value => x = value,
    loops: -1
);

// Ping pong (back and forth)
var pingpong = new PingPongTween<float>(
    from: 0f, to: 100f, duration: 1f, EaseType.QuadOut,
    lerpFunc, onUpdate: value => x = value
);

// Pulse (heartbeat effect)
var pulse = new PulseTween<float>(
    a: 1f, b: 1.5f, durationPerCycle: 1f,
    EaseType.QuadOut, lerpFunc, onUpdate: value => scale = value,
    cycles: -1
);

// Speed multiplier (fast forward / slow motion)
var speedTween = new SpeedTween<float>(
    from: 0f, to: 100f, duration: 1f, EaseType.QuadOut,
    lerpFunc, onUpdate: value => x = value,
    speed: 2f
);

Compositions

Combine multiple coroutines into sequences or concurrent groups.

Sequence: Run one after another

var sequence = new Sequence(
    new Tween<float>(0f, 100f, 1f, EaseType.QuadOut, Lerp, value => x = value),
    new Delay(0.5f),
    new Tween<float>(100f, 200f, 1f, EaseType.QuadOut, Lerp, value => x = value),
    new DoOnce(() => Console.WriteLine("Complete!"))
);

CoroutineManager.Instance.Run(sequence);

Concurrent: Run all at once

var concurrent = new Concurrent(
    new Tween<float>(0f, 100f, 1f, EaseType.QuadOut, Lerp, value => x = value),
    new Tween<float>(0f, 50f, 1.5f, EaseType.SineInOut, Lerp, value => y = value),
    new Delay(0.5f)
);

CoroutineManager.Instance.Run(concurrent);

Repeat: Run repeatedly

Func<IEnumerator> tweenFactory = () => new Tween<float>(
    from: 0f, to: 100f, duration: 0.5f, EaseType.QuadOut,
    lerpFunc, onUpdate: value => x = value
);

// Repeat 5 times
var repeat5 = new Repeat(tweenFactory, 5);

// Repeat indefinitely
var infinite = new Repeat(tweenFactory, -1);

CoroutineManager.Instance.Run(infinite);

Time-Based Actions

Execute actions at regular intervals.

// Every 1 second
CoroutineManager.Instance.Run(new EverySeconds(1f, () => Console.WriteLine("Tick!")));

// Every 30 frames
CoroutineManager.Instance.Run(new EveryFrames(30, () => Console.WriteLine("Frame tick!")));

// Single delayed callback
CoroutineManager.Instance.Run(new DelayCall(2f, () => Console.WriteLine("Delayed!")));

Waiting for Beacons

Wait for events to be published through the Beacon system.

// Wait for any beacon on a topic
var wait = new WaitForBeacon("PlayerDied");
yield return wait;
var handle = wait.Result;

// Wait with predicate filtering
var waitFiltered = new WaitForBeacon(
    "DamageEvent",
    h => h.Source == "Player"
);
yield return waitFiltered;

// Wait with timeout
var waitTimeout = new WaitForBeacon(
    "NetworkResponse",
    timeoutSeconds: 5f
);
yield return waitTimeout;

if (waitTimeout.Result == null)
    Console.WriteLine("Timed out!");

// Wait for multiple beacons
var waitCount = new WaitForBeaconCount("EnemyKilled", 3);
yield return waitCount;
Console.WriteLine("Three enemies killed!");

Coroutine Handle

Track and control running coroutines.

var handle = CoroutineManager.Instance.Run(MyCoroutine());

// Check if running
if (handle.IsRunning)
{
    // Still going...
}

// Stop the coroutine
handle.Stop();

// Wait for completion from another coroutine
IEnumerator WaitForIt()
{
    yield return handle.Wait();
    Console.WriteLine("Coroutine finished!");
}

Stopping Coroutines

// Stop by handle
handle.Stop();

// Stop all coroutines
CoroutineManager.Instance.StopAll();

// Check if a specific coroutine is running
bool running = CoroutineManager.Instance.IsRunning(handle);

Easing Functions

Void provides 33 easing functions across multiple families:

  • Linear
  • Quadratic (In, Out, InOut, OutIn)
  • Cubic (In, Out, InOut, OutIn)
  • Quartic (In, Out, InOut, OutIn)
  • Quintic (In, Out, InOut, OutIn)
  • Sine (In, Out, InOut, OutIn)
  • Exponential (In, Out, InOut, OutIn)
  • Circular (In, Out, InOut, OutIn)
  • Back (In, Out, InOut, OutIn)
  • Elastic (In, Out, InOut, OutIn)
  • Bounce (In, Out, InOut, OutIn)

Use any easing type in a tween:

var tween = new Tween<float>(
    from: 0f,
    to: 100f,
    duration: 1f,
    type: EaseType.BounceOut,
    lerpFunc: MathHelper.Lerp,
    onUpdate: value => position.X = value
);

Back to Home

Clone this wiki locally