Open-source project
landelare/ue5coro avatar
landelare/ue5coro

UE5Coro turns a latent UFUNCTION into a coroutine with a co_await

A deeply integrated C++ coroutine plugin for Unreal Engine 4 and 5.

1,114 stars108 forksC++BSD-3-Clause-Clear

At a glance

What is it?
UE5Coro is a C++20 coroutine plugin for Unreal Engine 4 and 5 that replaces the FPendingLatentAction boilerplate with a return type change, hides the coroutine type from Blueprint so designers see the same node as before, and ships awaiters for the engine features that are awkward to call asynchronously, including soft pointer loading, AI, animation, HTTP, collision queries and dynamic delegates. It is BSD 3-Clause Clear licensed, with a separate UE4 edition of the same release.
Who is it for?
UE5Coro is the right plugin for a gameplay programmer who has written the same latent action and delegate plumbing in a dozen places and wants it gone, because the change is as small as a return type and the Blueprint side does not move, so the change is invisible to designers.
Can I use it commercially?
Yes. BSD-3-Clause-Clear is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
No. The owners have archived the repository on GitHub, so it is read-only and no longer receives changes.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A latent function, rewritten as a coroutine

The headline example is the one the README puts first, and it shows the whole idea. A latent UFUNCTION normally means writing an FPendingLatentAction, wiring a callback, and unmounting it at the end. With UE5Coro you change the return type and the boilerplate disappears:

cpp
UFUNCTION(BlueprintCallable, meta = (Latent, LatentInfo = LatentInfo))
FVoidCoroutine Example(FLatentActionInfo LatentInfo)
{
    UE_LOGFMT(LogTemp, Display, "Before delay");
    co_await UE5Coro::Latent::Seconds(1); // Does not block the game thread!
    UE_LOGFMT(LogTemp, Display, "After delay");

    co_await UE5Coro::Async::MoveToTask();
    UE_LOGFMT(LogTemp, Display, "In game thread: {0}", IsInGameThread());
    FString Value = TEXT("Imagine this was expensive to compute");

    co_await UE5Coro::Async::MoveToGameThread();
    UE_LOGFMT(LogTemp, Display, "In game thread: {0}", IsInGameThread());
    UE_LOGFMT(LogTemp, Display, "Value: {0}", Value);
}

The function keeps its UFUNCTION declaration, the Latent meta and the LatentInfo parameter, and the delay does not block the game thread because the coroutine suspends instead of sleeping. The README also points out a property that matters more than it looks. You can destroy the actor while the coroutine is running, because latent coroutines track their target UObject and clean up early if needed. A latent action that fires a callback into a destroyed actor is a crash, and the plugin treats lifetime as part of the awaitable rather than as something the caller has to remember.

Blueprint never learns a coroutine happened

The second thing the README emphasises is that the coroutine return type is hidden from BP so as not to disturb designers. The Blueprint node for the example function looks exactly as it did before, with the same inputs and the same execution pins, and a screenshot of the node is included in the documentation to prove it. That is a design decision with real consequences. The usual way a runtime change reaches designers is a new node shape, and a new node shape means new graphs, new documentation and new confusion. Here the change is confined to the C++ side, and the plugin states that the return type is hidden from BP for exactly that reason. The same reasoning extends to people who are not interested in latent functions at all: raw C++ coroutines are also supported with the exact same feature set, and the backing implementation is selected at compile time, with latent actions only created if you actually use them.

The catalogue of engine features that get an awaiter

The feature list is organised by what kind of engine interaction you are waiting on, and each group has its own documentation page. Coroutine authoring covers the coroutines themselves, plus cancellation support on its own page, plus generators, plus Gameplay Ability System integration, which the documentation notes works slightly differently. The Unreal integration section covers AI, with MoveTo and pathfinding, animation awaiters for montages and notifies, async awaiters for multithreading and synchronisation, plus an async chain that acts as a universal wrapper for functions taking delegates, HTTP for asynchronous requests, implicit awaiters for engine types that are directly co_awaitable without a wrapper, latent awaiters for game thread interaction and Delay, and within that group asset loading for soft pointers and bundles, async collision queries for line traces and overlap checks, a latent chain as a universal latent action wrapper, and a tick time budget for running for a set number of milliseconds per frame. Latent callbacks round it off, covering interaction with the latent action manager. It is a curated list rather than a general async layer over the engine, which is what makes it usable.

Throttling, time slicing and moving between threads

The README works through the everyday problems with a question and a one line answer, and the answers are the API. For spreading a heavy computation across multiple ticks, you add a co_await NextTick() inside a loop, and there is a time budget class that lets you specify the desired processing time directly and lets the coroutine dynamically schedule itself. For throttling, co_await Ticks(bCloseToCamera ? 1 : 2) schedules the coroutine to resume every first or second tick depending on distance, which is the kind of decision that is otherwise a manual counter in a tick function. For using more than one core, co_await MoveToTask() moves everything after that line into the engine's task system on a worker thread, and co_await MoveToGameThread() brings it back. The plugin is explicit that you are free to move arbitrarily between threads, and that latent UFUNCTION coroutines automatically move back to the game thread when they are done so they can resume in Blueprint.

