Coroutine function (Delay node) for c++

Checkout the class I made for courtine/async style programming: Utility class for asynchronous/coroutine style programming in UE4 C++ · GitHub
It doesn’t do multi-threading and only intended to be be used with methods that take longer than one frame to execute (play animation, move to, open UI Widget and wait for it to be closed, etc).

It only works with functions (delegates), that accept callbacks as their parameter.
It’s very easy to write your own Async methods that run Unreal’s MoveTo, PlayAnimation, etc.
I can provide examples if you’re interested.

Any news on Coroutines lately?

Just to give some motivation to you guys, in case you haven’t worked with Unity Coroutines yet. Here is a little example what they can do for you.
Code below are two versions of the same thing. A text is faded in when Player enters a trigger and faded out if Player leaves it. Both versions are C# and run in Unity, but Version 1 is more C++ like. It uses a state machine. Version 2 uses a Coroutine.

Version 1:



public class InfoText : MonoBehaviour
{
    enum States
    {
        FadeIn,
        FadeOut,
        Inner,
        Outer,
    }

    float fadeSpeed = 1.0f;
    float alpha = 1f;
    int frameCount; 

    
    void Awake ()
    {
        stateMachine = new StateMachine<States>(OnEnterState, OnUpdateState, null);
        stateMachine.SetState(States.Outer); 
    }


    void Update()
    {
        stateMachine.UpdateStateMachine(); 
    }

    public void OnTriggerEnter(Collider other)
    {
        if (!other.CompareTag("Player")) return;
        stateMachine.SetState(States.FadeIn);
    }

    public void OnTriggerExit(Collider other)
    {
        if (!other.CompareTag("Player")) return;
        stateMachine.SetState(States.FadeOut);
    }


    void OnUpdateState(States curState)
    {
        // fade: 
        if (curState == States.FadeIn)
        {
            UpdateAlpha(1f);     
        }
        else if (curState == States.FadeOut)
        {
            UpdateAlpha(-1f);
        }

    }


    void OnEnterState(States lastState, States nextstate)
    {
        //
        if (nextstate == States.Inner)
        {
            meshRenderer.enabled = true;
            SetAlpha(1f);
        }
        else if (nextstate == States.Outer)
        {
            meshRenderer.enabled = false;
            SetAlpha(0f);
        }
        else if (nextstate == States.FadeIn)
        {
            meshRenderer.enabled = true;
        }
    }
   

    void UpdateAlpha(float sign)
    {
        alpha += fadeSpeed*sign*Time.deltaTime;

        if (alpha > 1f)
        {
            alpha = 1f;
            stateMachine.SetState(States.Inner);
        }
        else if (alpha < 0f)
        {
            alpha = 0f;
            stateMachine.SetState(States.Outer);
        }

        SetAlpha(alpha);
    }

    void SetAlpha(float v)
    {
        alpha = v; 
        Color color = textMesh.color;
        color.a = alpha;
        textMesh.color = color;
    }

}


Version 2:



public class InfoTxt : MonoBehaviour
{
    public float fadeSpeed = 0.5f;
    
    TextMesh textMesh;   
    float alpha = 0f;   // assume Player out of trigger at start
    MeshRenderer meshRenderer;
    IEnumerator fadeRoutine;

    void Awake() 
	{
        textMesh = GetComponent<TextMesh>();
        meshRenderer = GetComponent<MeshRenderer>();
        meshRenderer.enabled = false;     // set invisible
    }


    public void OnTriggerEnter(Collider other)
    {
        if (!other.CompareTag("Player")) return; 
        FadeInOut(1f); 
    }

    public void OnTriggerExit(Collider other)
    {
        if (!other.CompareTag("Player")) return;
        FadeInOut(-1f);
    }



    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /*
        Utility
    */
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    void FadeInOut(float sign)
    {
        // 
        if (fadeRoutine != null)
        {
            StopCoroutine(fadeRoutine);
        }

        fadeRoutine = CoFade(sign);
        StartCoroutine(fadeRoutine);
    }


