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.


Sunday 20 March 2016

Loading Screen

This is another quick tutorial on loading screens.

A loading screen is the most common way to show he user that the game is running and it provides a visual representation of the loading process.

First, I create a canvas called LoadingCanvas, which is going to contain an Image, called Loading Screen. Then, I add two more components and I place them as children of the image: a slider and a text. (Fig 1).

Fig 1

The text simply says "Loading..." and the slider will be used to show the loading progress.

Then other canvas you see in Fig 1 contains a button which, when pressed, will just load the next scene.

In Fig 2 we can see what the loading screen looks like.

Fig 2
The image is stretched so as to cover the entire canvas, and the 2 UI child elements are anchored and positioned to the center. Also, the LoadingCanvas has an additional component called CanvasGroup. I use this so I can modify the alpha parameter to turn the whole canvas invisible.

To put the loading screen to use we need 2 scripts: one for the actual loading screen, which will simply pass values to the slider, the other one is called by the button, which will activate the loading screen and pass the float value to it to be handed to the slider.

The first script, called LoadingScreen, assigned to the LoadingScreen image:


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

 Slider loadingSlider;
 CanvasGroup cg;

 // Use this for initialization
 void Start () {

  loadingSlider = GetComponentInChildren < Slider> ();
  cg = GetComponent<CanvasGroup> ();
  cg.alpha = 0;
 
 }
 
 public void AssignValue(float v)
 {
  loadingSlider.value = v;
 }

 public void Activate()
 {
  cg.alpha = 1;
 }
}

Nothing special, after getting all my references, I declare 2 methods, one that assign a float value to the slider and the other one that simply shows the whole canvas by raising the alpha value of the canvas group to 1.

Now, the other script, called LoadScene. This script is assigned to the button and its purpose is to load the next scene. Obviously, this is just our case, the loading screen could be activated by any other object that is required to load a scene.


 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
public class LoadScene : MonoBehaviour, IPointerUpHandler {
 

 public LoadingScreen loadingScreen;

 public int sceneToload;

 AsyncOperation ao = null;

 void IPointerUpHandler.OnPointerUp (PointerEventData eventData)
 {
  StartCoroutine (loadScene (sceneToload));
 }

 

 IEnumerator loadScene(int s)
 {

  ao = SceneManager.LoadSceneAsync (s);

  loadingScreen.Activate ();
  while (!ao.isDone) {
   
   loadingScreen.AssignValue (ao.progress);
   yield return null;
  }
 }



}

For this script we need to add the namespace UnityEngine.EventSystems. To add functionality to the button, I use the interface IPointerUpHandler, as you can see from the script above. This will require to override the method you see declared on line 10, which in our case will start the coroutine on line 17.

Also, we need the UnityEngine.SceneManagement so we can use the code for loading scenes as the old Application,Load(...) is now deprecated.

This coroutine accepts an integer value that represents the scene to load. Then, we assign the previously declared AsyncOperation parameter ao  to the object obtained by the static method LoadSceneAsync(...) of the SceneManagement class.

At this point we can activate the loading screen, which was given as a public value to this script so we can drag it in directly from the inspector.

The while loop is where everything happens: we check the the ao, which is our async operation, has not finished, and for each cycle we get the progress value with ao.progress, which is passed to the slider that will move accordingly.