Current Version: 0.3.1
Updated 12 April 2024 | Changelog
BeauPools is an object pooling library for Unity. It contains both generic pools and a Unity-specific prefab pool, along with utilities for managing those prefab pools.
Download BeauPools.unitypackage from the repo. Unpack it into your project.
BeauData uses the BeauPools
namespace. You'll need to add the statement using BeauPools;
to the top of any scripts using it.
There are four types of pools in BeauPools: Fixed, Dynamic, Prefab, and Serializable.
Pool Type | Capacity | Requires Constructor | Notes |
---|---|---|---|
Fixed | Fixed capacity. If allocating from empty pool, an exception is thrown. | ✓ | |
Dynamic | Dynamic capacity. If allocating from empty pool, a new element will be constructed. | ✓ | |
Prefab | Dynamic pool (see above). | X | Unity-specific behavior documented below. |
Serializable | Dynamic pool (see above). | X | Similar functionality to Prefab pool, inspector serializable. |
public class SomeObject { ... }
// -- Creating a FixedPool --
// Max size for the FixedPool
int fixedPoolCapacity = 32;
// Constructor, required for generating pool elements
// This should return identical elements every call
// Pool.DefaultConstructor returns a default constructor for types with empty constructors
Constructor<SomeObject> someObjectConstructor = Pool.DefaultConstructor<SomeObject>();
// Strict typing
// True by default. Set this to false if you don't know
// exactly the type of object being generated by the constructor
bool fixedPoolStrictTyping = true;
// Finally, generating the pool
FixedPool<SomeObject> fixedPool = new FixedPool<SomeObject>( fixedPoolCapacity, someObjectConstructor, fixedPoolStrictTyping );
// -- Creating a DynamicPool --
// You can use more specific constructors if desired
Constructor<SomeObject> customObjectConstructor = (IPool<SomeObject> pool) => { return new SomeObject(); };
DynamicPool<SomeObject> dynamicPool = new DynamicPool<SomeObject>( 16, customObjectConstructor );
// -- Destroying a Pool ---
// To destroy a pool, call Dispose()
fixedPool.Dispose();
dynamicPool.Dispose();
By default, pools do not start with any elements. You must prewarm the pool with its initial set of elements.
// This will ensure the pool contains 16 elements
somePool.Prewarm( 16 );
// This will fill the pool up to its current capacity.
somePool.Prewarm();
You can shrink the number of elements in the pool down as well.
// This will ensure the pool contains no more than 16 elements.
somePool.Shrink( 16 );
You can also clear a pool of all its elements with Clear()
.
To allocate an element from the pool, call Alloc()
. To return it to the pool, call Free( [element] )
.
// Allocate the element from the pool
var element = somePool.Alloc();
// Do something with the pooled element
...
// Return the element to the pool
somePool.Free(element);
You can also allocate and free in batches.
List<SomeObject> objectInUse = new List<SomeObject>();
// This will allocate 16 elements and add them to the list
somePool.Alloc(objectsInUse, 16);
// Do something with those elements
...
// Free them all at once
somePool.Free(objectsInUse);
If your pooled object inherits from the IPooledObject<T>
interface, it will receive certain calls from the pool during the object's lifecycle. These events are OnConstruct
, OnAlloc
, OnFree
, and finally OnDestruct
. The IPoolConstructHandler
and IPoolAllocHandler
interfaces also exist to provide non-generic interfaces for receiving these events, regardless of the exact pool type.
public class PooledObjectWithCallbacks : IPooledObject<PooledObjectWithCallbacks>
{
// Called when this object is constructed for the given pool
public void OnConstruct(IPool<T> inPool)
{
...
}
// Called when the element is allocated from the pool
public void OnAlloc()
{
...
}
// Called when the element is returned to the pool
public void OnFree()
{
...
}
// Called when the element is destroyed from within the pool
public void OnDestruct()
{
...
}
}
public class SomeConstructHandler : IPoolConstructHandler
{
public void OnConstruct()
{
...
}
public void OnDestruct()
{
...
}
}
public class SomeAllocHandler : IPoolAllocHandler
{
public void OnAlloc()
{
...
}
public void OnFree()
{
...
}
}
Some suggested uses for these callbacks:
- Use the
OnAlloc
orOnFree
callbacks to help reset state. - Use
OnConstruct
andOnDestruct
to help manage resources in situations where the standard UnityAwake
andOnDestroy
events may not fire (i.e. the object might never be activated) - Use
OnConstruct(IPool<T>)
to remember the pool that owns this object. This would allow you to write a method to return the object to its owning pool.
Every pool also contains an PoolConfig
object. This allows you to register additional callbacks on a per-pool rather than per-element basis. See PoolConfig Members below for documentation.
PrefabPools
are pools intended for instantiating and pooling Unity prefab objects. To this end, they have the following properties.
- New instances are assigned as children of a specific Transform. You should ensure this Transform is disabled to avoid unwanted behaviour from your pooled instances.
- By default, allocated instances are assigned as children of a specific Transform, or the root of the scene if no Transform is provided.
- By default, allocated instances reset their position and orientation on allocation. This can be disabled.
- PrefabPool provides overridden versions of the
Alloc
method to mirror some of Unity'sInstantiate
methods. See PrefabPool Members below for documentation. - Any components on the prefab or its children that implement the
IPoolConstructHandler
orIPoolAllocHandler
will receive pool events (OnConstruct
,OnAlloc
,OnFree
,OnDestruct
) - Instances are given a component tracking the PrefabPool that created it. You can use the
Pool.TryFree(GameObject)
orPool.TryFree(Component)
utilities to access this and free the prefab back to its pool, without needing to maintain your own reference to the pool itself.
BeauPools provides the SerializablePool
object for easy setup of a PrefabPool in the inspector. It can be treated as a PrefabPool, and also provides a set of methods for managing all currently allocated/active objects from the pool.
A SerializablePool operates by constructing an internal PrefabPool and performing operations on that pool. The internal pool will be constructed when Initialize
is called. It will also be lazily instantiated if any Alloc
or Prewarm
methods are called. It will not be lazily instantiated if a Free
method is called, and will error if the internal pool is not present.
Note: due to Unity's serialization rules in versions prior to 2020.1, you must create a subclass of SerializablePool for each type of component you want to pool.
// This is necessary, since Unity won't serialize generic classes
[Serializable]
public SomePooledBehaviourPool : SerializablePool<SomePooledBehaviour> { }
public class TestSerializedPool : MonoBehaviour
{
[SerializeField]
private SomePooledBehaviorPool myPool;
private void Start()
{
// Initialize can be called before the pool is used, but is not required
myPool.Initialize();
}
private void OnDestroy()
{
// Make sure to destroy your pool before it goes out of scope
myPool.Destroy();
}
public SomePooledBehaviour Spawn()
{
// A SerializablePool can be treated like any other pool
return myPool.Alloc();
}
}
Member | Type | Description |
---|---|---|
Capacity |
Property | Current capacity of the pool. In fixed pools, this is a hard limit. In dynamic pools, this is a softer limit. |
Count |
Property | Current number of elements in the pool. |
InUse |
Property | Current number of elements allocated from the pool. |
Config |
Property | Event configuration. Use this to hook up callbacks to specific pool events. |
Prewarm(int count) |
Method | Ensures the pool contains at least [count] number of elements. |
Prewarm() |
Method | Ensures the pool contains up to its current capacity in elements. |
Shrink(int count) |
Method | Ensures the pool contains no more than [count] elements. |
Clear() |
Method | Clears all elements from the pool. |
Dispose() |
Method | Clears all elements and cleans up memory used by the pool. |
Alloc() |
Method | Allocates an element from the pool. |
Alloc(T[] array) |
Method | Allocates elements from the pool to empty slots in the given array. |
Alloc(ICollection<T> collection, int count) |
Method | Allocates [count] elements and adds them to the given collection. |
TempAlloc() |
Method | Allocates an element from the pool and returns a disposable TempAlloc<T> struct that will free the element on dispose. |
TryAlloc(out T element) |
Method | Attempts to allocate an element from the pool. |
TryAlloc(T[] array) |
Method | Attempts to allocate elements from the pool to empty slots in the given array. |
TryAlloc(ICollection<T> collection, int count) |
Method | Attempts to allocate [count] elements and add them to the given collection. |
Free(T element) |
Method | Returns an element to the pool. |
Free(T[] array) |
Method | Returns elements from the given array to the pool. |
Free(ICollection<T> collection) |
Method | Returns elements from the given collection to the pool. |
UseIDisposable() |
Method | Hooks up the IDisposable.Dispose() call to object destruction. (Only when T inherits from IDisposable ) |
Member | Type | Description |
---|---|---|
RegisterOnConstruct(LifecycleEvent<T> callback) |
Method | Registers a callback to be called when an element is constructed. |
RegisterOnDestruct(LifecycleEvent<T> callback) |
Method | Registers a callback to be called when an element is destroyed. |
RegisterOnAlloc(LifecycleEvent<T> callback) |
Method | Registers a callback to be called when an element is allocated. |
RegisterOnFree(LifecycleEvent<T> callback) |
Method | Registers a callback to be called when an element is freed. |
Member | Type | Description |
---|---|---|
Name |
Property | Name of the pool. Used when naming prefab instances. |
PoolTransform |
Property | Parent Transform for all inactive elements in the pool. |
DefaultSpawnTransform |
Property | The default parent Transform to which allocated elements are attached. |
Alloc(Transform parent) |
Method | Allocates a new element as a child of the given transform. |
Alloc(Vector3 position, bool worldSpace) |
Method | Allocates a new element with the given position. |
Alloc(Vector3 position, Quaternion orientation, bool worldSpace) |
Method | Allocates a new element with the given position and orientation. |
Alloc(Vector3 position, Quaternion orientation, Transform parent, bool worldSpace) |
Method | Allocates a new element as a child of the given transform with the given position and orientation |
Member | Type | Description |
---|---|---|
ConfigureCapacity(int capacity, int prewarm, bool prewarmOnInit) |
Method | Configures capacity and prewarm settings. |
ConfigureTransforms(Transform poolRoot, Transform spawnRoot, bool resetTransformOnAlloc) |
Method | Configures transform and reset settings. |
Initialize() |
Method | Initializes the pool. |
IsInitialized() |
Method | Returns if the pool has been initialized. |
ActiveObjects |
Property | Returns the set of currently allocated objects. |
GetActiveObjects(ICollection<T>) |
Method | Adds the set of currently allocated objects to the given collection. |
GetActiveObjects(T[]) |
Method | Adds the set of currently allocated objects to the given array. |
GetActiveObjects(T[], int) |
Method | Adds the set of currently allocated objects to the given array at an offset. |
GetActiveObjects(List<T>) |
Method | Adds the set of currently allocated objects to the given array. |
Reset() |
Method | Returns all currently allocated objects to the pool. |
Destroy() |
Method | Calls Reset() and destroys the pool. |
Utility | Description |
---|---|
PooledList |
Pooled list. Useful when you need a temporary ordered collection. |
PooledSet |
Pooled hashset. Useful when you need a temporary unordered collection. |
PooledStringBuilder |
Pooled StringBuilder. Useful when you need to temporarily concatenate strings. |
TransformPool |
Transform specialization of SerializablePool |
SpriteRendererPool |
SpriteRenderer specialization of SerializablePool |
RectTransformPool |
RectTransform specialization of SerializablePool |
ImagePool |
Image specialization of SerializablePool |
TempAlloc<T> |
Disposable struct wrapper for a temporary allocation. Will free the allocated element on dispose. |
Pool.TryFree(Component) |
Attempts to free the given prefab to its source PrefabPool or SerializablePool |
Pool.TryFree(GameObject) |
Attempts to free the given prefab to its source PrefabPool or SerializablePool |