Skip to content

Unity Resource: GameObjectPool

January 6, 2010
tags:

Unity LogoOne way to improve a game’s performance in Unity3D is to avoid creating and destroying game objects, and instead activating/deactivating them. However, doing so can be a bit tricky and time consuming—especially if there isn’t a hard limit on the number of game objects that can be active at any given time, and/or their lifetimes are unpredictable.

Below is a pool class I developed for Intergalactic Sheep Pong. Here’s a rundown of its features.

  • It allows you to pre-instantiate a specific number of prefab instances.
  • It dynamically grows if you end up needing more objects than you originally allocated.
  • It allows objects to be spawned/unspawned in any order (in other words, it doesn’t assume that the objects spawned first will go back into the pool before objects spawned later).
  • It doesn’t require any special scripts to be attached to the objects it contains. They only need to use the built-in OnEnable/OnDisable functions to do their initialization and cleanup.

I hope you find it useful. Feel free to use/tweak/retool for your own purposes.

UPDATE: At the request of a reader, I created a little example of the pool in action. (Click here to download the project zip.) If you click the Spawn button fast enough, you should see the pool dynamically grow to accommodate more balls.

Click here to see the code in action.

Here’s the code for the pool used in the above example.

GameObjectPool.js

#pragma strict

// A general pool object for reusable game objects.
//
// It supports spawning and unspawning game objects that are
// instantiated from a common prefab. Can be used preallocate
// objects to avoid calls to Instantiate during gameplay. Can
// also create objects on demand (which it does if no objects
// are available in the pool).
class GameObjectPool {
	// The prefab that the game objects will be instantiated from.
	private var prefab : GameObject;
	// The list of available game objects (initially empty by default).
	private var available : Stack;
	// The list of all game objects created thus far (used for efficiently
	// unspawning all of them at once, see UnspawnAll).
	private var all : ArrayList;

	// An optional function that will be called whenever a new object is instantiated.
	// The newly instantiated object is passed to it, which allows users of the pool
	// to do custom initialization.
	private var initializationFunction : Function;
	// Indicates whether the pool's game objects should be activated/deactivated
	// recursively (i.e. the game object and all its children) or non-recursively (just the
	// game object).
	private var setActiveRecursively : boolean;

	// Creates a pool.
	// The initialCapacity is used to initialize the .NET collections, and determines
	// how much space they pre-allocate behind the scenes. It does not pre-populate the
	// collection with game objects. For that, see the PrePopulate function.
	// If an initialCapacity that is <= to zero is provided, the pool uses the default
	// initial capacities of its internal .NET collections.
	function GameObjectPool(prefab : GameObject, initialCapacity : int, initializationFunction : Function, setActiveRecursively : boolean){
		this.prefab = prefab;
		if(initialCapacity > 0){
			this.available = Stack(initialCapacity);
			this.all = ArrayList(initialCapacity);
		} else {
			// Use the .NET defaults
			this.available = Stack();
			this.all = ArrayList();
		}
		this.initializationFunction = initializationFunction;
		this.setActiveRecursively = setActiveRecursively;
	}

	// Spawn a game object with the specified position/rotation.
	function Spawn(position : Vector3, rotation : Quaternion) : GameObject {
		var result : GameObject;

		if(available.Count == 0){
			// Create an object and initialize it.
			result = GameObject.Instantiate(prefab, position, rotation) as GameObject;
			if(initializationFunction != null){
				initializationFunction(result);
			}
			// Keep track of it.
			all.Add(result);
		} else {
			result = available.Pop() as GameObject;
			// Get the result's transform and reuse for efficiency.
			// Calling gameObject.transform is expensive.
			var resultTrans = result.transform;
			resultTrans.position = position;
			resultTrans.rotation = rotation;

			this.SetActive(result, true);
		}
		return result;
	}

	// Unspawn the provided game object.
	// The function is idempotent. Calling it more than once for the same game object is
	// safe, since it first checks to see if the provided object is already unspawned.
	// Returns true if the unspawn succeeded, false if the object was already unspawned.
	function Unspawn(obj : GameObject) : boolean {
		if(!available.Contains(obj)){ // Make sure we don't insert it twice.
			available.Push(obj);
			this.SetActive(obj, false);
			return true; // Object inserted back in stack.
		}
		return false; // Object already in stack.
	}

	// Pre-populates the pool with the provided number of game objects.
	function PrePopulate(count : int){
		var array : GameObject[] = new GameObject[count];
		for(var i = 0; i < count; i++){
			array[i] = Spawn(Vector3.zero, Quaternion.identity);
			this.SetActive(array[i], false);
		}
		for(var j = 0; j < count; j++){
			Unspawn(array[j]);
		}
	}

