An Intro to Unity Object Pooling – Efficient FPS Games

Making games run is good, but making them run efficiently is even better. In this guide, we’re going to talk about Unity object pooling and how you can apply this to FPS games to make them more performant on any system.

Unity object pooling is extremely powerful though, so keep in mind what we teach here can easily be applied to almost every other genre. It simply depends on your use case.

Let’s jump in then, and learn the foundations of Unity object pooling.

You can access our newest FPS course here: Construct a First Person Shooter

Unity Object Pooling – Part 1

Every time we instantiate a Bullet in our FPS game it takes some time for Unity to get that element, put it inside the game, and make it work. Creating and deleting elements are expensive operations for systems to perform – so you have to avoid this as much as possible. This is where Unity object pooling comes in.

Unity object pooling is a process through which instead of creating and deleting elements, we reuse elements already created. In this way, we can make our games more performant and just overall optimize our game better.

For our FPS, we will create a Unity object pooling system where we will get a bullet from the pool of objects.

To start, create an empty game object in the Hierarchy and name it “ObjectPoolingManager“, then follow the steps below to set up the project.

  • Create a new C# script in the Scripts folder and name it “ObjectPoolingManager.”
  • Create a new folder in the Scripts folder and name it “Game.”
  • Select both the Bullet and Player scripts and move them into the Game folder.
  • Create another folder and name it “Utils.”
  • Move the ObjectPoolingManager into the Utils folder.
  • Attach the ObjectPoolingManager script to the ObjectPoolingManager object.

Once you’ve set up everything for our Unity object pooling adventure, open the ObjectPoolingManager script in the code editor. In this code, we’ll do a quick Hello debug log to test we have a working function, and also make sure to make our ObjectPoolingManager a singleton. If we don’t do this, we’ll get messy results from multiple objects trying to interact with our Unity pool objects.

private static ObjectPoolingManager instance;
public static ObjectPoolingManager Instance { get { return instance; } }

// Use this for initialization
	void Awake () {
		instance = this;
}

public GameObject GetBullet () {
    Debug.Log ("Hello!");
    return null;
}
}

Save this script, and open the Player script in the code editor. Here, we’ll set it up so when we left click, we trigger the function we made above. This will be the the start of our Unity object pool foundations.

// Update is called once per frame
	void Update () {
		if (Input.GetMouseButtonDown(0)) {
                    ObjectPoolingManager.Instance.GetBullet ();

                    GameObject bulletObject = ObjectPoolingManager.Instance.GetBullet (true);
		    bulletObject.transform.position = playerCamera.transform.position + playerCamera.transform.forward;
	            bulletObject.transform.forward = playerCamera.transform.forward;
}	
}
}

Save this script, and navigate back to the Unity editor. Hit the Play button and test out the changes. Now when the left mouse button is clicked, you can see the debug message display in the Console window:

Unity Console with debug message

So we now have the Player accessing the ObjectPoolingManager without having to have a direct reference to it.

Next, let’s actually start working with our Prefabs to create our Unity pooling objects. Open the ObjectPoolingManager script in the code editor and make a variable for the prefab.

public GameObject bulletPrefab;

Save this script, and navigate back to the Unity editor. We need to set the reference to the Bullet Prefab on the ObjectPoolingManager script component in the Inspector:

Unity object pooling script adding to Inspector object

Back in the ObjectPoolingManager script, we can now begin our Unity pool. First, we create a list that will hold a reference to all our bullets in the game. Then, we will create a loop that preloads a specific amount of bullets into the game.

These bullets won’t be visible yet. However, by creating them now, we enable our game to run faster while players are actually playing.

// Use this for initialization
	void Awake () {
		instance = this;

		// Preload bullets.
		bullets = new List<GameObject>(bulletAmount);

		for (int i = 0; i < bulletAmount; i++) {
			GameObject prefabInstance = Instantiate (bulletPrefab);
			prefabInstance.transform.SetParent (transform);
			prefabInstance.SetActive (false);

			
		}
	}

Save the script, and navigate back to the Unity editor. Press the Play button and if you look at the ObjectPoolingManager in the Hierarchy you will see that there are in fact 20 bullets:

Unity Hierarchy showing bullet clones

However, we aren’t quite pooling objects in Unity yet. Turning back to the ObjectPoolingManager script back, we need to do one last thing to turn this into a true Unity pooling objects system: add them to our list!

// Use this for initialization
	void Awake () {
		instance = this;

		// Preload bullets.
		bullets = new List<GameObject>(bulletAmount);

		for (int i = 0; i < bulletAmount; i++) {
			GameObject prefabInstance = Instantiate (bulletPrefab);
			prefabInstance.transform.SetParent (transform);
			prefabInstance.SetActive (false);

			bullets.Add (prefabInstance);
		}
	}