Awaiting a delegate, and awaiting a timeline

Two of the examples are about removing a specific kind of friction. The first is the dynamic delegate, which in the engine usually means writing a UFUNCTION purely so you can use AddDynamic and BindDynamic, and having to be in a UCLASS to do it:

cpp
co_await YourDynamicDelegate;

That is the entire code, and if the delegate carries parameters you can destructure them with auto [Your, Parameters] = co_await YourDynamicDelegate;. The second is a timeline, where co_await Timeline(this, From, To, Duration, YourUpdateLambdaGoesHere) runs an entire timeline as part of the coroutine. The plugin's argument for all of this is about the shape of the work rather than the elegance of the syntax, that a task reduced from writing the StreamableManager boilerplate and moving part of the calling function into a callback down to sticking co_await in front of it will be finished sooner, and that a synchronous blocking version of the same work needs a written justification. It also frames the alternative as a codebase problem, that asynchronous code being easy to write means there is no friction when it comes to doing things the right way, right away.

The Private namespace, and how to read the docs

The documentation has a note that is worth reading before you write code against the plugin. Most of these functions return undocumented internal types from the UE5Coro::Private namespace, and client code should not refer to anything from that namespace directly, because everything within is subject to change in future versions, without prior deprecation. The README explains why this is usually harmless, since the unnamed temporary object in co_await Something() does not appear in your source code, and the same is true of the types the awaiters return. It is a real constraint, though, and it means the surface you may name explicitly is narrower than the surface you may use. The other reading note is about where the documentation lives, since every API function is documented in the C++ headers and the releases contain the Markdown source of the documentation, so the docs can be read in a browser, read straight from an IDE, or read at the version your project actually pins, which is the one that matters when the internal types move.

One plugin, two engine generations

The release history is unusual and tells you how the project is organised. There is v2.3.2, published on 2026-07-19, and alongside it a separate tag for the same version number labelled as the UE4 edition, and an older v1.10.7 published on the same day, which is the long-running branch for the other engine generation. All three releases were published on the same day. The plugin itself is a single Unreal plugin, UE5Coro.uplugin, with Source/ for the code, Config/, Resources/ and a Docs/ directory that holds the Markdown documentation the README links to, and the documentation is organised as one page per topic, from Coroutine.md and Cancellation.md through the GAS, AI, Animation, Async, Http, Implicit, Latent and LatentLoad pages. The licence is BSD 3-Clause Clear, with the text in a COPYING file at the root, and the topics listed for the repository cover async-await, coroutines, cpp20, unreal-engine, unreal-engine-4 and unreal-engine-5.

Editorial conclusion

UE5Coro is the right plugin for a gameplay programmer who has written the same latent action and delegate plumbing in a dozen places and wants it gone, because the change is as small as a return type and the Blueprint side does not move, so the change is invisible to designers. It is the wrong tool if you are on a project that cannot adopt C++20, or if your team wants the compiler to catch thread-affinity mistakes, because moving between threads is a runtime decision the plugin makes for you. Before adopting it, read the page on the Private namespace, since most of the awaiters return internal types that are subject to change without deprecation, and check the documentation for the specific engine feature you need rather than assuming the wrapper set covers it, because the plugin is a curated list rather than a general async layer.

Frequently asked questions

What does UE5Coro do to a latent UFUNCTION?

You change its return type to one of the coroutine types the plugin provides, and you get the FPendingLatentAction boilerplate for free, with Blueprint-safe multithreading support. The coroutine return type is hidden from Blueprint so the node designers see does not change.

How do you run code on a worker thread in UE5Coro?

co_await UE5Coro::Async::MoveToTask() moves everything after that line into the engine's task system on a worker thread, and co_await UE5Coro::Async::MoveToGameThread() brings it back. You can move between threads arbitrarily, and latent UFUNCTION coroutines return to the game thread when they finish so they can resume in Blueprint.

How do you wait for a dynamic delegate in UE5Coro?

Write co_await YourDynamicDelegate, which avoids writing a UFUNCTION just for AddDynamic and BindDynamic and does not require being in a UCLASS. If the delegate has parameters, they are available through auto [Your, Parameters] = co_await YourDynamicDelegate.

Is UE5Coro supported on Unreal Engine 4 as well as 5?

The project describes itself as a coroutine plugin for Unreal Engine 4 and 5, and the release history carries both a v2.3.2 tag and a v2.3.2 tag marked as the UE4 edition, plus an older v1.10.7 line. The plugin ships as UE5Coro.uplugin with Source/, Config/, Resources/ and Docs/ directories.

Official sources

  1. Issues
  2. landelare/ue5coro on GitHub
  3. License: BSD-3-Clause-Clear
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/landelare-ue5coro.svg)](https://hysenlabs.com/projects/landelare-ue5coro)

Was this useful?