Spawners and Object Pooling for Unity. Work in progress. Following documentation is not complete and may be outdated
Object pooling is a technique, which reduces heap memory allocations, by recycling objects. Pool is made to be lightweight and efficient, it's also responsible for:
- Tracking pooled objects
- Retrieving and returning them
- Resetting their state
- Expanding when necessarry
When object is retrieved:
- If Pool is empty, then it's expanded
- Pooled object is selected and marked as used
- It's state is being restored to default
When object is returned
- Returned object is disabled
- The object is marked as pooled
Pooled object has to be a prefab with component that inherits from Poolable<T>. It's a generic class used by pools, which holds a reference to the actual object.
This classes are responsible for creating and initializing instances of the Pool class. Every needed parameters are set up more or less directly through the Unity Inspector.
- Retrieve an object
Poolable<T> poolable = pool.Retrieve();- Retrieve multiple objects
Poolable<T>[] poolables = new Poolable<T>[10];
pool.RetrieveMany(poolables);
pool.RetrieveMany(poolables, count: 5);
pool.RetrieveMany(poolables, 11); // This will throw an AssertionException, since the 'count' parameter is greater than 'poolables' length
pool.RetrieveMany(poolables, 0); // This will throw too.- Return an object
pool.Return(poolable); // 'poolable' cannot be nullMulti Pool is a container for multiple Pools and other Multi Pools. It's interface is extended by following methods, which allow retrieving object from selected Pool:
int poolIndex = 0;
Poolable<T> poolable = pool.RetrieveFrom(poolIndex);
pool.RetrieveManyFrom(poolables, poolIndex);
pool.RetrieveManyFrom(poolables, poolIndex, count: 5);When Retrieve or RetrieveMany methods are invoked, used Pool is selected based on provided ISelector interface implementation. Currently, there are following Selectors available:
- Random - selects Pools randomly
- Priority based - Pools are selected based on given priorities. Pool with higher priority has higher chance of being chosen.
- Sequence - Pools are selected in a sequential way with customizable step (i.e. every second or third Pool).
It's time for some examples. Let's set up a simple Pool for Transform components.
class TransformPoolable : Poolable<Transform> { }Yes, that's it. This is necessary, since Unity can't serialize generic classes. Following step is going to be similar.
class TransformPoolPreparer : PoolPreparer<Transform>
{
[SerializeField]
private TransformPoolable prefab;
protected override Poolable<Transform> Prefab => prefab;
}This allows to select a prefab, which is going to be pooled. PoolPreparer class can be further customized with two optional overrides.
By default, pooled object's GameObject is activated when retrieved, and deactivated when returned. This behaviour can be extended or completly modified. To change it, provide an implementation of IPoolableStateRestorer<Transform> interface.
class TransformPoolableStateRestorer : IPoolableStateRestorer<Transform>
{
public void OnRetrieve(Poolable<Transform> poolable)
{
poolable.gameObject.SetActive(true);
poolable.Target.localScale = Vector3.one;
}
public void OnReturn(Poolable<Transform> poolable)
{
poolable.gameObject.SetActive(false);
}
}Then, use it in Pool Preparer by overriding StateRestorer property:
protected override IPoolableStateRestorer<Transform> StateRestorer => restorer;Pooled objects are instantiated on the scene when:
- Pool instance is created
- Pool is being expanded
By default, pooled objects are instantiated as Pool Preparer's children. Note, that for every created object, OnRetrieve method of provided IPoolableStateRestorer implementation is invoked. To extend this behaviour, either provide an implementation of IPoolableFactory<Transform> interface or extend PoolableFactory<Transform> class. The latter is generally more preferable. An example use case is to provide dependencies, that can't be set up directly in the object's prefab.
class TransformFactory : PoolableFactory<Transform>
{
public TransformFactory(Poolable<Transform> prefab, Transform parent, IPoolableStateRestorer<Transform> stateResotrer) : base(prefab, parent, stateResotrer)
{
poolable.Target.eulerAngles = new Vector3(30, 0, 90);
}
public override void OnCreated(Poolable<Transform> created)
{
created.Target.localScale = scale;
}
}To use it, override PoolableFactory property:
protected override IPoolableFactory<Transform> PoolableFactory => new TransformFactory(prefab, transform, StateRestorer, scale);Finally, Pool Preparer should look like this:
class TransformPoolPreparer : PoolPreparer<Transform>
{
[SerializeField]
private TransformPoolable prefab;
protected override Poolable<Transform> Prefab => prefab;
protected override IPoolableStateRestorer<Transform> StateRestorer => new TransformPoolableStateRestorer();
protected override IPoolableFactory<Transform> PoolableFactory => new TransformFactory(prefab, transform, StateRestorer);
}Coming soon...
Coming soon...
Coming soon...
Coming soon...
Coming soon...
Coming soon...
This project is licensed under the MIT License - see the LICENSE file for details.