Wednesday 23 March 2016

Object pooling in Unity

When playing games, accessing the memory is one of those things that slows your game down dramatically. Although this might not be much of an issue if you are running a game on the latest computer, it will most certainly become a problem on mobile devices.

This happens when objects are created (instantiated) and destroyed repeatedly during runtime. These usually are recyclable objects, like projectiles, disposable enemies or even background objects, which are continuously destroyed and recreated.

Object pooling is a technique that helps avoiding this problem. By instantiating in the scene all the enemies we need, for example, we can simply activate them a few at the times when we need them. Then, when they need to be removed from the scene, because let's say, they have been killed, we deactivate them, so they are no longer present, but still available for use later on. This way, we only interact with the memory only once, at the beginning, and leave the objects in the scene and simply activate them whenever is necessary.

In this quick and short tutorial, we see how we can code object pooling. In this example, we will be shooting projectiles from the camera. We will create all the objects first and call them when it is time to shoot.

For our projectile, just create a Sphere, set the collider to isTrigger and give it a Rigidbody, with no gravity.

This is the script for the projectile, called, shockingly, Projectile:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Projectile : MonoBehaviour {

 public float speed;


 void OnEnable () {
  
  StartCoroutine (die ());
   
 }
 

 void Update () {
 
  transform.position += new Vector3 (0, 0, speed * Time.deltaTime);
 }

 IEnumerator die()
 {
  yield return new WaitForSeconds (5);
  gameObject.SetActive (false);
 }
}

The coroutine will simply deactivate the object after 5 seconds and it is started any time the object is activated, causing the OnEnable() method to be called. During the Update(), all I do is to move the object along the Z axis.

Attach the script to the Sphere just created and save the object as a prefab.

Now, the Shooting 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
public class Shooting : MonoBehaviour {

 public GameObject projectilePrefab;

 GameObject[] allProjectiles;
 GameObject projectilesParent;


 void Start () {

  projectilesParent = new GameObject ("ProjectilesParent");
  allProjectiles = new GameObject[50];

  for (int i = 0; i < allProjectiles.Length; i++) {
   
   GameObject obj = Instantiate (projectilePrefab, transform.position, Quaternion.identity) as GameObject;
   allProjectiles [i] = obj;
   obj.transform.SetParent (projectilesParent.transform);
   obj.SetActive (false);
  }
 
 }

 void Update()
 {
  if (Input.GetMouseButtonDown (0))
   Shoot ();
 }

 void Shoot()
 {
  for (int i = 0; i < allProjectiles.Length; i++) {

   if (!allProjectiles [i].activeInHierarchy) {
    allProjectiles [i].transform.position = transform.position;
    allProjectiles [i].SetActive (true);
    break;
   }
  }
 }

}

The public GameObject on line 2 is our projectile prefab which we'll assign in the inspector

Then, we create the array of GameObjects and also an empty GameObject which simply act as a parent for all projectiles, so they don't mess up the hierarchy.

In the Start() method, we initialize the parent object as well as the array. Then , we proceed  to fill it up. With the for loop, we instantiate multiple copies of the prefab, put each one of them into the array, set their parent to the parent object and set them as inactive in the hierarchy. This way, they won't be "physically present" in the scene.When you run the scene, your hierarchy should look like this:


All the projectiles are now in the scene and waiting to be used.

In the Update(), we simply detect any mouse click so we can call the Shoot() function when it happens.

When it is time to shoot, we iterate inside the array of projectiles, looking for a non active one (line 34). When found, we set its position to the current position, which in this case is the camera position, and then we simply activate it, so the game object will now move as dictated by its Projectile script.

Remember, after 5 seconds the projectile object will be deactivated, which means it will be available to be shot again..

It is important to remember that by using this technique we need to assign the right number of objects to be instantiated. For example if we expect to be able to shoot more than 50 objects at the time, we need to create a larger array. If all the projectiles in the array are active, we simply wouldn't be able to shoot anymore as we need a non active one in order to do so.


No comments:

Post a Comment