When creating content, such as scripted open world encounters or specially created content, you may find yourself in a situation where you have an actor that controls an encounter and is responsible for spawning NPCs, and later despawning them when it is done.
However, some actors that do this have an unreliable lifespan: It is possible that they may be destroyed or unloaded while an Async SpawnNPC action is executing. When this happens, the results are undefined and may result in an orphaned NPC hanging around in the world.
NPCSpawnProxy provides a spawnable companion actor that tracks your actor’s lifespan and handles all asynchronous NPC spawning and standardized setup internally. In the event that an NPC spawn completes outside your actor’s lifespan, it handles cleanup on its own.
As a significant bonus, this shifts handling async spawning away from your control blueprint, and makes the most complex setups like spawning 8 of the same NPC at different spawn points trivial to implement.
Please note that the NPCSpawnProxy is in no way a replacement for the existing camp/territory/spawnpoint system. It exists as a tool for custom content and encounters.
The proxy is an actor that automatically manages its own lifespan based on the lifespan of your controller. It needs to be spawned before you will use it. BeginPlay is the perfect spot for this. Note that the proxy only works on the Server, so make sure you only spawn it there.
Class:
/Game/Systems/Spawning/SpawnProxy/BP_NPCSpawnProxy.BP_NPCSpawnProxy
Note: Make sure you pass
Self
as the proxy's Owner. This is how the proxy knows what actor it belongs to.
One of the most common usecases for spawning an NPC is to create a single unique NPC at a specific known location.
With the proxy created, we can start requesting NPC spawns. Creating the request is quite simple, involving just a single function call. It may help to expand out the Params struct inline, as shown in this screenshot:
The returned request object contains the params you requested, as well as the state of the spawn. These can be thrown away when you are done with them (simply clear references to them), or you can keep them for ease later.
The optional Payload parameter will accept any object, and can be used to carry arbitrary data with it. What you do with it is entirely up to you.
When created, a spawn request is inactive and requires binding and then Activation. This may seem a little odd, but it ensures that timing-based edge cases can be avoided.
Most scripts will bind into OnSpawnSuccess and then call Activate:
As soon as Activate is called, the NPC may spawn. As such, it is vital that you bind to OnSpawnSuccess first.
Note that it is a fact of life that NPCs may fail to spawn. It is often out of our control when this happens, but it is typically worth logging or otherwise noting for debugging purposes. This can be tracked with the OnSpawnFailed delegate.
Many encounters will have setup that require many of the same NPC. Typically to make this situation work requires an awkward event that repeatedly calls itself until it has requested and finished setting up all of the NPCs - as async calls cannot be processed in ForLoop or ForEachLoop.
NPCSpawnProxy allows for cleaner implementation that does not have this limitation.
As the spawn requests are not themselves inherently asynchronous actions, but rather point to asynchronous actions, you can operate on them in a tightly nested ForLoop/ForEachLoop. Additionally, you can even work with them in functions, though that is not recommended as it makes tracing the spawn success/failure events very awkward.
In this example, we have 8 spawn points (represented as ArrowComponents in our controller blueprint) and we want to spawn 1 copy of the NPC on each of the 8 spawn points.
Keep in mind that the actual spawning of the NPCs is still asynchronous and may occur in any order.
When using the NPCSpawnProxy, your controller script has some obligations:
Your controller will spawn a fresh NPCSpawnProxy in BeginPlay.
Once an NPC has been successfully spawned and the OnSpawnSuccess delegate has fired, it is YOUR script’s responsibility to clean up the NPC as necessary.
You do not need to explicitly clean up the NPCSpawnProxy. It will automatically clean itself up when your controller script is destroyed or unloaded. In doing so, it will cancel any in-flight spawns and ensure that any dangling spawns that still manage to complete will be immediately cleaned up.
Sometimes you may need to abort spawning NPCs. This process destroys the SpawnProxy when it is done, as it assumes that no further spawning will be needed.
Note that per the above obligations, any NPCs that have had their successful spawn reported are your script’s responsibility to cleanup at this point