Save the script, and save the Scene and project. So far we’ve laid the groundwork, but now we need to be able to activate the bullets. Otherwise, we’re actually not actually increasing our performance since we’re not using our Unity object pool for anything.

Unity Object Pooling – Part 2

Per the above, we can now preload bullets, but nothing else. So, our object pooling system is only half implemented. To make the Unity object pooling fully work, we need to open the ObjectPoolingManager script in the code editor again.

In order to use our Unity object pool, we need to find a bullet that is not active in the hierarchy with a function. If it finds one, we can simply activate it. But, if it doesn’t find one, then we can instruct Unity to make one. In this way, we don’t unintentionally limit the player’s ammo.

public GameObject GetBullet () {
		foreach (GameObject bullet in bullets) {
			if (!bullet.activeInHierarchy) {
				bullet.SetActive (true);				
				return bullet;
			}
		}

		GameObject prefabInstance = Instantiate (bulletPrefab);
		prefabInstance.transform.SetParent (transform);		
		bullets.Add (prefabInstance);

		return prefabInstance;
	}
}

Save this script, and open the Player script in the code editor. In the update function, we now want to trigger the function we wrote above when we left click. We also want to make sure that we move the bullet to where the player is. Otherwise, our Unity object pooling won’t work properly since bullets will be positioned wherever the object settings are vs. the player’s.

// Update is called once per frame
	void Update () {
		if (Input.GetMouseButtonDown(0)) {			
				GameObject bulletObject = ObjectPoolingManager.Instance.GetBullet (true);
				bulletObject.transform.position = playerCamera.transform.position + playerCamera.transform.forward;
				bulletObject.transform.forward = playerCamera.transform.forward;
			}
		}
	}

Save this script, and open the Bullet script in the code editor. Here, we want to adjust our destruction functionality. Instead of destroying the bullet, for the purposes of object pooling in Unity, we simply want to disable the object in the hierarchy.

// Update is called once per frame
	void Update () {
		// Make the bullet move.
		transform.position += transform.forward * speed * Time.deltaTime;

		// Check if the bullet should be destroyed.
		lifeTimer -= Time.deltaTime;
		if (lifeTimer <= 0f) {
			gameObject.SetActive (false);
		}
	}
}

Save the script, but before we go, we need to fix the Start function in the Bullet script. Because we’re not destroying our game object any more, we need to make sure we can change the lifeTimer so that the bullet actually disappears. In other words, every time the left mouse button is clicked the lifetime duration is set back to its original state.

// Use this for initialization
	void OnEnable () {
		lifeTimer = lifeDuration;
	}

Save the script and test the changes again by pressing the Play button. You will see your bullets properly activate and deactivate in your hierarchy.

While a simple showcase of Unity object pooling, this is a quick path to improving performance – and will be essential as your games get more complex.

Transcript Part 1

Even though we worried about making the bullets not last forever, so they’re not going to use very important space in memory, there is something else we have to worry about. Every time we add a bullet to this game, every time we do this instantiation process, it takes some time for Unity to get that element, to put it inside the game, and make it work, okay.

So actually creating and deleting elements, they are expensive operations. So you have to avoid that as much as possible. And at that moment, you might ask yourself, okay I’m a bit confused now, the bullet is something that has to be instantiated, but I have to avoid instantiating things. And it sounds funny if you say it, but what you have to worry about is Unity object pooling. And that Unity object pooling is as follows.

You are going to have a little manager, something that is going to hold a certain object, a bullet, for example, then it’s going to preload a certain amount of objects. So maybe you want to preload 20 bullets, for example. And you’re going to make these objects inactive. They’re going to be kind of dormant. They’re going to be unavailable for use. Basically, they are sleeping, and they are hidden in the game.

And every time you want to spawn a bullet, instead of instantiating, you’re going to get a bullet from the pool of objects, because we already have one available. So that bullet is going to be activated, and you can do whatever you want with it. So, it might seem a bit confusing, a bit complex for you to understand that, maybe if that’s your first game, or one of your first courses you’re taking on game development, but this is one of the most important subjects in game development, which is Unity object pooling. It’s going to save you lots of processing power. And memory as well, okay.

So, we want to make a pooling manager. So I’m going to come here in the Hierarchy. We need to right click, and need to choose create empty object. We’re going to take this game object from, outside the game controller, it’s going to be right here, and I’m going to rename that, let’s see, PoolingManager, or ObjectPoolingManager, or ObjectPooler, whatever name you think is going to work well. I’m going to call it ObjectPoolingManager, okay. We hit enter, it’s in the position 0,0,0. It’s not going to make any difference later, but still, let’s leave it like this.

