Home

Awesome

UE5Coro

UE5Coro implements C++20 coroutine support for Unreal Engine 5 with a focus on gameplay logic, convenience, and providing seamless integration with the engine.

[!NOTE] Support for C++17, older compilers, platforms, and engine versions is available in the legacy UE5Coro 1.x series.

There's built-in support for easy authoring of latent UFUNCTIONs. Change the return type of a latent UFUNCTION to make it a coroutine, and you get all the FPendingLatentAction boilerplate for free, with BP-safe multithreading support out of the box:

UFUNCTION(BlueprintCallable, meta = (Latent, LatentInfo = LatentInfo))
FVoidCoroutine Example(FLatentActionInfo LatentInfo)
{
    UE_LOGFMT(LogTemp, Display, "Started");
    co_await UE5Coro::Latent::Seconds(1); // Does not block the game thread!
    UE_LOGFMT(LogTemp, Display, "Done");
    co_await UE5Coro::Async::MoveToThread(ENamedThreads::AnyThread);
    auto Value = HeavyComputation();
    co_await UE5Coro::Async::MoveToGameThread();
    UseComputedValue(Value);
}

This coroutine will automatically track its target UObject across threads, so even if its owning object is destroyed before it finishes, it won't crash due to a dangling this on the game thread.

Even the coroutine return type is hidden from BP to not disturb designers:

Latent Blueprint node for the Example function above

Not interested in latent UFUNCTIONs? Not a problem. Raw C++ coroutines are also supported, with the exact same feature set. The backing implementation is selected at compile time; latent actions are only created if you actually use them.

Change your return type to one of the coroutine types provided by this plugin, and complex asynchronous tasks that would be cumbersome to implement yourself become trivial one-liners that Just Work™, eliminating the need for callbacks and other handlers.

This should give a taste of the significant reduction in code and effort that's possible with this plugin. Less and simpler code to write generally translates to fewer bugs, and asynchronous code being easy to write means there's no friction when it comes to doing things the right way, right away.

Say goodbye to that good-enough-for-now™ LoadSynchronous that's still in Shipping, two updates later. With the task at hand reduced from "write all the StreamableManager boilerplate and move a part of the calling function into a callback" to merely "stick co_await in front of it", you'll finish quicker than it would've taken to come up with a justification for why the synchronous blocking version is somehow acceptable.

There are plenty of additional features in the plugin, such as generators that let you avoid allocating an entire TArray just to return a variable number of values. Easier for you to write, easier for the compiler to optimize, you only need O(1) storage instead of O(N) for N items, what's not to like?

List of features

The following links will get you to the relevant pages of the documentation. Bookmark this section if you prefer to read the latest documentation in your browser, or read it straight from your IDE. Every API function is documented in the C++ headers, and the releases contain the Markdown source of the documentation that you're reading right now.

Coroutine authoring

These features focus on exposing coroutines to the rest of the engine.

<!-- There is an additional, unlisted documentation page: Private.md -->

Unreal integration

These wrappers provide convenient ways to consume engine features from your coroutines.

[!NOTE] Most of these functions return undocumented internal types from the UE5Coro::Private namespace. Client code should not refer to anything from this namespace directly, as everything within is subject to change in future versions, without prior deprecation.

Most often, this is not an issue: for example, the unnamed temporary object in co_await Something() does not appear in source code. If a Private return value needs to be stored, use auto (or a constrained TAwaitable auto) to avoid writing the type's name.

Directly calling the public-by-necessity C++ awaitable functions await_ready, await_suspend, and await_resume is not supported on any awaiter.

Additional features

Installation

Only numbered releases are supported. Do not use the Git branches directly.

Download the release that you've chosen, and extract it into your project's Plugins folder. Rename the folder to just UE5Coro, without a version number. Done correctly, you should end up with YourProject\Plugins\UE5Coro\UE5Coro.uplugin.

[!NOTE] Please refer to the release's own README if you're using 1.x. It had a different method of installation involving multiple plugins.

Project setup

Your project might use some legacy settings that need to be removed to unlock C++20 support, which otherwise comes as standard in new projects made in Unreal Engine 5.3 or later.

In your Target.cs files (all of them), make sure that you're using the latest settings and include order version:

DefaultBuildSettings = BuildSettingsVersion.Latest;
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;

If you're using the legacy bEnableCppCoroutinesForEvaluation flag, that's not needed anymore, and it should no longer be explicitly turned on; doing so may cause issues. It is recommended to remove all references to it from your build files.

If you're setting CppStandard to CppStandardVersion.Cpp17 in a Build.cs file... don't :)

Usage

Reference the "UE5Coro" module from your Build.cs as you would any other C++ module, and use #include "UE5Coro.h". The plugin itself does not need to be enabled.

Some functionality is in optional modules that need to be referenced separately. For instance, Gameplay Ability System support needs "UE5CoroGAS" in Build.cs, and #include "UE5CoroGAS.h". The core UE5Coro module only depends on engine modules that are enabled by default.

[!IMPORTANT] Do not directly #include any other header, only the one matching the module's name. Major IDEs used with Unreal Engine are known to get header suggestions wrong. If you add UE5Coro.h to your PCH, you can make it available everywhere.

Updates

To update, delete UE5Coro from your project's Plugins folder, and install the new version using the instructions above.

Packaging

Packaging UE5Coro separately (from the Plugins window) is not needed, and not supported.

Removal

To remove the plugin from your project, reimplement all your coroutines without its functionality, remove all references to the plugin and its modules, and add a core redirect from /Script/UE5CoroK2.K2Node_UE5CoroCallCoroutine to /Script/BlueprintGraph.K2Node_CallFunction.