Skip to content
A simple generic pooling script for Unity3D with a helper class
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
README.md
SimplePool.cs Improved NewObject function slightly Apr 2, 2018
SimplePoolUtils.cs

README.md

UnityGenericPool

A pooling solution for Unity3D that can store "almost" anything; ranging from Unity objects (e.g. Component, GameObject, Texture) to plain C# objects. It can't store structs but since they are pass-by-value, that would be pointless.

This pool comes with a helper class (SimplePoolHelper) that provides global access to pools that can optionally be named. It is no longer necessary to keep references to pools in your scripts (however, if you wish, you can ignore SimplePoolHelper and manage your pools manually). Note that SimplePoolHelper pools are stored statically and are persistent between scenes.

Method Signatures

SimplePool<T>:

// blueprint: object that gets instantiated when the pool is empty (only if T is of type UnityEngine.Object); also passed as parameter to the CreateFunction
// CreateFunction: called when the pool needs to be populated. Takes blueprint as parameter and should return a new object (if left null and if T is of type UnityEngine.Object, blueprint will be instantiated)
// OnPush: called when an object is pushed to the pool; can be used to e.g. deactivate the object
// OnPop: called when an object is popped from the pool; can be used to e.g. activate the object
SimplePool( Func<T, T> CreateFunction = null, Action<T> OnPush = null, Action<T> OnPop = null );
SimplePool( T blueprint, Func<T, T> CreateFunction = null, Action<T> OnPush = null, Action<T> OnPop = null );

bool Populate( int count );
bool Populate( T blueprint, int count );
T Pop();
T[] Pop( int count );
void Push( T obj );
void Push( IEnumerable<T> objects );
void Clear( bool destroyObjects = true );

SimplePoolHelper:

static SimplePool<T> GetPool<T>( string poolName = null );
static void Push<T>( T obj, string poolName = null );
static T Pop<T>( string poolName = null );
static void Pool<T>( this T obj, string poolName = null );

Example Code

using UnityEngine;

public class PlayerWeapons : MonoBehaviour 
{
	public Transform bulletPrefab;
	public Transform grenadePrefab;

	// Called when the scene starts
	void Awake()
	{
		// Get a Transform pool called Bullets
		SimplePool<Transform> bulletsPool = SimplePoolHelper.GetPool<Transform>( "Bullets" );

		// Get another Transform pool called Grenades
		SimplePool<Transform> grenadesPool = SimplePoolHelper.GetPool<Transform>( "Grenades" );

		// When a bullet is pooled, deactivate it
		bulletsPool.OnPush = ( item ) => item.gameObject.SetActive( false );

		// When a bullet is fetched from the pool, activate it and set its position
		bulletsPool.OnPop = ( item ) =>
		{
			item.gameObject.SetActive( true );
			item.position = transform.position;
			item.rotation = transform.rotation;
		};
		
		// When pool tries to create a new bullet (when empty), create an instance of bulletPrefab and keep it alive between scenes
		bulletsPool.CreateFunction = ( template ) => 
		{
			Transform newBullet = Instantiate( bulletPrefab );
			DontDestroyOnLoad( newBullet );
			return newBullet;
		};

		// When a grenade is pooled, deactivate it
		grenadesPool.OnPush = ( item ) => item.gameObject.SetActive( false );
		// When a grenade is fetched from the pool, activate it (you can also set its position here, it is entirely up to you)
		grenadesPool.OnPop = ( item ) => item.gameObject.SetActive( true );
		// When pool tries to create a new grenade, simply create an instance of grenadePrefab
		grenadesPool.CreateFunction = ( template ) => Instantiate( grenadePrefab );

		// Populate the pool with 10 bullet instances (optional)
		bulletsPool.Populate( 10 );

		// Populate the pool with 4 grenade instances (optional)
		grenadesPool.Populate( 4 );

		// Yet another pool that will store some WaitForSeconds instances for reuse in Grenade class
		// This pool is different from the previous pools in two ways:
		// 1) it stores a plain object: WaitForSeconds, which is not a Component nor a GameObject
		// 2) it has no name since there is only one pool of type WaitForSeconds; note that
		//    it is faster and more efficient to access an unnamed pool in SimplePoolHelper
		// CreateFunction: simply create a new WaitForSeconds instance that waits for 6 seconds
		// There is no need to do any special operations on OnPush or OnPop, so they are not altered
		SimplePoolHelper.GetPool<WaitForSeconds>().CreateFunction = ( template ) => new WaitForSeconds( 6f );
	}

