ObjectPool<T> class allows you to easily and efficiently reuse objects instead of creating and destroying them all the time, which can improve performance. It is fully generic so it can be used with any type of object such as
Components in the scene,
ScriptableObject assets, or any other class.
Basic usage of an object pool is quite simple:
- Construct a
new ObjectPool<T>with a delegate that will be used to create new items as they are needed. You can optionally specify a number of items to
Acquireto get an object from the pool.
Releasewhen you are done with the object to give it back to the pool.
Utilities and Extensions
ObjectPool class contains various utilities and extension methods for creating and interacting with pools:
CreateDefaultPoolreturns a pool that creates new items using a the default constructor of T.
CreateComponentPooltakes a prefab of any
Componenttype and returns a pool that creates new items by instantiating that prefab.
GetSharedComponentPoolis similar, but it stores each pool it creates so it can return the same one if it is given the same prefab again. The examples mentioned above use this method so that each prefab with a
Missilecomponent has its own pool of instances (same for
GetSharedPrefabPoolare the same thing for
GameObjectprefabs. Generally you will want a specific
Componenton the prefab though, so it's often best to use the component type directly.
TryReleasereleases the item to the given pool, or returns false if the pool is null.
TryReleaseOrDestroyis similar, but calls
Object.Destroyon the item if the pool is null.
TryReleaseOrDestroyGameObjectis similar, but if the object is released it's
GameObjectis automatically deactivated, or if the pool is null it calls
Object.Destroyon the component's
GameObject(rather than only destroying the component).
By default, an
ObjectPool<T> will use a
HashSet<T> as its
ActiveObjects collection for efficiently adding and removing items, but you can give it any type of collection in the constructor and if you want to use a
List<T> in particular, you can use the
PooledList<T> class instead of
ObjectPool<T>. The non-generic
PooledList class also has similar utilities and extension methods.
Destroy or Release?
Other object pooling systems generally force you to choose whether something can be used on its own or will be pooled:
- If the object destroys itself when it's done then it can't be reused in an object pool.
- If the object deactivates itself when it's done then using it without an object pool would leave it inactive without ever reusing it, which isn't necessarily a problem but would be wasting memory at the very least.
- If the object simply indicates that it is done and relies on something else to respond accordingly, it still depends on something else and can't be used on its own.
ObjectPool<T>.Current property allows you to avoid the problem. When a pool creates a new item, the pool is assigned to that property (and cleared immediately after) so that the new object can get a reference to the pool that created it in its constructor. This means the object can decide what to do when it's done based on whether it came from a pool or not.
PoolableBehaviour<T> is a
Component class which makes use of that feature in conjunction with the
ObjectPool.TryReleaseOrDestroyGameObject<T> extension method. All of the examples mentioned above inherit from
PoolableBehaviour<T> so that while they are currently only used in object pools, you could for example have a big boss fire a custom once-off
Missile prefab without using a pool and it would still work properly.
[AssetPool] attribute allows
ObjectPool<T> fields to have their value set by the Asset Injection system. The
TextManager class contains an example of this: each of its text types is an injected prefab the user can assign in the Weaver Window, then on startup Weaver automatically creates an
ObjectPool<T> for each of them which will instantiate their prefab to create its items.