Object Pooling

The 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.

Several of the Weaver examples make use of pools: Floating Text, Missiles, and Missile Command.

Basic usage of an object pool is quite simple:

  1. 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 preAllocate immediately.
  2. Call Acquire to get an object from the pool.
  3. Call Release when you are done with the object to give it back to the pool.

Utilities and Extensions

The non-generic ObjectPool class contains various utilities and extension methods for creating and interacting with pools:

  • CreateDefaultPool returns a pool that creates new items using a the default constructor of T.
  • CreateComponentPool takes a prefab of any Component type and returns a pool that creates new items by instantiating that prefab.
  • GetSharedComponentPool is 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 Missile component has its own pool of instances (same for FloatingText and Explosion).
  • CreatePrefabPool and GetSharedPrefabPool are the same thing for GameObject prefabs. Generally you will want a specific Component on the prefab though, so it's often best to use the component type directly.
  • TryRelease releases the item to the given pool, or returns false if the pool is null.
  • TryReleaseOrDestroy is similar, but calls Object.Destroy on the item if the pool is null.
  • TryReleaseOrDestroyGameObject is similar, but if the object is released it's GameObject is automatically deactivated, or if the pool is null it calls Object.Destroy on 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.

The static 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.

Asset Injection

The [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.