    IEnumerator CoFade(float sign)
    {
        //
        meshRenderer.enabled = true;

        while (!FadeFinished())
        {
            alpha += fadeSpeed * Time.deltaTime * sign;
            SetAlpha(alpha);
            yield return null;
        }
        
        // ensure no rendering if nothing visible 
        if (alpha < 0f)    meshRenderer.enabled = false;

        // ensure Alpha is exactly 0 or 1:
        alpha = Mathf.Clamp01(alpha);
        SetAlpha(alpha);
    }


    bool FadeFinished()
    {
        if (alpha > 1f) return true;
        if (alpha < 0f) return true;
        return false; 
    }

    void SetAlpha(float v)
    {
        alpha = v;
        Color color = textMesh.color;
        color.a = alpha;
        textMesh.color = color;
    }

}



There are some differences:
Aside from the setup Version 2 only needs one function to handle all states: CoFade(). If fading is needed, this function is started and does not end until fading is finished or it is stopped from outside by calling StopCoroutine(). It is easily readable and debugable. If you want to add a flash effect on the Mesh after it faded in, just go ahead and add the code at the end of the function. No need for another state that must be wired. If you want to call a callback function when fading finishes, just give it as a parameter to CoFade() and call it like a normal function at the end of CoFade().

There is no update() necessary for Version 2. Again, fading begins when ordered and ends when CoFade() returns. So no overhead if nothing happens.

You could easily write a generalized CoFade() that takes a Mesh or whatever and Fades it. Just fire and forget. If you want more control, save the IEnumerator and call StopCoroutine() whenever you like from wherever you like (just like in the sample above), e.g when the faded mesh dies.

Don’t think of it in terms of graphical effects or artist stuff. Think of behavior. Think of AI. No behavior trees needed, no state machines, no mambo jambo. Think of what could be done when combining state machines for high level behavior and Coroutines for low level behavior. Perhaps you could even skip state machines at all, I don’t know.

As far as I know, this is done by some Enumerator Objects handled by the system that also track state. So it should be possible to do in C++, even if usage would be a bit more clunky.
Coroutines are a killer feature. Half a beer for the guy who develops it for Unreal!

Ah, one more thing. You can nest coroutines. That is, if you start a coroutine from inside a coroutine the outer coroutine waits for the inner to finish and resumes execution at the right point. If you call StopCoroutine() with the IEnumerator handle from the outer coroutine while execution is in the inner coroutine the whole thing stops, just as expected.

Futures / co-routines / green-threads don’t need to stall a main thread.
However, it will require allocated stack. And, more importantly, if some resource/object on your call chain goes away while you’re not running, and you come back and run some more, you suddenly will crash.
This is one reason Blueprints only allow Delay nodes in event dispatchers, which are the “first level” of the call stack – there is nothing else to return to.
That being said, threading is totally available in Unreal – the pre-emptive kind, rather than the co-operative kind. You could use that for asynchronizing work in C++.

There are several versions that take objects, so you can just create a simple trampoline object that contains the state. Add a template and a C++ closure and it’s almost transparent.
You can also pass in a TFunction instance that closes over whatever variables you need.
Once you’ve done this once or twice, it’s simple, second nature, and no longer a nuisance :wink:

It may not have existed when OP created this thread, but there is a version of SetTimer that takes a delegate now, and you can pass arbitrary values into the delegate when you create it, e.g.,:

FTimerDelegate SomeDelegate = FTimerDelegate::CreateUObject(this, &Redacted::FunctionName, PayloadParameter1, PayloadParameter2);
TimerManager.SetTimerForNextTick(SomeDelegate);

void FunctionName(PayloadParameter1, PayloadParameter2)
{
}

I read somewhere that the C++ Gods are planning to make Coroutines a native feature on C++17.