	// Unspawns all the game objects created by the pool.
	function UnspawnAll(){
		for(var i = 0; i < all.Count; i++){
			var obj : GameObject = all[i] as GameObject;
			if(obj.active)
				Unspawn(obj);
		}
	}

	// Unspawns all the game objects and clears the pool.
	function Clear(){
		UnspawnAll();
		available.Clear();
		all.Clear();
	}

	// Returns the number of active objects.
	function GetActiveCount() : int {
		return all.Count - available.Count;
	}

	// Returns the number of available objects.
	function GetAvailableCount() : int {
		return available.Count;
	}

	// Returns the prefab being used by this pool.
	function GetPrefab() : GameObject {
		return prefab;
	}

	// Applies the provided function to some or all of the pool's game objects.
	function ForEach(func : Function, activeOnly : boolean){
		for(var i = 0; i < all.Count; i++){
			var obj : GameObject = all[i] as GameObject;
			if(!activeOnly || obj.active){
				func(obj);
			}
		}
	}

	// Activates or deactivates the provided game object using the method
	// specified by the setActiveRecursively flag.
	private function SetActive(obj : GameObject, val : boolean){
		if(setActiveRecursively)
			obj.SetActiveRecursively(val);
		else
			obj.active = val;
	}
}
Advertisement
23 Comments leave one →
  1. hengxin permalink
    January 28, 2010 8:13 pm

    Not understand it, an example can be a unity

  2. Ehren permalink
    January 30, 2010 3:45 pm

    Good suggestion, hengxin. I’ve added an example above.

  3. hengxin permalink
    January 31, 2010 12:10 pm

    Thank you very much
    Another problem is that the Prefab object can not have child objects Prefab

  4. Ehren permalink
    February 1, 2010 9:49 am

    To handle child objects, simply pass true instead of false for the setActiveRecursively parameter (the last parameter in the GameObjectPool constructor). That will ensure that all child objects get activated/deactivated.

    If you don’t have child objects, setting active directly is faster.

  5. hengxin permalink
    February 18, 2010 5:16 am

    How to construct arguments, please enlighten, thank you

    • Ehren permalink
      February 18, 2010 10:16 am

      Hi hengxin. I’m not sure I understand. Can you be more specific about what you’d like me to clarify?

  6. January 5, 2011 4:01 am

    Hey, great piece of work. However, it should be noted that this can be detrimental to performance (in some cases) due to how it uses arrays and constant Y axis checking.

    For example, if you have 8-10 different prefabs you want to spawn/ unspawn quite often and have multiple instances of each on the screen at one time. I found that pooling all of them will make a visible dent in performance versus instantiating/ destroying them.

    • Ehren permalink*
      January 7, 2011 11:12 am

      Hmmm…the Y axis checking isn’t related to the use of pools, but it’s possible that Instantiating/Destroying could be faster than using a pool in certain cases. In Langman, I pool/unpool hundreds of objects in a single frame when a new level is loaded, and there isn’t a noticeable lag. Can you provide an example project showing the perf hit?

  7. January 8, 2011 12:16 pm

    Sure, I’ll drop a comment with a test project when i get the time (100 yard release dash right now). Dunno how visible this perf. hit would be on modern systems but I’ve tried this with one of my iPhone projects.

    The project was already maxing the iPhone’s limited resources (while still running at ~30fps). When testing the object pool, the frame rate dropped bellow tolerable.

  8. January 11, 2011 12:24 pm

    Ehren,

    This is wonderful and greatly appreciated! Thanks for posting and sharing.

    I implemented it with no problems at all.

    Any chance you might provide a C# version?

    Thanks
    Allan

  9. January 11, 2011 12:32 pm

    Ehren,

    I also noticed you use SendMessage in InitializeGameObject in CentralController.js.

    According to the Unity Community wiki, this should be avoided for iPhone development and replaced by a direct method call.

    Here is what the wiki says and the link:
    “Avoid SendMessage() calls, create a script/class variable to hold a reference to the specific scripts you want to call, and use a standard method call. Method calls are ~100x faster than SendMessage()!”

    http://www.unifycommunity.com/wiki/index.php?title=IPhone_Optimization_Tips

    Would love to see a C# version.

    Allan

  10. Ehren permalink*
    January 12, 2011 9:18 am

    @Azzogat: One thing I forgot to mention is that for iPhone, you might want to try Spawning and then Unspawning the number of game objects you think you’ll need up front (in Start). That way you’ll take the hit for creating them once, which should make the spawning during gameplay faster.

    @Chaneya: The SendMessage in the example could be replaced with a GetComponent call, but that’s also slow. Because the initialization function is only called when a pooled object is created, you only take the SendMessage hit once per pooled object. If you Spawn/Unspawn all your game objects up front (in Start), you can take the perf hit for all objects before your game begins. I don’t think the iPhone guidance means you should never call SendMessage, just that you should call it sparingly (or refactor to avoid it when possible).

  11. January 12, 2011 12:23 pm

    Ehren,

    That’s a very good point regarding SendMesssage. I agree….I think the idea is to not use it per frame in say…the Update loop.

    I took the liberty of attempting to convert your code to C# but I am stuck on InitializationFunction in GameObjectPool.js. and one function in CentralController.js. I think I have everything else converted. I have a few years of experience in C# but no history in Unity/Javascript at all.

    The first issue is that C# does not have a Function Type to replace.
    And I’ve never seen a procedure/method declared as a variable type in C#.

    In GameObjectPool.js, At the top:
    private var initializationFunction : Function;
    (This appears to declare a method as a Function declaration.)??

    function GameObjectPool() receives initializationFunction : Function in its constructor and makes it equal to the declaration at the top.
    this.initializationFunction = intializationFunction;
    I have no idea how to convert this to C#.

    function ForEach() also receives func : Function in its constructor and then uses a foreach loop to cycle through the objects in the array.
    Again Function has me baffled.

    In CentralController.js:
    function Awake() instantiates the ballPool calling the function InitializeGameObject in it’s contructor. Calling a procedure/method from the Awake constructor. Again, no idea how to convert to C#.

    Any tips for these two items?

    Thanks
    Allan

  12. Ehren permalink*
    January 12, 2011 1:13 pm

    Delegates are used to pass function references in C#.

    Try changing all the Function references to System.Action<GameObject>. I think that should make everything work.

  13. May 7, 2011 10:32 am

    I converted your script to C# and it works very nicely.

    You can download it here: http://download.mediamonkey.nl/GameObjectPool.cs

    Example:

    public GameObject ballPrefab;
    protected GameObjectPool ammoPool;
    protected Transform spawnPoint;

    void Start() {
    ammoPool = new GameObjectPool(ballPrefab, 5, initBallAction, false);
    spawnPoint = transform.Find(“spawnPoint”); // just an empty gameobject as locator
    }

    void Update() {
    if (Input.getMouseButtonDown(0)) fire();
    }

    void fire() {
    GameObject ball = ammoPool.spawn(spawnPoint.position, spawnPoint.rotation);
    ball.rigidbody.velocity = transform.up * 20f;
    }

    void initBallAction(GameObject target) {
    Debug.Log(“ball created”);
    // set pool to the AutoDestruct behavior so the ball can return to it on destuction
    AutoDestruct ad = target.GetComponent();
    if (ad) ad.pool = ammoPool;
    }

    • May 7, 2011 10:35 am

      Very cool!

    • August 5, 2011 3:11 pm

      What’s the AutoDestruct ad = target.GetComponent(); ???
      It is giving me an error when using it. Do I need to include something or is it a function you didn’t put up??

      • August 11, 2011 2:38 am

        Hi Willie,

        The post contained more code, but the site interpreted it as html and removed it.
        AutoDestruct ad = target.GetComponent();
        should’ve been:
        AutoDestruct ad = target.GetComponent ();

        It is a script that automatically removes the object after a certain amount of time.
        I probably should’ve included the code, but it seemed as a no-brainer to me.
        In the initBallAction method I assign the correct pool to the script.
        When the audodestruction is triggered, it destroys the gameobject by placing it back into the pool.

        You can download the script here: http://download.mediamonkey.nl/source/AutoDestruct.cs

        With regards,
        Bart

      • August 11, 2011 11:34 am

        You ended up typing the same thing twice with regard to the autodestruct. I imagine you meant to type in a parameter for the get component the second time.
        Also in the code you linked your destroy is capitalized where it should be lowercase.

      • August 12, 2011 1:33 am

        Ah yes, the site swallowed the code again.
        I meant to type GetComponent < AutoDestruct > ()
        Replace the escaped codes with the correct characters.

        About the capitalization: in C# methods are in caps. It takes some getting used to it :) But when everyone is coding like that it is considered the norm, so I’ll just follow along.

        So remember: from now on write your methods in C# with an uppercase first character.

  14. September 15, 2011 9:18 am

    Perfect! Just what I need. I was getting a lot of lag from calling Instantiate in my game. This may be the solution I’m looking for.

    With your permission, and with all credit due, I’d like to port this over to C# and post this up on my blog so my readers can benefit from this as well. As I said before, I’ll credit you, and also link back to this page so people can see the original, if that’s all OK with you.

    • September 15, 2011 9:38 am

      Sure, feel free to use the code in any way you like. But you might want to check out the C# version linked in the comments above before you go through the work of porting it yourself. :)

Trackbacks

  1. Trace Invaders and Unity3D: Development Tips « vitagames

Leave a Reply

Fill in your details below or click an icon to log in:

Gravatar
WordPress.com Logo

Please log in to WordPress.com to post a comment to your blog.

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.