Gamieon is a one-developer part-time studio focused on creating video games for the desktop and mobile platforms. Since 2010 Gamieon has developed and released six games as well as six more prototypes. Including beta distributions, Gamieon's products have netted a combined 300,000 downloads! Below this bio are images from all my games and prototypes. Check them out and don't forget to follow me on Twitter @[Gamieon](members:gamieon:321769)

Report RSS An object pooling script for Unity

Posted by on

I ended up using object pooling because the following things were causing a lot of needless object creations in a short time:

  • Explosions and particle systems
  • Sound effects
  • Enemy warp-ins

I figured I'd share the pooling code with everyone since it may come in handy for someone. You're responsible for resetting all of the public and private member variables for your pooled objects; otherwise this should do most of the rest of the work for you. I only use simple objects on it; none of them even have physics properties or colliders.

GameObjectPoolElement.cs

csharp code:
/// <summary>
/// This class represents a single object in the game object pool
/// </summary>
public class GameObjectPoolElement
{
  /// <summary>
  /// The pool GameObject
  /// </summary>
  public GameObject obj; 
 
  /// <summary>
  /// The last time this object was activated by the pool
  /// </summary>
  public float instantiateTime;
}
 

GameObjectPool.cs

csharp code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

/// <summary>
/// Game object pool. Once created, this object and its pool objects will persist for the lifetime of the game.
/// Usage is:
///
/// - Put something like this in an Awake function in your startup scene
/// GameObjectPool myPool = GameObjectPool.Create("Bullets", bulletPrefab, 30, 50);
///
/// - And when you need a bullet
/// bullet = myPool.GetObject();
///
///
/// Limitations:
///
/// - If you have a complex object that has some child objects activated and some deactivated at certain times, you
/// need to be extra careful at initializing them outside of the pool.
///
/// - You are responsible for resetting all the member variables of the object.
///
/// </summary>
public class GameObjectPool : MonoBehaviour
{
  /// <summary>
  /// The prefab from which the objects in the pool were created. We need this in case
  /// we want to add more objects to the pool.
  /// </summary>
  private GameObject prefab;
 
  /// <summary>
  /// The objects being pooled; including their last "instantiation" time.
  /// </summary>
  private List<GameObjectPoolElement> objects;
  /// <summary>
  /// The life time of the objects in the pool, or zero if we don't try to auto-deactivate them
  /// </summary>
  private float lifeTime;
  /// <summary>
  /// The index of the next object to try to get from the pool.
  /// </summary>
  private int nextObjectIndex;
 
  public GameObject Prefab { get { return prefab; } }
  public string PrefabName { get { return prefab.name; } }
 
  /// <summary>
  /// Creates a pool of objects based on a given prefab.
  /// </summary>
  ///
  /// Pool name (used for editor lookups).
  /// </param>
  ///
  /// Prefab that the pool objects are based on.
  /// </param>
  ///
  /// If zero, then the objects are responsible for deactivating themselves. Otherwise, the number of
  /// seconds for the object to live before the object is flagged for deletion. (You can still inactivate
  /// them yourself, of course)
  /// </param>
  ///
  /// The base size of the pool.
  /// </param>
  static public GameObjectPool Create(string poolName, GameObject prefab, float lifeTime, int poolSize)
  {
    GameObject o = new GameObject();
    GameObjectPool pool = o.AddComponent<GameObjectPool>();
    o.name = poolName;
    // Clone the prefab because we need a copy of it to persist throughout the rest of the game
    GameObject prefabClone = (GameObject)Instantiate(prefab, prefab.transform.position, prefab.transform.rotation);
    SetActiveRecurse(prefabClone.transform, false);
    prefabClone.name = prefab.name;
    // Now fill the pool with objects
    pool.Init(prefabClone, lifeTime, poolSize);
    // Make sure we're not ever destroying things.
    DontDestroyOnLoad(prefabClone);
    DontDestroyOnLoad(o);
    return pool;
  }
 
