Version: 1.0
Author: NeelFrostrain
Engine Support: Unreal Engine 5.x
License: See LICENSE file
| Document | Purpose | Read Time |
|---|---|---|
| INDEX.md | Complete documentation index & navigation | 5 min |
| QUICK_START.md | Get started in 5 minutes | 5 min |
| HOW_TO_USE.md | Step-by-step usage guide | 20 min |
| HOW_IT_WORKS.md | Technical architecture & deep dive | 40 min |
| BLUEPRINT_GUIDE_DETAILED.md | Complete Blueprint guide | 30 min |
| API_REFERENCE.md | Complete API documentation | Reference |
๐ START HERE: See Documentation/INDEX.md for complete navigation guide!
Object pooling is a performance optimization pattern that reuses objects instead of constantly creating and destroying them. Instead of:
Traditional: Spawn โ Use โ Destroy โ [Expensive!]
Pooling: Spawn Once โ Reuse โ Return โ [Fast!]
Without Pooling:
- Allocate memory, initialize, add to world
- Frame rate spike โ
With Pooling:
- Reuse 100 pre-created actors
- Instant spawn โ
- โ World Subsystem Integration - Automatic per-world management
- โ Expandable Pools - Grow when needed (with constraints)
- โ Batch Processing - Spread spawning/destruction over frames
- โ Blueprint Compatible - Full Blueprint & C++ support
- โ Soft Class Loading - Efficient lazy class loading
- โ Lifetime Management - Auto-return after configurable duration
- โ Event System - Customize pool behavior with events
- โ Multiple Pools - Unlimited pools for different actor types
- ๐ Smooth Frame Rate - No GC spikes from allocations
- ๐พ Memory Efficient - Prevents fragmentation
- โก Instant Spawning - O(1) retrieval from pool
- ๐ Scalable - Handle hundreds of actors
- ๐ฎ Responsive - Better gameplay feel
1. Copy ObjectPoolingSubsystem/ to YourProject/Plugins/
2. Right-click .uproject โ Generate Visual Studio project files
3. Build and open in Unreal Engine
4. Plugin auto-enables โ
C++ Header:
#include "GameFramework/Actor/ObjectPoolingEntry.h"
UCLASS()
class YOURPROJECT_API AMyPooledActor : public AObjectPoolingEntry
{
GENERATED_BODY()
protected:
virtual void OnInitializeInPool_Implementation() override;
virtual void OnSpawnActor_Implementation(float Lifetime, bool bForceReturnToPool) override;
virtual void OnDespawnActor_Implementation() override;
};Or Blueprint: Inherit from AObjectPoolingEntry and override events in Blueprint
C++ (In Game Mode BeginPlay):
UObjectPooling::SpawnActorsPoolListByClass(
this,
AMyPooledActor::StaticClass(),
100, // Initial
300, // Max
true, // Expandable
0.02f // Spawn rate
);Or Blueprint: Call "Spawn Actors Pool List By Class" node in Event BeginPlay
C++:
AObjectPoolingEntry* PooledActor = nullptr;
UObjectPooling::SpawnActorFromActorListByClass(
this,
AMyPooledActor::StaticClass(),
FTransform(Location),
PooledActor,
nullptr,
true, // Auto-return
5.0f // 5 second lifetime
);
if (PooledActor)
{
// Use the actor
}Or Blueprint: Call "Spawn Actor From Pool" node and check if valid
Done! You now have object pooling working! ๐
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Unreal Engine World โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ UObjectPooling_Subsystem โ โ
โ โ (World Subsystem) โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ Pool Container (TMap) โ โ โ
โ โ โ Class โ FObjectPoolList โ โ โ
โ โ โ โโ Actors (Array) โ โ โ
โ โ โ โโ Size / Max โ โ โ
โ โ โ โโ Expandable (bool) โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ Spawn Queue (Async) โ โ โ
โ โ โ Destroy Queue (Async) โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โ Timers: โ โ
โ โ - TickPoolSpawning (0.02s) โ โ
โ โ - TickDestroyingActors (0.02s) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ AObjectPoolingPlaceholder โ โ
โ โ (Hidden attachment parent) โ โ
โ โ Contains pooled actors as children โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
1. CREATE POOL
User Request โ Add to Queue โ TickPoolSpawning โ
Load Class โ Spawn Batches โ Store in Container
2. SPAWN ACTOR
User Request โ Search Container โ
Found Free โ Activate โ Return Actor
Not Found โ Expand if allowed โ Return Actor or Null
3. RETURN ACTOR
Called by User or Timer โ Reset State โ Mark Free โ
Hide/Disable โ Attach to Placeholder
4. DESTROY POOL
User Request โ Add to Destroy Queue โ TickDestroyingActors โ
Destroy Batches โ Remove from Container
Location: Source/ObjectPoolingSubsystem/Public/Core/ObjectPooling_Subsystem.h
Purpose: Main pool manager for the world
Key Methods:
// Create a pool
virtual void SpawnActorPoolListBySoftClass(
const TSoftClassPtr<AObjectPoolingEntry> EntryClass,
int32 SpawnAmount = 10,
int32 MaxSpawnAmount = 100,
bool bExtendOnEmpty = false,
float SpawnTimerRate = 0.01f
);
// Spawn from pool
virtual void SpawnActorFromActorListBySoftClass(
const TSoftClassPtr<AObjectPoolingEntry> ActorClass,
AObjectPoolingEntry*& OutActor,
const FTransform SpawnTransform,
AActor* NewOwner = nullptr,
bool bForceReturnAfterLifetimeExpire = false,
float Lifetime = 10.f
);
// Destroy pool
virtual void DestroyActorListBySoftClass(
const TSoftClassPtr<AObjectPoolingEntry> ActorClass,
float DestroyingTimerRate = 0.01f
);
// Query
int32 FindFreeEntryOnContainer(const TSoftClassPtr<AObjectPoolingEntry> EntryKey) const;
const TMap<...>& GetObjectActorPoolContainer() const;Properties:
int32 SpawnPerBatch = 40; // Actors/frame during spawn
int32 DestroyPerBatch = 40; // Actors/frame during destroyLocation: Source/ObjectPoolingSubsystem/Public/GameFramework/Actor/ObjectPoolingEntry.h
Purpose: Base class for all poolable actors (MUST inherit from this)
Key Methods:
// Check state
bool IsFreeInPool() const;
// Lifecycle (called automatically)
virtual void InitializeInPool(); // Reset to pool state
virtual void SpawnActor(float Lifetime, bool bForceReturnToPool); // Activate
virtual void DespawnActor(); // Return to pool
// Override these in your actor
UFUNCTION(BlueprintNativeEvent)
void OnInitializeInPool(); // Pool state setup
UFUNCTION(BlueprintNativeEvent)
void OnSpawnActor(float Lifetime, bool bForceReturnToPool); // Activate setup
UFUNCTION(BlueprintNativeEvent)
void OnDespawnActor(); // CleanupProperties:
bool bIsFreeInPool; // In pool? (true/false)
FTimerHandle TH_ReturnToPool; // Lifetime timerLocation: Source/ObjectPoolingSubsystem/Public/GameFramework/Actor/ObjectPoolingPlaceholder.h
Purpose: Hidden parent actor for pooled actors while inactive
Features:
- Invisible billboard component (editor visibility)
- Keeps pooled actors organized
- Hidden during gameplay
Location: Source/ObjectPoolingSubsystem/Public/GameFramework/Lib/ObjectPooling.h
Purpose: Convenience wrappers for subsystem functions
Static Functions:
// Wrappers around subsystem
static void SpawnActorsPoolListByClass(...);
static void SpawnActorFromActorListByClass(...);
static void DestroyActorListByClass(...);
static AObjectPoolingPlaceholder* GetObjectPoolingPlaceholder(...);
static void CleanupRedundantPlaceholders(...);struct FObjectPoolList
{
TArray<TObjectPtr<AObjectPoolingEntry>> ObjectList; // Pool actors
int CurrentPoolSize; // Current count
int MaxSize; // Max allowed
bool bExpandable; // Can grow
};struct FPendingPoolTask
{
TSoftClassPtr<AObjectPoolingEntry> EntryClass; // Class to pool
int32 Remaining; // Actors left to spawn
int32 MaxSize; // Pool max
bool bExpandable; // Allow expansion
};ObjectPoolingSubsystem/
โ
โโโ Documentation/ โ โญ START HERE!
โ โโโ INDEX.md โ Navigation hub
โ โโโ QUICK_START.md โ 5-min setup
โ โโโ BLUEPRINT_GUIDE_DETAILED.md โ Detailed Blueprint guide
โ โโโ BLUEPRINT_GUIDE.md โ Blueprint overview
โ โโโ HOW_TO_USE.md โ Complete usage guide
โ โโโ HOW_IT_WORKS.md โ Technical deep dive
โ โโโ API_REFERENCE.md โ All functions & classes
โ โโโ FAB_AUDIT_REPORT.md โ Marketplace compliance
โ
โโโ Source/ObjectPoolingSubsystem/
โ โโโ Public/
โ โ โโโ Core/
โ โ โ โโโ ObjectPooling_Subsystem.h
โ โ โโโ GameFramework/
โ โ โ โโโ Actor/
โ โ โ โ โโโ ObjectPoolingEntry.h
โ โ โ โ โโโ ObjectPoolingPlaceholder.h
โ โ โ โโโ Lib/
โ โ โ โโโ ObjectPooling.h
โ โ โโโ Structs/
โ โ โ โโโ ObjectPoolStruct.h
โ โ โ โโโ ObjectPoolEnum.h
โ โ โโโ ObjectPoolingSubsystem.h
โ โ
โ โโโ Private/
โ โโโ ObjectPoolingSubsystem.cpp
โ โโโ Core/
โ โ โโโ ObjectPooling_Subsystem.cpp
โ โโโ GameFramework/
โ โ โโโ Actor/
โ โ โ โโโ ObjectPoolingEntry.cpp
โ โ โ โโโ ObjectPoolingPlaceholder.cpp
โ โ โโโ Lib/
โ โ โโโ ObjectPooling.cpp
โ
โโโ Binaries/ (Compiled binaries)
โโโ Config/
โ โโโ DefaultObjectPoolingSubsystem.ini
โ โโโ FilterPlugin.ini (FAB marketplace filtering)
โโโ Content/ (Content - empty)
โโโ Resources/Icon128.png (Marketplace icon)
โโโ README.md (This file - overview)
โโโ LICENSE (MIT License)
โโโ ObjectPoolingSubsystem.uplugin (Plugin manifest)
๐ All detailed documentation is in the Documentation/ folder! โโโ LICENSE License
All detailed documentation is in the Documentation/ folder.
Start here: Documentation/INDEX.md for complete navigation guide!
| Goal | Document | Location | Time |
|---|---|---|---|
| Get started now | QUICK_START.md | ๐ Doc | 5 min |
| Use only Blueprints | BLUEPRINT_GUIDE_DETAILED.md | ๐ Doc | 30 min |
| Learn everything step-by-step | HOW_TO_USE.md | ๐ Doc | 30 min |
| Understand internals | HOW_IT_WORKS.md | ๐ Doc | 45 min |
| Look up specific API | API_REFERENCE.md | ๐ Doc | Reference |
| Find any document | INDEX.md | ๐ Doc | 5 min |
| FAB Compliance | FAB_AUDIT_REPORT.md | ๐ Doc | 15 min |
๐จโ๐ฎ Blueprint Designers (30 min):
- Documentation/QUICK_START.md - Get working in 5 minutes
- Documentation/BLUEPRINT_GUIDE_DETAILED.md - Detailed Blueprint workflow
- This README - Understand concepts
๐จโ๐ป C++ Programmers (60 min):
- Documentation/HOW_TO_USE.md - Learn all patterns
- Documentation/HOW_IT_WORKS.md - Understand internals
- Documentation/API_REFERENCE.md - Reference specific functions
๐จโ๐ซ Technical Leads:
- Documentation/FAB_AUDIT_REPORT.md - Compliance verification
- Documentation/HOW_IT_WORKS.md - Technical architecture
- Documentation/API_REFERENCE.md - API evaluation
Base Actor: ~200-500 bytes
Components: Variable (mesh, collision, etc)
Pooling Overhead: Minimal (~1% extra)
Example (500 pooled actors):
500 ร 1 KB average = ~500 KB fixed
(Compare to: constant allocation/deallocation overhead)
No Pooling (100 actors/frame):
Allocate: 2ms
Initialize: 3ms
Add to World: 1ms
Total: 6ms/frame [SPIKE!]
With Pooling (100 actors/frame):
Retrieve from pool: 0.1ms
Activate: 0.2ms
Total: 0.3ms/frame [SMOOTH!]
Scenario: Spawn 1000 projectiles
Without Pooling: ~100ms spike
With Pooling (40/batch, 0.02s rate): 25 frames (~417ms smooth)
// Setup (BeginPlay)
UObjectPooling::SpawnActorsPoolListByClass(
this, AProjectile::StaticClass(), 100, 300, true, 0.02f
);
// Usage
void AWeapon::Fire(FVector Direction)
{
AObjectPoolingEntry* ProjPtr = nullptr;
UObjectPooling::SpawnActorFromActorListByClass(
this, AProjectile::StaticClass(),
FTransform(MuzzleLocation), ProjPtr, this
);
if (AProjectile* Proj = Cast<AProjectile>(ProjPtr))
{
Proj->Launch(Direction * 2000.0f);
}
}
// Return to pool (on hit)
void AProjectile::OnHit(const FHitResult& Hit)
{
ApplyDamage();
DespawnActor(); // Back to pool
}void AGameMode::PlayExplosion(FVector Location)
{
AObjectPoolingEntry* FXPtr = nullptr;
UObjectPooling::SpawnActorFromActorListByClass(
this, AExplosionEffect::StaticClass(),
FTransform(Location), FXPtr, nullptr,
true, // Auto-return
3.0f // 3 second lifetime
);
// Auto-cleans up after 3 seconds!
}void AGameMode::SpawnWave(int32 Count)
{
for (int i = 0; i < Count; i++)
{
AObjectPoolingEntry* EnemyPtr = nullptr;
UObjectPooling::SpawnActorFromActorListByClass(
this, AEnemy::StaticClass(),
FTransform(GetRandomSpawn()), EnemyPtr
);
if (AEnemy* Enemy = Cast<AEnemy>(EnemyPtr))
{
Enemy->Initialize();
}
}
}
void AGameMode::OnEnemyDeath(AEnemy* DeadEnemy)
{
DeadEnemy->DespawnActor(); // Return to pool
}void AGameMode::SpawnCoins(FVector Location, int32 Count)
{
for (int i = 0; i < Count; i++)
{
AObjectPoolingEntry* CoinPtr = nullptr;
FVector CoinLoc = Location + FMath::RandPointInRadius(100.0f);
UObjectPooling::SpawnActorFromActorListByClass(
this, ACoin::StaticClass(),
FTransform(CoinLoc), CoinPtr
);
}
}Initial Size = Average concurrent actors used
Max Size = 2-10x initial size (depends on variability)
Examples:
- Stable projectiles: 100 initial, 300 max
- Variable effects: 50 initial, 200 max
- Unpredictable enemies: 30 initial, 150 max
SpawnPerBatch = 40 (default)
โโ Increase (50-100): Faster pool setup, higher frame spike
โโ Decrease (10-30): Slower setup, smoother frame rate
DestroyPerBatch = 40 (default)
โโ Recommendations same as SpawnPerBatch
SpawnTimerRate = 0.01s (100 Hz, default)
โโ Lower (0.005): Faster but more overhead
โโ Default (0.01): Balanced
โโ Higher (0.05): Slower but less overhead
Calculation: Time to spawn N actors = (N / SpawnPerBatch) ร SpawnTimerRate
Example: 1000 actors at 40/batch, 0.02s = 25 batches ร 0.02s = 0.5s smooth spread
Causes:
- Pool wasn't created yet
- Pool exhausted and not expandable
- Actor class failed to load
Solutions:
// Ensure pool created first (in BeginPlay!)
UObjectPooling::SpawnActorsPoolListByClass(this, YourClass, 100, 300, true, 0.02f);
// Check for valid actor
if (PooledActor)
{
// Use it
}
else
{
// Pool exhausted - increase size or enable expandable
}Cause: OnSpawnActor_Implementation not properly hiding/showing
Solution:
void AMyActor::OnSpawnActor_Implementation(float Lifetime, bool bForceReturnToPool)
{
// System already called SetActorHiddenInGame(false)
// Verify visibility
if (IsHidden())
{
SetActorHiddenInGame(false);
}
}Cause: Too many actors created per frame
Solution:
// Reduce batch size
UObjectPooling::SpawnActorsPoolListByClass(
this, ActorClass, 100, 300, true,
0.05f // Slower (2x) โ smoother spawning
);
// OR in Project Settings: Reduce SpawnPerBatch from 40 to 20Cause: Expandable pool growing without limit
Solution:
UObjectPooling::SpawnActorsPoolListByClass(
this, ActorClass,
50, // Start small
200, // Set realistic max
true, // Limited expansion
0.02f
);- โ Create pools during BeginPlay (not runtime)
- โ Always check if OutActor is valid before using
- โ Use auto-return for temporary objects (effects, projectiles)
- โ Use manual despawn for interactive objects (enemies, items)
- โ Set realistic max pool sizes
- โ Monitor frame rate during development
- โ Clean up pools when levels transition
- โ Create pools during gameplay
- โ Forget null checks on spawned actors
- โ Set unlimited max sizes
- โ Spawn thousands of actors at once
- โ Leave pools uncleaned on level exit
- โ Use pooling for rarely-spawned objects (overthinking)
๐ Documentation Files (All in Documentation/ folder)
| File | Purpose | Link |
|---|---|---|
| INDEX.md | Navigation hub | ๐ |
| QUICK_START.md | Get started in 5 minutes | ๐ |
| BLUEPRINT_GUIDE_DETAILED.md | Detailed Blueprint guide | ๐ |
| HOW_TO_USE.md | Complete usage guide | ๐ |
| HOW_IT_WORKS.md | Technical architecture | ๐ |
| API_REFERENCE.md | Complete API documentation | ๐ |
| FAB_AUDIT_REPORT.md | Marketplace compliance | ๐ |
- Start with: Documentation/INDEX.md for complete navigation
- Get started: Documentation/QUICK_START.md (5 minutes)
- Learn patterns: Documentation/HOW_TO_USE.md
- Reference API: Documentation/API_REFERENCE.md
- Troubleshoot: See "Troubleshooting" section in Documentation/HOW_TO_USE.md
- Read: Documentation/QUICK_START.md (5 minutes)
- Create: A poolable actor blueprint
- Initialize: A pool in BeginPlay
- Test: Spawning and returning actors
- Implement: Your game mechanics
- Monitor: Performance and tune as needed
- Reference: Documentation/ for detailed information
Copyright (c) 2026 Neel Frostrain. All Rights Reserved.
See LICENSE file for details.
The ObjectPoolingSubsystem is now ready to use. Start with QUICK_START.md and enjoy optimized performance!
Happy pooling! ๐