And now we need to make a script for it. So I’m going to the scripts folder, I’m going to right click, select create, C# Script, and then ObjectPoolingManager and then hit enter. And before we move forward, we’re going to make a folder. We’re going to organize these scripts. So I’m going to right click, select create, folder. I’m going to name this as game. And I’m going to select both the bullet and the player and drop it here. And I’m going to make another folder, and I’m going to name that Utils.

Okay, I’m going to put the ObjectPoolingManager inside there. I worry a lot about folders because it’s important to keep things organized. And now that we did this, we’re going to put a reference to that ObjectPoolingManager. We need to drag and drop the script here. So, what I’m going to do is. At the player, okay so if you open the player script. Which is this one.

Instead of instantiating the bullet like this, calling instantiate, we’re going to try to get a bullet from the ObjectPoolingManager. So we want to access the ObjectPoolingManager, but how do we do this? There are two ways. One way is to make the player to reference the ObjectPoolingManager and well, make a direct call for it. But that might be a bit ugly, and that’s not going to be easy to reuse later, because if you make other elements, if you want to pool a particle effect for example, or other bullets and several different things.

If you want to access the pooling manager from other places, from the player, from the enemy, from the turret, from the door, whatever elements you have, you need to think of a better way to do this. So we need to make a little mix of the simple return pattern.

So, the ObjectPoolingManager is going to have a reference to the only instance of that pooling manager that is going to be available in the scene. Which is this little guy here. So what I’m going to do here, is type private static ObjectPoolingManager instance, so there’s going to be a reference to the instance of the ObjectPoolingManager.

And now, I’m going to type public, then static ObjectPoolingManager Instance, with an uppercase I. I’m going to open and close curly braces, and type get, and, once again, between curly braces, return instance, okay. So it’s a very simple pattern that we’re making for defining a property. If we go the player and we type player. If we type ObjectPoolingManager.instance, we should access the instance that we have here. However, right now it’s going to return null.

So what we can do here is, instead of using start, we can use Awake. And we can type instance equals to this. So, the Awake method happens before the Start method. So we’re going to insure that instance is going to be set once this Awake method is going to be called, okay. And all of the other elements can access it in their Start method, otherwise we have to worry about the script execution order, but let’s worry about that later.

Okay, so this is going to be, to make us able to access the ObjectPoolingManager, so as a simple test, I’m going to make here another method, let’s get rid of the update. And it’s going to be public and then GameObject, and let’s name this as GetBullet, for example. We’re going to name it like this. And I’m going to print just a message here. I’m going to type Debug.Log and say hello. And I’m going to type return null. Okay, we’re not going to return any bullets right now. So let’s save this.

In the player, just to test it out, I’m going to type ObjectPoolingManager here where we spawn a bullet dot Instance.GetBullet, which is this one here, okay. So let’s see what happens if we do this. I’m going to save, let’s go to Unity. Let’s select the console by the way to see the messages. And now, if we press play, here we are. If I click, I’m printing the hello message. Which means the player is accessing the ObjectPoolingManager without having a direct reference to it. Which is perfect, exactly what we want.

Now, we want the ObjectPoolingManager to be able to access the bullet prefab, to know what the bullet prefab is. So, I’m going to type here, let’s type below these pair of lines, public GameObject bulletPrefab. Okay, so let’s save this, by the way, and go to Unity, wait a little bit, and in the ObjectPoolingManager we’re going to drag and drop the bullet, just like we did with the player. So, prefabs, bullet, drag, drop. Perfect, there we go.

And now, what we want to do here, is we want to preload a certain amount of bullets. So I’m going to type public int bulletAmount, and let’s use 20 as default. And here, in the Awake method, we’re going to instantiate 20 bullets, but we’re going to leave them disabled. So, how are we supposed to do this? We’re going to type it like this: for int I equals to zero, while I is lesser than 20, then do something, and finally i++. So, this is a basic loop that’s going to execute 20 times. But instead of using 20 here, we use bulletAmount.

Okay, so you can scale that to whatever you want. And when this happens, we’re going to type GameObject bulletObject, or we can just type here prefabInstance, because we want to make that easier to reuse later, equals to instantiate and then bulletPrefab. Okay, once we do this. Let me, instantiate, there we go bulletPrefab. Once we do this, we also want this bullet to be under the ObjectPoolingManager to keep things organized, otherwise the bullets are going to appear at the root of the Hierarchy.

So, prefabInstance. transform.SetParent and we pass transform as a parameter, because it’s going to reference the ObjectPoolingManager. And after we do this, I’m going to type prefabInstance.SetActive and I’m going to pass false as a parameter, okay. So we’re going to instantiate 20 bullets, and all of them are going to be disabled. So this time, if I save this. I go to Unity, and I wait for the processing to be finished.

