Actor Pooling
Last modified: 27 April 2025Combat Pawns can use the Actor Pool by declaring a
NinjaCombatActorPoolComponent
.Combat Pawns with a valid Actor Pool must implement
CombatPoolProviderInterface
.The Actor Pool supports any Actor implementing
CombatPoolableActorInterface
.The
NinjaCombatPoolableActor
is a default implementation that supports replication.The pool is replicated, and pooled actors behave as if they were placed in the map.
It is common for combat systems to require frequent spawning and destroying of actors, such as projectiles or cast actors, in short intervals.
To mitigate the performance cost of constant actor spawning and destruction during gameplay, an Actor Pool was introduced to the Combat System. This pool prepares a predefined number of instances of frequently used actors for each combatant.
The Actor Pool Component
Each combatant has its own Actor Pool, represented by the NinjaCombatActorPoolComponent
. You can configure all the actors that will be pooled and specify how many instances will be available.
Combatants using an Actor Pool must implement the CombatPoolProviderInterface
to provide the component, allowing external functionalities, such as the Projectile Request, to quickly retrieve and use the pool.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Interfaces/CombatPoolProviderInterface.h"
#include "PluginLabsCharacter.generated.h"
class UNinjaCombatActorPoolComponent;
UCLASS()
class PLUGINLABS_API APluginLabsCharacter : public ACharacter, public ICombatPoolProviderInterface
{
GENERATED_BODY()
public:
APluginLabsCharacter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
// -- Begin Pool Provider interface.
virtual UNinjaCombatActorPoolComponent* GetActorPool_Implementation() const override;
// -- End Pool Provider interface.
private:
/** Pool of actors that are frequently used. */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Components", meta = (AllowPrivateAccess = true))
TObjectPtr<UNinjaCombatActorPoolComponent> ActorPool;
};
#include "GameFramework/PluginLabsCharacter.h"
#include "Components/NinjaCombatActorPoolComponent.h"
APluginLabsCharacter::APluginLabsCharacter(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
static const FName ActorPoolName = TEXT("ActorPool");
ActorPool = CreateDefaultSubobject<UNinjaCombatActorPoolComponent>(ActorPoolName);
}
UNinjaCombatActorPoolComponent* APluginLabsCharacter::GetActorPool_Implementation() const
{
return ActorPool;
}
Poolable Actors
Poolable Actors are primarily defined by the CombatPoolableActorInterface
. Any actor implementing this interface can be added to the Actor Pool Component.
It's generally recommended to extend from NinjaCombatPoolableActor
, the default base class, rather than directly implementing the interface. This base actor not only implements the Poolable Actor interface but is also configured to support networking and replication, as if the actor was originally placed in the map.
The Actor Pool Component and the base Poolable Actor work together to reduce the initial network overhead that would otherwise occur when replicating many actors being spawned simultaneously to prepare the pool.
The Actor Pool can be configured in the Actor Pool Component by setting the actor class and the number of instances to initialize.

tip
Pools are Actor Specific
You are configuring an actor pool in the component assigned to each pool owner. This means that the pool size is specific to that actor.
Keep that in mind while defining your pool limits for all pool owners in the scene!
Actor Lifecycle
When you use Poolable Actors, it is important to keep in mind how the Actor lifecycle changes.
Functions like Begin Play and End Play will be called when the Poolable Actor is created and destroyed by the pool, making them inadequate to handle events that should happen whenever the actor is actually added or removed from the world.
Instead, Poolable Actors use methods defined by CombatPoolableActorInterface
:
Activate
: Invoked when a poolable actor is retrieved from the pool and placed in the world.Deactivate
: Invoked when the poolable actor is ready to be returned to the pool.
If you are using the default implementation, NinjaCombatPoolableActor
, those methods are already implemented, and you can simply hook any important events to their respective extension points, available to Blueprints and C++.
OnActivation
: Invoked when the actor is activated on the server and clients.OnDeactivation
: Invoked when the actor is deactivated on the server and clients.
Here are some examples of functionalities that should be handled by these events:
Playing a Particle Effect from the start.
Playing a Sound Effect from the start.
Adding velocity to a projectile.
note
Call Parent/Super
When extending
OnActivation
orOnDeactivation
, make sure to call their parent/super implementations!
Supported Actors
Currently, the following actors are supported by default by the Actor Pool:
Projectiles used by the Attack Ability.
Cast Actors and Targeting Actors used by the Cast Ability.
Marker Actors used by the Target Lock Ability.
If you need to support new objects, you can do so by extending NinjaCombatPoolableActor
, which is recommended, or by implementing CombatPoolableActorInterface
.
Retrieving Actors
To retrieve an actor from the pool, you can use the TryGetActorFromPool
function, from the Actor Pool Function Library. Supported actors listed before are already retrieved using this functionality and require no additional work.
#include "NinjaCombatActorPoolFunctionLibrary.h"
void APluginLabsCharacter::ActivateActor()
{
AActor* PoolableActor = UNinjaCombatActorPoolFunctionLibrary::TryGetActorFromPool(RequestOwner, DesiredClass);
if (IsValid(PoolableActor))
{
// The actor was spawned.
}
else
{
// The pool does not have an instance available.
}
}
note
Logging Pool Behavior
The Actor Pool Component will log messages when an actor is retrieved or when there were no instances available. To track these messages, set the output level of
LogNinjaCombat
to Verbose.