  /// <summary>
  /// Initialized the object pool
  /// </summary>
  ///
  /// The prefab from which to create the objects
  /// </param>
  ///
  /// The number of seconds an object should exist, or zero if the object is responsible
  /// for inactivating itself. You can still have objects inactivate themselves even with
  /// using a lifeTime value of non-zero.
  /// </param>
  ///
  /// The number of objects the pool initially has.
  /// </param>
  void Init(GameObject prefab, float lifeTime, int poolSize)
  {
    this.objects = new  List<GameObjectPoolElement>();
    this.prefab = prefab;
    this.lifeTime = lifeTime;
    this.nextObjectIndex = 0;
   
    for (int i=0; i < poolSize; i++)
    {
      GameObject o = (GameObject)GameObject.Instantiate(prefab, new Vector3(0,0,0), Quaternion.identity);    
      SetActiveRecurse(o.transform, false);
      o.name = prefab.name + (i+1).ToString();
      GameObject.DontDestroyOnLoad(o);
      objects.Add(new GameObjectPoolElement());
      objects[i].obj = o;
      objects[i].instantiateTime = -1; // Means it's new
    }
   
    if (lifeTime > 0) {
      InvokeRepeating("OnPoolMaintenance", 2.0f, 2.0f);
    }
  }
 
  /// <summary>
  /// Recursively renders a game object inactive
  /// </summary>
  ///
  /// The transform of the root game object
  /// </param>
  ///
  /// True to set active, false to set inactive
  /// </param>
  static void SetActiveRecurse(Transform t, bool value)
  {
    t.gameObject.active = value;
    foreach (Transform c in t)
    {
      SetActiveRecurse(c, value);
    }
  }
 
  /// <summary>
  /// Gets the next available object in the pool. If all objects are in use, then
  /// a new one is created.
  /// </summary>
  /// <returns>
  /// The object.
  /// </returns>
  public GameObject GetObject()
  {
    GameObject result = null;
   
    // Get an object from the pool. The idea here is, rather than iterating from
    // 0 to Count each time, we retain a round-robin index. This saves us from
    // the trouble of, during active times, having to constantly skip over a bunch
    // of active elements. Of course, if the whole pool is busy, the loop will end
    // and result will still be null.
    if (objects.Count > 0)
    {
      for (int i=0; i < objects.Count; i++)
      {
        int index = nextObjectIndex;
        nextObjectIndex = (nextObjectIndex + 1) % objects.Count;
        if (false == objects[index].obj.active)
        {
          result = objects[index].obj;
          objects[index].instantiateTime = Time.time;
          SetActiveRecurse(result.transform, true);
          break;
        }
      }
    }

    if (null == result)
    {
      // We can only get here if there's crazy spawning going on and the object
      // at the current index is still going. We will have to expand the pool.
      GameObjectPoolElement newPoolObject = new GameObjectPoolElement();
      newPoolObject.obj = (GameObject)GameObject.Instantiate(prefab, new Vector3(0,0,0), Quaternion.identity);
      result = newPoolObject.obj;
      GameObject.DontDestroyOnLoad(result);
      objects.Add(newPoolObject);
      newPoolObject.instantiateTime = Time.time;
    }
           
    // Return this object
    return result;
  }
 
  /// <summary>
  /// If the level changes, we need to inactivate all pooled objects
  /// </summary>
  void OnLevelWasLoaded()
  {
    for (int i=0; i < objects.Count; i++)
    {
      GameObject o = objects[i].obj;
      if (o.active) {
        AudioSource source = o.GetComponent<AudioSource>();
        if (null != source) {
          source.Stop();
        }
        SetActiveRecurse(o.transform, false);
      }
    }
  }
 
  /// <summary>
  /// This is called in regular intervals to inactivate expired pool objects
  /// if we are responsible for the inactivation.
  /// </summary>
  void OnPoolMaintenance()
  {
    foreach (GameObjectPoolElement e in objects)
    {
      if (e.instantiateTime + lifeTime < Time.time &amp;&amp; e.obj.active)
      {
        SetActiveRecurse(e.obj.transform, false);
      }
    }
  }
}
 

If you keep looking online, you will find other, more comprehensive Unity object pool managers. This was just enough to get me by for my purposes. Enjoy!


Check out my homepage and news feeds

And my projects!

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: