Runtime PCG post-generation callback
PCG gives us a couple options for creating callbacks when a graph or task has finished executing. The first is to supply a function name in the "Post Generate Function Names" editable array on the PCG component itself, which, as the name suggests, executes that function when the graph has finished all tasks. Unfortunately, this method seems intended for the non-runtime PCG generation modes. If the PCG generation trigger is set to "Generate at Runtime", the post-generate function will not be called.
The second method for creating a callback is to use the "Execute Blueprint" node inside the PCG graph itself. You can create a new blueprint class of type "PCG Blueprint Element", then select this class in the dropdown menu of the Execute Blueprint node. Then you can write your post-generation logic inside the new class. The nice thing about this option is that the Execute Blueprint node can be added to any section of your graph, so you can have callbacks after specific PCG nodes have finished executing.
While the Execute Blueprint solution does work with runtime generation, it doesn't play nicely with graphs using hierarchical generation. Since the PCG volume is split into multiple cells that generate independently, the callback will trigger whenever any cell reaches the node, instead of triggering when all cells have reached it. This is undesirable in cases where you might want to display a loading screen that persists until all runtime PCG tasks have completed.
In the case of a loading screen, we want a callback that works with runtime generation and fires once after all PCG cells have finished executing all tasks. I could not find a simple way to do this using blueprints, but after examining the UPCGSubsystem engine class, I found a function "IsAnyGraphCurrentlyExecuting()" which we can leverage for this purpose.
We can poll the result of UPCGSubsystem->IsAnyGraphCurrentlyExecuting() inside a latent node to get a function that waits until runtime PCG generation has completed. To do this, I use the UE5Coro library, as it drastically simplifies the boilerplate of setting up latent BP nodes and function polling.
Function header:
UFUNCTION(BlueprintCallable, Category = "PCG", Meta = (Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject", DisplayName = "Wait Until PCG Completes"))
static FVoidCoroutine WaitUntilPCGComplete(FLatentActionInfo LatentInfo, const UObject* WorldContextObject, float& OutTime);Function body:
FVoidCoroutine UGameUtilities::WaitUntilPCGComplete(FLatentActionInfo LatentInfo, const UObject* WorldContextObject, float& OutTime)
{
if (!WorldContextObject) co_return;
co_await Latent::NextTick(); // wait one frame to ensure PCG has started
const double StartTime = FPlatformTime::Seconds();
// wait until the graph is no longer executing
if (const UPCGSubsystem* PCGSubsystem = WorldContextObject->GetWorld()->GetSubsystem<UPCGSubsystem>())
{
co_await Latent::Until([&]
{
return !PCGSubsystem->IsAnyGraphCurrentlyExecuting();
});
}
const double EndTime = FPlatformTime::Seconds();
OutTime = EndTime - StartTime;
co_return;
}This gives us a BP node we can use to delay graph execution until the runtime PCG graph and all hierarchical cells have finished generating.
Last updated