If we press play, and we look at the Hierarchy, you see that the ObjectPoolingManager has an arrow, because it contains several objects inside it. If you open, you’re going to see the 20 bullets in there sleeping, in their dormant state. Okay, which is good, now we have them. Now, for the getBullet method. What we have to do, is we have to store all of the references for the bullets that we have here somewhere. So we’re going to need a list of bullets. So here, I’m going to type private List of type GameObject and it’s going to be named bullets, simple as that.

In the Awake method, before we instantiate the bullets. Let me, let’s add a comment here, so preload bullets. We need to type bullets equals to a new List of GameObject. Okay, that’s a generic list of GameObject. And you can also start by including its capacity. So we already pre-allocate a certain amount of spaces in our memory to load bullets, and the capacity is basically bulletAmount.

Okay, so we’re going to make a new list that can store up to 20 bullets, which is good, okay. And for the getBullet, we have to worry about something, we need to store the references for the bullets, before we try to use the getBullet.

So here, when we instantiate the prefabs, I’m going to type bullets.Add, in between parentheses, I’m going to pass prefabInstance, just like that, okay. And if you go to Unity, well you’re not going to see the changes yet, we’re simply going to preload the bullets, they’re going to be stored internally in the list, in a generic list. But now we have to learn how to use them.

Transcript Part 2

We have now set the entire ground work for making the Unity object pooling to fully work. So now we need to go to the Unity object pooling manager, and we have to make the get bullet method to work. And how we’re going to do this, let’s take off these two lines. We want to iterate over all of the prefab instances that we have loaded here, and we need to check if any of them are inactive, because if it is then that’s the bullet that we’re going to use.

So to do this, we’re going to type for each game object bullet in bullets. That’s an easy way to make a loop. We’re simply going to check that list for all of the bullets that we have.

Now we check if bullet.activeInHierarchy, this is a property that says basically if the game object is active in the scene. But instead of checking if it’s active, we want to check if it’s inactive. So we add an exclamation mark in the beginning of this statement. So we check if we have anything inactive. If this is the case, then this is the bullet that we want to use.

So the first step is we’re going to make it active by typing bullet.SetActive and pass true, as a parameter. So the bullet is going to be back to how it should be. And after that we type return bullet. But there is a problem here. Maybe all of the bullets are being used, so we are going to iterate over all of them. None of them are going to be available because they are all already in the scene.

And then we have to return something. If we reach this state, then it means we should instantiate another bullet. So we can just grab the code that we made here for making a new prefab. You can just copy that and paste here.

We’re making a new bullet. There’s only two to use this set active here. So we basically make a new prefab instance. We set its parent to be the Unity object pooling manager, and finally we store it in the bullet’s array, in the bullet’s list. And after we do this, we type return prefab instance. So that’s going to return what we just added. And we’re going to basically add the bullet. And back in the player, instead of using this instantiate method call here, we’re going to use the get bullet from the Unity object pooling manager. So I’m going to paste it here.

Alright, so now the bullet object is spawned by using the Unity object pooling manager. I’m going to remove the reference for the bullet prefab. The player no longer needs to know what is the exact prefab. And in the bullet there is one important step here.

Instead of calling destroy game object, instead of removing the bullet from the game entirely, we’re going to type GameObject.SetActive false. Otherwise the bullet would disappear after it has been instantiated. And then it would happen over and over again. So we need all of these changes. So let’s test them out. I’m going to save that. Let’s go to Unity. Wait for the compilation to be complete. I’m going to change the bullet amount to two. And I’m going to press play.

So if we go to look at the Unity object pooling manager, we have two bullets. If I shoot one, you see that it got active and then deactivated. But then if I click again, the bullet quickly blinks in the screen. So why is this happening? If you go to the bullet script, you’re going to see that the live timers is only set in the start method. And very important to know is the start method is only called when the object is instantiated. But since this is going to be pooled, we’re going to change the start to OnEnable.

So let’s give this a try. We’re going to save this. Let’s go to Unity, wait a little bit more, and we’re going to press play. I’m going to click here, and yes, OnEnable with the case. Now every time I click, the duration is set back to its original state, so the entire lifetime. And take a look at the Unity object pooling manager to see how things are working here.

If I click bullet one is instantiated. If I click twice, both of them are active, because we need two bullets in this case. We’re basically reusing bullets no longer re-instantiating. We’re just retrieving from the pooling manager. If I click several times, new bullets are adding to the pooling manager. So I can click four times very quickly and it’s going to use all of the bullets available.

So basically you can click like crazy, very, very frequently, and the bullets are going to be reused. No more memory allocation, no more very large amount of processing involved in adding these bullets. And now you can pretty much see the limit that you have here. So seven bullets, but let’s change that to eight, just to make sure. So the game is going to have a little time to load in the beginning, but that’s going to save you a lot of time in the future and it’s going to make sure that the game is going to be very efficient.

Interested in continuing? Check out our new Construct a First Person Shooter course, which is part of our Unity Game Development Mini-Degree.