UE 5 Truly Asynchronous Actor Spawning With C++

I am new to Unreal Engine 5 and I am in the process of writing a specific Blueprint code into C++, to achieve asynchronous spawning. So far, I’ve written the node to take a custom array, example below, and spawn the actor with the applied transformation.

[[RoomClass1, Location1, Rotation2],[RoomClass1, Location1, Rotation2]]

The node is shown below:

The code that cycles through the array:

AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this, World, LatentInfo, SpawnDataArray, &OutSpawnedActors]()
    {
        TArray<AActor*> LocalSpawnedActors;

        // Perform the actual spawning on the game thread
        AsyncTask(ENamedThreads::GameThread, [this, World, LatentInfo, SpawnDataArray, &OutSpawnedActors, LocalSpawnedActors = MoveTemp(LocalSpawnedActors)]() mutable
            {
                for (const FRoomSpawnData& Data : SpawnDataArray)
                {
                    if (Data.RoomClass)
                    {
                        FActorSpawnParameters SpawnParams;
                        SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;

                        AActor* SpawnedActor = World->SpawnActor<AActor>(Data.RoomClass, Data.Location, Data.Rotation, SpawnParams);
                        if (SpawnedActor)
                        {
                            LocalSpawnedActors.Add(SpawnedActor);
                        }
                    }
                }

                // Update SpawnedActors
                SpawnedActors = MoveTemp(LocalSpawnedActors);
                OutSpawnedActors = SpawnedActors; // Pass references back
                OnCompleted.Broadcast(SpawnedActors); // Notify listeners

                // Resume the latent action
                if (LatentInfo.CallbackTarget)
                {
                    FLatentActionManager& LatentManager = World->GetLatentActionManager();
                    auto* LatentAction = LatentManager.FindExistingAction<FAsyncBatchSpawnLatentAction>(LatentInfo.CallbackTarget, LatentInfo.UUID);
                    if (LatentAction)
                    {
                        LatentAction->OutSpawnedActors = SpawnedActors; // Update latent action output
                    }
                }
            });
    });

Is there a real way to implement asynchronous spawning of actors? As currently each actor takes X amount of time to complete and it would be much better to spawn a few actors at the same time. The time it would take to initialize the world would be less.

Any help is appreciated!

You’re still doing all the “expensive” work on the game thread. You’re not really doing it asynchronously, you’ve just moved it to a different part of the game thread execution to do serially.

If I were trying to do this, I would use the BlueprintAsyncActionBase instead of the LatentInfos. Then I’d make my Async action tickable (by also inheriting from FTickableGameObject). During that tick, I would spawn some subset of SpawnDataArray. That could be N per frame or you could do timings so that you don’t spend more than X ms per tick on room spawning. Once all the actors are spawned, call OnComplete.

“Splatting” work across multiple frames is something that comes up enough that I wrote my own generalized utility for it. You can check it out here in my Starfire Utilities plugin. But that’s geared toward an entirely C++ workflow/trigger and not any sort of blueprint integration.

Of course all of this would be assuming that I had actually profiled this or had some actual reason to do it. If you’re spawning the rooms that the player(s) will move through, you’ve probably got to spawn all of those before you can actually start playing. Which probably means that you’re doing it behind a loading screen and you don’t really need to do things asynchronously behind a loading screen.

1 Like

Thank you for your help. I did not even think about BlueprintAsyncActionBase. I’ll surely look into this!
Your utility looks interesting, I appreciate showing it.

The initial plan was to spawn the rooms during a loading screen. The issue is that if I would want to make a world with 100 or more rooms, the loading time would increase linearly. This is not such a problem for a player with a fast CPU, but I would say that dividing the work into async would speed up the loading process. Instead of spawning 1 room synchronously, it would drastically help to reduce the loading time by spawning 4 or 8 rooms at a time.

If there is no way of actually spawning actors in async, I should probably divide the work during the gameplay (spawning some of the rooms in-game).

In that case, you’re probably looking for a more complicated loading solution that can manage what rooms are loaded based on the room the player is currently in and where you might be expecting them to go. This way you also limit the resource requirements of having rooms that aren’t in use loaded while you don’t need them. But this is a somewhat different problem than what a single async blueprint node could ever accomplish. You’d be looking for a world subsystem at that point that is continually doing work to manage what rooms exist.

Open world games like GTA and Saint’s Row do this sort of thing. World Partition in the Engine is also a version of this. You can easily roll your own “streaming” solution to spawn and despawn rooms as you need them.