UE5Coro Documentation

repository·master·Indexed 22 days ago

https://github.com/landelare/ue5coro

C++20 coroutine support for Unreal Engine 5 designed to simplify gameplay logic and asynchronous tasks. It enables non-blocking code for asset loading, delays, and thread switching using co_await syntax, integrating with Unreal's latent action system and Blueprints. The library includes specialized modules for Gameplay Ability System (UE5CoroGAS), AI and Navigation (UE5CoroAI), and animation montage/notify awaiters.

Tokens
31.3K
Snippets
86
Records
124
Agent score
77%

What's inside UE5Coro

  1. How aggregate awaiters work in UE5Coro

    master

    Aggregate awaiters allow you to combine multiple awaitables or TCoroutine objects into a single co_await operation. They support expedited cancellation and are designed so that once the first await completes, subsequent awaits of the same result return the value synchronously on the calling thread.

    Threading Behavior

    • Standard Awaiters (WhenAny, WhenAll, Race): The coroutine resumes on a thread corresponding to one of the input parameters or the thread that canceled the coroutine. If all inputs resume on the game thread and no cross-thread cancellation occurs, the aggregate is guaranteed to resume on the game thread.
    • Latent Awaiters (Latent::WhenAny, Latent::WhenAll): These always resume on the game thread.

    Completion

    Completion includes unsuccessful completions (e.g., failures or cancellations).

  2. Use FAwaitableSemaphore for coroutine-compatible semaphore signaling

    master

    FAwaitableSemaphore is a coroutine-friendly replacement for std::counting_semaphore. It does not use an acquire() method; instead, it is directly awaitable, which acquires exactly 1 count.

    Key Behaviors:

    • Thread Safety: All operations are thread-safe.
    • Immovability: Objects are immovable. Use smart pointers if you need to move or copy them.
    • Cancellation: Supports expedited cancellation, processed on the same thread that calls Cancel().
    • Limitations: Does not support interprocess usage (unlike some Unreal FSemaphore types).
    // Example conceptual usage
    FAwaitableSemaphore MySemaphore(1, 1); // Capacity 1, Initial 1
    
    // In a coroutine (acquires 1 count)
    co_await MySemaphore;
    
    // In another thread (releases 1 count)
    MySemaphore.Unlock();
  3. Use FAwaitableEvent for coroutine-compatible event signaling

    master

    FAwaitableEvent is a coroutine-friendly replacement for Unreal Engine's FEvent/FEventRef. Unlike standard events, it does not use Wait() functions; instead, it is directly awaitable.

    Key Behaviors:

    • Thread Safety: All operations are thread-safe.
    • Resumption: When Trigger() is called, eligible awaiting coroutines are resumed directly from the caller's thread.
    • Immovability: Objects are immovable. Use smart pointers if you need to move or copy them.
    • Cancellation: Supports expedited cancellation, processed on the same thread that calls Cancel().

    Event Modes:

    • EEventMode::AutoReset (Default): The event clears itself after a trigger. Only one coroutine is guaranteed to resume per Trigger() call.
    • EEventMode::ManualReset: Allows all currently awaiting coroutines to pass through. Subsequent awaits after a Reset() will suspend.
    // Example conceptual usage
    FAwaitableEvent MyEvent(EEventMode::AutoReset);
    
    // In a coroutine
    co_await MyEvent;
    
    // In another thread
    MyEvent.Trigger();
  4. Understand Blueprint support for Coroutine UFUNCTION calls

    master

    UE5Coro includes a custom K2Node to improve the visual experience of calling Coroutine UFUNCTIONs in Blueprints.

    Key behaviors:

    • Visual Cleanup: It removes unnecessary pins from the node to keep the graph clean.
    • Automatic Patching: It performs runtime patching on UFunctions so you do not need to manually mark them with BlueprintInternalUseOnly.
    • Editor Tooltip: The node is labeled with the tooltip "Call Coroutine" to distinguish it from standard function calls.
    • Performance: There is no performance penalty in Shipping builds because K2Nodes are editor-only.

    Note on Removal: If you decide to remove UE5Coro from your project later, you can simply delete this class. However, if you do, you must implement a core redirect back to K2Node_CallFunction to maintain Blueprint compatibility.

  5. How async collision queries work in UE5Coro

    master

    Async collision queries are provided in the UE5Coro::Latent namespace. These functions wrap standard Unreal Engine collision queries (LineTrace, Sweep, and Overlap) and return objects that satisfy the TLatentAwaiter concept.

    When you co_await one of these functions, the coroutine suspends and resumes automatically once the asynchronous collision query is completed.

    Key Behaviors:

    • Overlapping Queries: You can start multiple async queries before awaiting any of them to increase throughput.
    • Synchronous Completion: Awaiting a query that has already finished will resume the coroutine synchronously.
    • Lvalue vs Rvalue Awaiting: The type of the result depends on whether you await an lvalue or an rvalue. Awaiting an rvalue (using std::move) is more efficient as it returns the TArray by value (moving it), whereas awaiting an lvalue returns a const reference to the internal array.
    using namespace UE5Coro::Latent;
    
    // The simplest usage effortlessly avoids all copies:
    TArray<FHitResult> Result1 = co_await AsyncLineTraceByObjectType(this, /*...*/);
    
    // Three overlapped queries
    auto Query2 = AsyncLineTraceByChannel(this, /*...*/);
    auto Query3A = AsyncSweepByProfile(this, /*...*/);
    auto Query3B = Query3A;
    TArray<FOverlapResult> Result4 = co_await AsyncOverlapByChannel(this, /*...*/); // Move
    
    TArray<FHitResult> Result2A = co_await Query2; // Copy
    const TArray<FHitResult>& Result2B = co_await Query2; // Reference into Query2
    TArray<FHitResult> Result3 = co_await std::move(Query3A); // Move from Query3A
    // Query3A and Query3B are now invalid
  6. How promises work in UE5Coro

    master

    UE5Coro uses specialized std::coroutine_traits to inspect arguments like FLatentActionInfo, FForceLatentCoroutine, and TLatentContext to determine the execution mode. This allows the library to parameterize TCoroutinePromise accordingly.

    Core promise types (located in UE5Coro::Private):

    • FPromise: Implements cancellation, ContinueWith, and exception handling. It forces reaction to unhandled C++ exceptions.
    • FPromiseExtras: Holds data with a lifetime different from FPromise (e.g., result storage). TCoroutine and FPromise hold shared_ptrs to this.
    • FAsyncPromise: A trivial implementation for async mode (callback-based).
    • FLatentPromise: Manages ownership transfer between the coroutine and the Unreal latent action manager.
    • TCoroutinePromise<T, Base>: The standard promise type for TCoroutines, inheriting from FPromise or FAsyncPromise and adding return type support.
    • TAbilityPromise: A specialized version for UE5CoroGAS, parameterized by the owning class.
  7. How FLatentAwaiter works for polling/ticking

    master

    The FLatentAwaiter is a base class for awaiters that poll or tick on the game thread. It is not derived from TAwaiter.

    Key Characteristics:

    • State Storage: Uses a generic pointer or 64 bits of storage (e.g., a double) to avoid heap allocations.
    • Async/Latent Interaction: Awaiting a FLatentAwaiter with an FAsyncPromise creates a latent action in the world to tick the awaiter. This means the latent action manager temporarily owns the async coroutine.
    • Latent/Latent Fast Path: FLatentPromise has a fast path for co-awaiting another FLatentPromise, bypassing the promise/coroutine to poll the FPendingLatentCoroutine directly.
    • Subclassing Constraints: Derived objects must maintain the same sizeof as the base to avoid object slicing when copied into the promise.
    • Implementation: To define a simple latent awaiter with no return value, initialize it with an arbitrary state and a function pointer that is called once on co_await and once per tick. The third constructor argument should be std::true_type() if the function is sensitive to world changes, or std::false_type() otherwise.
  8. Use Animation awaiters in UE5Coro::Anim

    master

    The UE5Coro::Anim namespace provides awaitable functions to interact with Unreal Engine montages and notifies from the game thread. These are useful for gameplay logic that needs to react to animation state changes or specific notifies without implementing boilerplate UFUNCTION callbacks.

    Important Usage Constraints:

    • The return values of these functions may be copied, but only one copy may be awaited at a time.
    • Do not reuse returned values; subsequent awaits are not guaranteed to return a valid value.
    • For functions returning pointers (like PlayMontageNotifyBegin), the pointer points to engine-managed memory and has a limited lifetime. It is only valid until the next co_await or co_return.
  9. How FLatentPromise manages ownership and detaching

    master

    The FLatentPromise bridges latent TCoroutines and their backing latent actions.

    Key Behaviors:

    • Initial Suspend: Mimics Blueprint behavior; it will NOT start execution if a similar latent action is already running. If clear, it creates an FPendingLatentCoroutine registered with the world.
    • Detaching: To allow seamless multithreading, FLatentPromise can "detach" from the game thread. This ensures the coroutine can reach its next co_await safely even if the latent action manager attempts to delete the pending action on the game thread.
    • Resumption: Any awaiter that detaches a FLatentPromise must guarantee that FPromise::Resume is called. This call determines if ownership returns to the game thread/latent action manager or remains detached.
    • Final Suspend: Reaching final_suspend always re-attaches the promise to the game thread.
  10. Understand UE5Coro assertion behavior

    master

    UE5Coro uses aggressive assertions to catch memory and asynchronous execution bugs early. When debugging, pay attention to the following macro types:

    • ensureMsgf: Indicates a likely error that the plugin has handled. It will not crash the engine but should be investigated.
    • checkf: Indicates a definite error that will likely cause a crash.
    • checkf(..., "Internal error: ..."): Indicates a bug within the UE5Coro plugin itself.
  11. Understand UE5Coro cancellation model

    master

    UE5Coro implements a simplified, cooperative cancellation model. To avoid the massive overhead of a universal cancellation system, cancellation processing only occurs during co_await when the coroutine is in a known, safe state.

    Key Characteristics:

    • Cooperative: Cancellation is checked when a coroutine resumes.
    • Expedited Cancellation: Awaiters that can handle faster cancellation can register themselves by calling FPromise::RegisterCancelableAwaiter.
  12. Use TGenerator for yielding values

    master

    Returning TGenerator<T> from a function allows it to yield an arbitrary number of values (including infinite) using co_yield. The caller controls when and how many values to fetch. This is more memory-efficient than allocating a TArray because values are generated on demand.

    Note on Deprecation: TGenerator is deprecated when targeting C++23 or later. It is recommended to use std::generator instead. To suppress deprecation warnings, define the macro UE5CORO_DISABLE_GENERATOR_DEPRECATION.

    Lifecycle Behaviors:

    • A default-constructed TGenerator yields no elements.
    • Moving a generator transfers the execution state to the new object; the moved-from object becomes identical to a default-constructed one.
    using namespace UE5Coro;
    
    TGenerator<int> Example()
    {
        co_yield 1;
        co_yield 2;
        co_yield 3;
    }