https://github.com/mistyuk/poolmaster
High-performance Unity pooling for Unity 6.0–6.4 (Built-in/URP/HDRP). ObjectPooling, render pipeline agnostic.
https://github.com/mistyuk/poolmaster
csharp gamedev hdrp object-pooling performance pooling render-pipeline unity unity3d unity6 urp
Last synced: about 2 months ago
JSON representation
High-performance Unity pooling for Unity 6.0–6.4 (Built-in/URP/HDRP). ObjectPooling, render pipeline agnostic.
- Host: GitHub
- URL: https://github.com/mistyuk/poolmaster
- Owner: mistyuk
- License: mit
- Created: 2025-12-20T01:45:02.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2025-12-20T19:26:04.000Z (6 months ago)
- Last Synced: 2025-12-22T11:40:11.562Z (6 months ago)
- Topics: csharp, gamedev, hdrp, object-pooling, performance, pooling, render-pipeline, unity, unity3d, unity6, urp
- Language: C#
- Homepage: https://github.com/mistyuk/PoolMaster
- Size: 162 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# PoolMaster
**PoolMaster** is a high-performance, production-ready object pooling system for Unity. Designed for both 2D and 3D games, it provides a comprehensive solution for managing GameObject lifecycles efficiently, reducing GC pressure, and maximizing runtime performance.
[](https://unity.com/)
[](LICENSE)
---
### Getting Started
For the fastest setup, start with the No‑Code Quick Start: [Documentation/no-code-quick-start.md](Documentation/no-code-quick-start.md)
---
## Features
- Zero-allocation pooling — minimal GC, fast hot paths
- Type-safe API — generic Pool with safety
- Batch ops — spawn/despawn many at once
- Command buffers — thread-safe enqueue, main-thread flush
- Diagnostics — real-time metrics + editor window
- Configurable — precise control over pool behavior
- Events — decoupled, opt-in hooks
- Collection pooling — reuse lists/dicts/sets
- Easy integration — IPoolable + helpers
## Compatibility
- Supported Unity: 6.0 – 6.4 (stable)
- Render Pipelines: Built-in, URP, HDRP
## Links
- Add me on Discord: [misty2023](https://discord.com/users/misty2023)
## Installation
### Option 1: Unity Package Manager (Recommended)
1. Open Package Manager (`Window > Package Manager`)
2. Click `+` and select `Add package from git URL`
3. Enter: `https://github.com/mistyuk/PoolMaster.git`
### Option 2: Manual Installation
1. Download the latest release from [GitHub](https://github.com/mistyuk/PoolMaster/releases)
2. Extract to your `Assets/Plugins/PoolMaster` folder
---
## Quick Start
**Choose your path:** No code setup or full API control.
### Path 1: No-Code Setup (60 seconds)
Perfect for beginners or rapid prototyping. Zero programming required.
#### Step 1: Add Manager
1. **Hierarchy** → Right-click → **Create Empty** → Name it `PoolMaster`
2. **Add Component** → `PoolMaster Manager`
#### Step 2: Create Pool
1. Select `PoolMaster` → Inspector → **Add New Pool**
2. Drag your prefab → Set **Prewarm Amount** = `10`
#### Step 3: Auto-Spawn
1. Create empty GameObject → Name it `Spawner`
2. **Add Component** → `PoolMaster Spawner`
3. Drag prefab → **Spawn On** = `On Start`
#### Step 4: Auto-Return
1. Select your prefab (in Project) → **Add Component** → `PoolMaster Return To Pool`
2. **Return Condition** = `After Time` → **Lifetime** = `2` seconds
**Press Play** - Objects spawn, live 2 seconds, return to pool automatically.
> **Full no-code guide:** See [Documentation/no-code-quick-start.md](Documentation/no-code-quick-start.md)
---
### Path 2: Code Setup (Programmer API)
Full control for advanced users. Type-safe, high-performance pooling.
#### Step 1: Configure Pool
```csharp
using UnityEngine;
using PoolMaster;
public class BulletSpawner : MonoBehaviour
{
[SerializeField] private GameObject bulletPrefab;
void Start()
{
var request = new PoolRequest
{
prefab = bulletPrefab,
initialPoolSize = 20,
shouldPrewarm = true
};
PoolingManager.Instance.GetOrCreatePool(request);
}
}
```
#### Step 2: Spawn from Pool
```csharp
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var bullet = PoolingManager.Instance.Spawn(
bulletPrefab,
transform.position,
Quaternion.identity
);
}
}
```
#### Step 3: Implement IPoolable
```csharp
using UnityEngine;
using PoolMaster;
public class Bullet : MonoBehaviour, IPoolable
{
public IPool ParentPool { get; set; }
public bool IsPooled { get; private set; }
public void OnSpawned()
{
IsPooled = true;
GetComponent().velocity = transform.forward * 20f;
}
public void OnDespawned()
{
IsPooled = false;
}
public void PoolReset()
{
GetComponent().velocity = Vector3.zero;
}
void OnCollisionEnter(Collision collision)
{
ParentPool?.Despawn(gameObject);
}
}
```
**That's it.** Spawn uses the pool, collision returns to pool.
---
## API Reference
### Core Classes
#### PoolingManager
Central singleton for managing all pools.
```csharp
// Get or create a pool
var pool = PoolingManager.Instance.GetOrCreatePool(request);
// Spawn objects
GameObject obj = PoolingManager.Instance.Spawn(prefab, position, rotation);
GameObject obj = PoolingManager.Instance.Spawn(prefab); // At origin
// Despawn objects
PoolingManager.Instance.Despawn(obj);
// Batch operations
int count = PoolingManager.Instance.SpawnBatch(
prefab,
positions,
rotations,
parent
);
// Get pool by prefab or ID
IPool pool = PoolingManager.Instance.GetPool(prefab);
IPool pool = PoolingManager.Instance.GetPool("poolId");
// Global snapshot
PoolSnapshot snapshot = PoolingManager.Instance.CaptureSnapshot();
```
#### Pool
Generic type-safe pool implementation.
```csharp
// Create pool directly
var pool = new Pool(
prefab,
request,
poolParent,
poolId
);
// Pool operations
GameObject obj = pool.Spawn(position, rotation, parent);
bool success = pool.Despawn(obj);
pool.PrewarmPool(count);
pool.Clear();
pool.DestroyPool();
// Pool info
int active = pool.ActiveCount;
int inactive = pool.InactiveCount;
int total = pool.Capacity;
PoolMetrics metrics = pool.Metrics;
```
#### PoolRequest
Configuration for pool creation.
```csharp
var request = new PoolRequest
{
prefab = bulletPrefab,
initialPoolSize = 50, // Initial capacity
shouldPrewarm = true, // Create objects immediately
maxPoolSize = 200, // Max objects to keep pooled
allowExpansion = true, // Grow beyond initial size
cullExcessObjects = true, // Remove excess objects
cullThreshold = 100, // Cull when over this count
initializationTiming = PoolInitializationTiming.OnAwake,
usePoolContainer = true, // Parent container
containerName = "Bullet Pool"
};
```
#### CollectionPool
Static utility for pooling collections.
```csharp
// Lists
var list = CollectionPool.GetList();
// ... use list ...
CollectionPool.Return(list);
// HashSets
var set = CollectionPool.GetHashSet();
CollectionPool.Return(set);
// Dictionaries
var dict = CollectionPool.GetDictionary();
CollectionPool.Return(dict);
```
### Extension Methods
```csharp
// GameObject extensions
gameObject.ReturnToPool();
// Batch spawning
pool.SpawnBatch(positions, rotations, parent);
int count = pool.DespawnBatch(objects);
```
### Events
```csharp
// Pool lifecycle
PoolingEvents.OnPoolCreated += (poolId, prefab) => {};
PoolingEvents.OnPoolDestroyed += (poolId, prefab) => {};
PoolingEvents.OnPoolPrewarmed += (poolId, count) => {};
// Object lifecycle
PoolingEvents.OnObjectSpawned += (obj, poolId) => {};
PoolingEvents.OnObjectDespawned += (obj, poolId) => {};
PoolingEvents.OnObjectCreated += (obj, poolId) => {};
PoolingEvents.OnObjectDestroyed += (obj, poolId) => {};
// Performance
PoolingEvents.OnPoolExpanded += (poolId, newCapacity) => {};
PoolingEvents.OnPoolCulled += (poolId, objectsDestroyed) => {};
```
## Performance Benchmarks
Performance comparison vs traditional `Instantiate/Destroy`:
| Operation | Instantiate/Destroy | PoolMaster | Improvement |
|-----------|---------------------|------------|-------------|
| Spawn Single Object | ~0.8ms | ~0.002ms | **400x faster** |
| Spawn 100 Objects | ~80ms | ~0.2ms | **400x faster** |
| Despawn Single Object | ~0.3ms | ~0.001ms | **300x faster** |
| GC Allocations (1000 cycles) | ~120 MB | ~0.5 MB | **240x less** |
| Frame time impact (60fps) | 5-15ms spikes | <0.1ms | **No hitching** |
### Batch Operations Performance
| Batch Size | Individual Spawn | Batch Spawn | Improvement |
|------------|------------------|-------------|-------------|
| 10 objects | ~0.02ms | ~0.008ms | **2.5x faster** |
| 50 objects | ~0.1ms | ~0.03ms | **3.3x faster** |
| 100 objects | ~0.2ms | ~0.05ms | **4x faster** |
| 500 objects | ~1.0ms | ~0.2ms | **5x faster** |
*Benchmarks run on Unity 6000.0.62f1 LTS, Intel i9-13950HX, 32GB RAM DDR5 5200mhz, RTX 4070 mGPU*
## 🛰️ Advanced Usage
### Command Buffer System
For thread-safe enqueueing from Jobs or background threads:
```csharp
// Get command buffer for a pool
var buffer = PoolingManager.Instance.GetCommandBuffer("bullets");
// Enqueue spawn (thread-safe)
buffer.EnqueueSpawn(position, rotation, parent);
// Async spawning
var gameObject = await buffer.SpawnAsync(position, rotation);
// Batch enqueueing
buffer.EnqueueSpawnBatch(positions, rotations, parent);
var objects = await buffer.SpawnBatchAsync(positions, rotations);
// Commands are automatically flushed each frame in LateUpdate
```
### Pool Metrics & Diagnostics
```csharp
// Get metrics
PoolMetrics metrics = pool.Metrics;
Debug.Log($"Total Spawned: {metrics.TotalSpawned}");
Debug.Log($"Total Despawned: {metrics.TotalDespawned}");
Debug.Log($"Reuse Efficiency: {metrics.ReuseEfficiency:P}");
Debug.Log($"Current Active: {metrics.CurrentActive}");
Debug.Log($"Expansion Count: {metrics.ExpansionCount}");
// Open diagnostics window
// Window > PoolMaster > Diagnostics
```
### Pre-warming Strategies
```csharp
// 1. On Awake (before scene starts)
var request = new PoolRequest
{
prefab = prefab,
initialPoolSize = 50,
shouldPrewarm = true,
initializationTiming = PoolInitializationTiming.OnAwake
};
// 2. On Start (after scene initialized)
request.initializationTiming = PoolInitializationTiming.OnStart;
// 3. Lazy (only when first needed)
request.initializationTiming = PoolInitializationTiming.Lazy;
// 4. Next frame (avoid loading hitches)
request.initializationTiming = PoolInitializationTiming.NextFrame;
// 5. On event (custom timing)
request.initializationTiming = PoolInitializationTiming.OnEvent;
request.eventId = "level_loaded";
// Then trigger:
PoolingManager.Instance.BootstrapPools(PoolInitializationTiming.OnEvent, "level_loaded");
```
### Working with Particles
Use the included `PooledVfx` component for automatic particle system management:
```csharp
public class PooledVfx : PoolableMonoBehaviour
{
[SerializeField] private bool autoReturnWhenFinished = true;
[SerializeField] private float maxLifetime = 10f;
// Automatically returns to pool when particles finish
}
```
### Custom Pool Control
```csharp
if (pool is IPoolControl poolControl)
{
// Advanced operations
poolControl.PrewarmPool(count);
poolControl.Clear();
poolControl.CullExcess(maxCount);
poolControl.DestroyPool();
// Batch operations
poolControl.SpawnBatch(positions, rotations, parent);
poolControl.DespawnBatch(objects);
}
```
## 🔧 Configuration
### Enable Debug Logging
Add `ENABLE_POOL_LOGS` to your Scripting Define Symbols:
1. `Edit > Project Settings > Player > Other Settings`
2. Add `ENABLE_POOL_LOGS` to Scripting Define Symbols
3. Logs will completely compile out when the symbol is removed (zero overhead)
### Assembly Definitions
PoolMaster uses assembly definitions for clean separation:
- `PoolMaster` - Core runtime assembly
- `PoolMaster.Editor` - Editor-only tools
To reference PoolMaster in your code, add `PoolMaster` to your assembly definition references.
## Migration Guide
### From Unity's Built-in ObjectPool
```csharp
// Before (Unity ObjectPool)
using UnityEngine.Pool;
var pool = new ObjectPool(
createFunc: () => Instantiate(prefab),
actionOnGet: (obj) => obj.SetActive(true),
actionOnRelease: (obj) => obj.SetActive(false),
actionOnDestroy: (obj) => Destroy(obj),
collectionCheck: true,
defaultCapacity: 20,
maxSize: 100
);
var obj = pool.Get();
pool.Release(obj);
// After (PoolMaster)
using PoolMaster;
var request = new PoolRequest
{
prefab = prefab,
initialPoolSize = 20,
maxPoolSize = 100,
shouldPrewarm = true
};
PoolingManager.Instance.GetOrCreatePool(request);
var obj = PoolingManager.Instance.Spawn(prefab, position, rotation);
PoolingManager.Instance.Despawn(obj);
```
### From Manual Pooling
```csharp
// Before (Manual pooling)
Queue pool = new Queue();
GameObject Spawn()
{
if (pool.Count > 0)
{
var obj = pool.Dequeue();
obj.SetActive(true);
return obj;
}
return Instantiate(prefab);
}
void Despawn(GameObject obj)
{
obj.SetActive(false);
pool.Enqueue(obj);
}
// After (PoolMaster)
using PoolMaster;
// One-time setup
var request = new PoolRequest { prefab = prefab, initialPoolSize = 10 };
PoolingManager.Instance.GetOrCreatePool(request);
// Use anywhere
var obj = PoolingManager.Instance.Spawn(prefab, position, rotation);
obj.ReturnToPool(); // Extension method
```
## Best Practices
1. **Always implement IPoolable** - Even if empty, it ensures proper lifecycle hooks
2. **Use PoolableMonoBehaviour** - Handles common cleanup patterns automatically
3. **Pre-warm pools on load** - Avoid runtime hitches with `shouldPrewarm = true`
4. **Set reasonable max sizes** - Use `maxPoolSize` to prevent unbounded memory growth
5. **Enable culling** - Use `cullExcessObjects = true` to manage memory
6. **Use batch operations** - Spawn/despawn multiple objects in one call when possible
7. **Profile your pools** - Use the diagnostics window to optimize pool sizes
8. **Disable logs in production** - Remove `ENABLE_POOL_LOGS` for zero logging overhead
## FAQ
**Q: Can I use PoolMaster with addressables?**
A: Yes! Pass the loaded addressable as the prefab parameter.
**Q: Does it work with nested prefabs?**
A: Yes, PoolMaster handles any GameObject prefab regardless of hierarchy.
**Q: What about pooling across scene loads?**
A: PoolingManager persists across scenes with `DontDestroyOnLoad`. Pools survive scene transitions.
**Q: Can I have multiple pools for the same prefab?**
A: Yes, provide unique `poolId` parameters when creating pools.
**Q: Is it thread-safe?**
A: Core pooling must happen on the main thread, but use `PoolCommandBuffer` for thread-safe enqueueing.
**Q: Does it work with ECS/DOTS?**
A: PoolMaster is designed for GameObject-based workflows. For DOTS, use Unity's native entity pooling.
## License
MIT License - see [LICENSE](LICENSE) file for details.
## Credits
Created by Max Thomas Coates, Misty.
## Contributing
- Enable repo hooks: `git config core.hooksPath .githooks`
- Verify CSharpier: `csharpier --version` (required for formatting)
- Format manually if needed: `csharpier format .`
See full dev setup: [DEVELOPING.md](DEVELOPING.md)
## 🐛 Support
- **Issues**: [GitHub Issues](https://github.com/yourusername/PoolMaster/issues)
- **Discussions**: [GitHub Discussions](https://github.com/yourusername/PoolMaster/discussions)
- **Discord**: misty2023
---
⭐ **If PoolMaster helps your project, consider giving it a star on GitHub!**