Note that the cost of a coroutine is almost as high as the cost of a “real” thread because you need to allocate stack memory for each coroutine just like the main cost of a thread is the thread stack.

Can someone explain what is happening in that 2nd C# snippet? I’m guessing something like synchronous execution as far as the yield, at which point the calling thread continues and the coroutine also continues in parallel? Doesn’t the caller need to somehow repeatedly tell the coroutine it can run again?

HOW is this not in the docs… I wish I’d known this years ago!

1 Like

You mean these docs, that have been there years? :confused:
The delegate timer has been around a while too.

The fact you can’t bind context with events like this in blueprint is its single biggest shortfall for me. I don’t know how people get by without it.

@jwatte:
Forget about threading. I know it’s counter intuitive but coroutines are not about letting something run in another thread. This is only the way they are implemented in let’s say Lua. And the Coroutines in Lua cannot be used like the ones in Unity. They work differently.
Unity Coroutines are all running in the same thread, the main thread where anything else runs.
The coroutine does not stall the thread. It returns on every yield return something. So if you see a yield return null in the code, this is the place the function returns. It resumes at the following line in the next frame.
So it works like a Tick() function, but it maintains state automatically. A sample:




    public delegate void voidFunc(); // called when action is completed, given by clients

    public void MyDelay(float secs, voidFunc callBack) 
    {
        curCoroutine = CoDelay(secs, callBack);
        StartCoroutine(curCoroutine);
    }

    IEnumerator CoDelay(float delay, voidFunc callBack)
    {
        while (delay > 0f)
        {
            delay -= Time.deltaTime;
            yield return null;
        }

        if (callBack != null) callBack(); 
    }

// anywhere in your application: 
    public void PleaseCallMeBack()
    {
        // do something
    }

    MyDelay(3.1415f, PleaseCallMeBack);



This does exactly what it looks like. Call MyDelay() anywhere and a few seconds later you will receive a callback. May be typos in there, just hacked it in a minute.
The point is, the state of ‘delay’ is automatically kept. You don’t have to care about it. All coroutines are updated at some point in the update loop, so there is no multithreading. Your callback (or anything else) must **not **be thread save.
It’s an update without a Tick() and without keeping state by yourself. Don’t ask me how they did it, I don’t know.
I know this looks like magic for a C++ programmer (it appeared this way to me though), but it works perfectly.

EDIT: the reason for checking ‘if (callBack != null)’ is only convenience. It will not become invalid at some time. Copy&Paste, you know.

@Michael:
This looks nice, what it basically does is register a callback function for the next frame, right?
It does not keep any state, right?
May be a good base for implementing coroutines.

Ah I get it, I was thinking StartCoroutine was a C# thing when it’s obviously a Unity thing, and unity runs all the registered coroutines once each frame. It’s nice.

Thing is since C++ has no yield equivalent, you’ll never be able to create a system where you can write the code in the same way and have it run synchronously. The language just doesn’t support it, which means you have to simulate it with a Tick/Update function and store the state yourself.

Coroutines, and the built-in “yield” function of some languages to express them, are implemented exactly like threads.
I’ve implemented them myself several times in various systems in the past, and I worked on one of the heaviest-threaded OS-es to ever ship in the past, too.

The only difference is that co-routines (or “light threads” or “fibers” or whatever) are scheduled in user mode, whereas modern “kernel” or “system” threads are scheduled based on blocking I/O or a timer interrupt.
Before system-level pre-emptive threads were a common feature available in all OS-es, many OS-es shipped a “threading” library as part of their runtime, where the “threads” were all user-space, being implemented exactly like coroutines. (Java did the same thing in version 1, using a package called “Green threads.”)

So, everything I said in my previous post is accurate and factual, and “forgetting” about threads just means you’re “forgetting” about some of the necessary infrastructure.

1 Like

There is some info about iterators in C#. Afaik Unity is exploiting this to support coroutines. Most is done in the compiler. No threading involved:

Still I’ll give half a beer ;-).