	// Called when the object is destroyed
	void OnDestroy()
	{
		// This transform is about to be destroyed, bullets fetched from pool can no longer use it
		// So fetched bullets are simply activated, without changing their position
		SimplePoolHelper.GetPool<Transform>( "Bullets" ).OnPop = ( item ) => item.gameObject.SetActive( true );

		// Grenades in the pool are not persistent between scenes (unlike bullets), and will become null references
		// So clear all grenade instances in the Grenades pool
		SimplePoolHelper.GetPool<Transform>( "Grenades" ).Clear();
	}

	void Update()
	{
		// Left mouse button clicked: fire a bullet
		if( Input.GetMouseButtonDown( 0 ) )
		{
			// Add forward force to the bullet
			SimplePoolHelper.Pop<Transform>( "Bullets" ).GetComponent<Rigidbody>().velocity = transform.forward * 100f;
		}

		// Right mouse button clicked: throw a grenade
		if( Input.GetMouseButtonDown( 1 ) )
		{
			Transform grenade = SimplePoolHelper.Pop<Transform>( "Grenades" );

			// Set the position of the grenade (unlike bullets, it is not automatically set in Pop)
			grenade.position = transform.position;

			// Add forward force to the grenade (also some upwards force as well)
			grenade.GetComponent<Rigidbody>().velocity = transform.forward * 30f + transform.up * 15f;
		}
	}
}
using UnityEngine;

public class Bullet : MonoBehaviour 
{
	// Pool the bullet if it doesn't hit anything in 5 seconds
	void OnEnable()
	{
		Invoke( "PoolBullet", 5f );
	}

	// Bullet is deactivated (probably hit something and got pooled), cancel the PoolBullet invoke just in case
	void OnDisable()
	{
		CancelInvoke( "PoolBullet" );
	}
	
	void OnCollisionEnter( Collision other )
	{
		// Apply damage to the collided entity
		Health entityHealth = other.transform.GetComponent<Health>();
		if( entityHealth != null )
			entityHealth.ApplyDamage( 10 );

		// Pool the bullet instead of destroying it 
		// As bullets are kept in a Transform pool, the Pool extension function is used on the transform of the pool
		transform.Pool( "Bullets" );

		// Alternative pool method (does exactly the same thing as Pool function)
		// SimplePoolHelper.Push( transform, "Bullets" );
	}

	private void PoolBullet()
	{
		transform.Pool( "Bullets" );
	}
}
using UnityEngine;
using System.Collections;

public class Grenade : MonoBehaviour 
{
	// Explode after 6 seconds
	// Notice how we use OnEnable instead of Start as Start is only called once during the lifetime of the object
	void OnEnable()
	{
		StartCoroutine( Explode() );
	}

	IEnumerator Explode()
	{
		// Fetch an instance of WaitForSeconds from the pool
		WaitForSeconds waitForSeconds = SimplePoolHelper.Pop<WaitForSeconds>();

		// Wait for 6 seconds (set in PlayerWeapons class)
		yield return waitForSeconds;

		// Pool the WaitForSeconds instance for reuse
		waitForSeconds.Pool();

		// Play some fancy particles
		GetComponent<ParticleSystem>().Emit( 50 );

		// Pool the grenade for reuse
		transform.Pool( "Grenades" );
	}
}
You can’t perform that action at this time.