A Replacement for Coroutines in Unity + C#

Coroutines are a great idea and super useful, but they’re kind of unwieldy to use in C# and sometimes they just don’t plain work. Or I don’t know how to use them properly. All I know is that they’re more complicated than they need to be, and I remember having problems using them from an Update method.

So I made my own version of Coroutines inspired by the XNA WaitUntil stuff I posted about a long time ago. Here it is!

using System;
using UnityEngine;
using Debug = UnityEngine.Debug;
using Object = UnityEngine.Object;

class ConditionalBehaviour : MonoBehaviour
{
    public float SinceAlive;

    public Action Action;
    public Condition Condition;

    void Update()
    {
        SinceAlive += Time.deltaTime;
        if (Condition(SinceAlive))
        {
            if (Action != null) Action();
            Destroy(gameObject);
            Action = null;
            Condition = null;
        }
    }
}

public delegate bool Condition(float elapsedSeconds);

public static class Wait
{
    public static void Until(Condition condition, Action action)
    {
        var go = new GameObject("Waiter");
        var w = go.AddComponent<ConditionalBehaviour>();
        w.Condition = condition;
        w.Action = action;
    }
    public static void Until(Condition condition)
    {
        var go = new GameObject("Waiter");
        var w = go.AddComponent<ConditionalBehaviour>();
        w.Condition = condition;
    }
}

Here’s an example of use, straight out of the Volkenessen code (with special guest appearance from my ported easing functions) :

var initialOffset = new Vector3(hitDirection.x * -1, 0, 0);
var origin = armToUse.transform.localPosition;
armToUse.renderer.enabled = true;

Wait.Until(elapsed =>
{
    var step = Easing.EaseOut(1 - Mathf.Clamp01(elapsed / Cooldown), EasingType.Cubic);
    armToUse.transform.localPosition = origin + initialOffset * step;
    return step == 0;
},
() => { armToUse.renderer.enabled = false; });

What’s going on here :

  • You call Wait.Until as a static method and pass it one or two methods (be it lambdas or method references) : The first one is the Condition which gets evaluated every Update until it returns true, and the second gets evaluated when the condition is true (it’s a shorthand, basically)
  • The Wait static class instantiates a “Waiter” game object and hooks a custom script component to it that does the updating and checking stuff
  • The condition gets passed the number of seconds elapsed since the component was created, so you don’t have to keep track of it separately.

I use it for waiting for amounts of time (Wait.Until(elapsed => elapsed > 2, () => { /* Something */ })), interpolate values and do smooth transitions (like the code example above, I animate the player’s arm with it), etc.

I’ll probably keep updating my component as I need more things out of it, but up to now it’s served me well. Hope it helps you too!

7 thoughts on “A Replacement for Coroutines in Unity + C#”

  1. I agree that coroutines are unwieldy to handle in C# but I never had an issue with them not working. Do you have an example where you struggled with using them?

    I like the approach of using delegates to define the wait condition, which gives great freedom to implement different conditions. However, this approach looses the sequential nature of coroutines, which is great to script sequences of actions spread over time. It seems to me be two approaches are quite different in what they’re trying to achieve.

    1. Yeah, I should’ve researched what I’m complaining about more before posting. I’ll try to reproduce my problems and post back.
      I also agree that the approaches are different, but out of habit I’ve never felt the need to have more than what my Waiters provide.
      I’ve been linked a comprehensive article on coroutines and how they work on twitter, looks like I have some studying to do. :P

  2. Dude, when we worked together last year I liked your waiters so much that I reimplemented a lightweight variant of them in Unity too. I didn’t share the code because, uh, I’m still sort of shy about sharing code for some reason, but now I feel silly for not at least sending it to you/telling you about it!

    In my variant, I have an overloaded version of the instantiation method that allows you to attach the new component to an arbitrary GameObject — which can be useful in cases where an object affected by the action might be destroyed, or is otherwise mutable in a such a way that would require the interruption of the action before it’s through.

    1. Aha! Glad to hear it :D
      Hooking to an existing object is a great idea, and addresses the problem of garbage (one short-lived object per waiter isn’t very nice for the GC). I’ll try it when I have a chance!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>