If you don’t know what is object pooling, it’s a perfect time to learn this technique. It’s not complicated so it’s easy to understand even for beginners. You can create complex games in Unity without object pooling but this approach can save a few frames which affects overall users’ comfort.
Introduction
Let’s assume that we are creating shooter game. There is a main character which holds machine guns. If player clicks fire button, character fires tons of bullets per second. We also want to remove these bullets from the scene after a moment of time. It requires calling Instantiate and Destroy functions many times. It’s not a secret that new object’s creation is resource consuming. If many objects are creating and removing from the scene, Garbage Collector can be started to remove unnecessary objects from memory. This may result in game’s slowdown which is very undesirable.
Better approach is to reuse once created objects. If our game needs more bullets, we create them. If we don’t need them anymore, we disable them instead of removing it from the scene.
Object pooling – example
Today, our goal is to create a simple example with object pooling. We will create sample scene with two spawners. The first spawner will create objects in ‘traditional’ way and the second one will use object pooling. To make it as simple as possible, spawners will create cubes and spheres which fall from the top of the screen.
First, create generic spawner’s script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
using UnityEngine; using System.Collections; public class Spawner : MonoBehaviour { public GameObject spawnables; // objects holder public float interval = 0.1f; // delay between spawns public int spawnedObjects; // we want to check how many objects we spawned so far void Awake () { spawnedObjects = 0; } void Start () { StartCoroutine (SpawnCoroutine ()); // start spawning coroutine } // this function is set as virtual, we will override it in our spawners public virtual GameObject GetObject () { return null; } // here we set all necessary object's properties like start position and parent transform public void InitObject (GameObject go) { if (go != null) { Vector3 randomPosition = transform.position; randomPosition.x = Random.Range (-(transform.localScale.x / 2f), (transform.localScale.x / 2f)); randomPosition.x += transform.position.x; go.transform.position = randomPosition; go.transform.parent = spawnables.transform; go.GetComponent<Rigidbody> ().velocity = Vector3.zero; go.SetActive (true); } } private IEnumerator SpawnCoroutine () { while (true) { InitObject (GetObject ()); // get object to spawn and set its properties yield return new WaitForSeconds (interval); } } } |
which implements all necessary functionalities. It contains SpawnCoroutine (read more about coroutines: Coroutines – the first encounter!) which is responsible for periodically objects creation. There is also InitObject method which sets object’s random position (based on spawner’s scale and position), resets its speed and set it as active. Virtual function GetObject will be overridden in target spawners’ implementions.
To create ‘classic’ spawner we used this script:
1 2 3 4 5 6 7 8 9 10 11 12 |
using UnityEngine; using System.Collections; public class ClassicSpawner : Spawner { public GameObject prefab; // object to be spawned public override GameObject GetObject () { spawnedObjects++; return Instantiate (prefab) as GameObject; // just return new object } } |
As you can see, we used inheritance. As a prefab we will use a cube with rigidbody attached. Spawner with object pooling is created with:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
using UnityEngine; using System.Collections; public class SpawnerWithPooler : Spawner { private Pooler pooler; void Awake () { pooler = GetComponent<Pooler> (); // get object pool component } public override GameObject GetObject () { GameObject go = pooler.GetObject (); // tell pooler that we need another object to spawn spawnedObjects = pooler.PooledObjectsCount (); // get pool's size return go; } } |
There is the most interesting part. This spawner uses Pooler object (as component attached to second spawner) to get new objects instances. Pooler‘s script is shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
using UnityEngine; using System.Collections; using System.Collections.Generic; public class Pooler : MonoBehaviour { public GameObject prefab; // object to be spawned private List<GameObject> objects = new List<GameObject> (); // list of currently spawned objects public GameObject GetObject () { GameObject result = null; // first, check if there is an unused object in the pool for (int i = 0; i < objects.Count; i++) { if (!objects [i].activeInHierarchy) { // if there is an unused one result = objects [i]; // get its reference break; } } // if result is still null which means that we don't have available objects if (result == null) { result = Instantiate (prefab) as GameObject; // create new result.SetActive (false); // set it as inactive objects.Add (result); // add to pool } return result; } public int PooledObjectsCount () { return objects.Count; } } |
Pooler contains only two funtions. GetObject function checks if there is an unused object in the pool. If not, it creates new object and adds it to list (read more about Collections in C# and Unity). This script is completely generic so it can be used for pooling every kind of objects you need. You can combine it with singleton pattern to make it accessible from everywhere.
Ok. We said that our scene should contain two spawners so we need two collectors also. First one will destroy objects and the second one will just disable them. We created simple Collector script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using UnityEngine; using System.Collections; public class Collector : MonoBehaviour { public bool destroyOnTouch = true; void OnTriggerEnter (Collider c) { if (destroyOnTouch) GameObject.Destroy (c.gameObject); else c.gameObject.SetActive (false); } } |
Next, create objects like in the screen:
Red Spawner and Collector is our ‘classic’ implementations and the green ones are created with object pooling. Take a look at this animation to see how it works:
One important thing! Spawned objects don’t interact with themselves. We used Physics Layers (read more here: Physics layers in Unity) to be sure that cubes and spheres don’t interfere with each other. It look’s like the spawners work in the same manner but check Inspector to see the difference. There are only about 25 spawned objects with object pooling. ‘Classic’ spawner will create as many objects as possible. It will make Garbage Collector busy in a short period of time.
We hope that everything is clear. As usual we share with you complete project here: Download
Do you have any questions? Comment below 🙂