Capto_Capture 2019-02-10_03-55-23_PM
George Rowland

George Rowland

Object creation the easy way

Or is it…

This is a continuation down the refactoring road after I played my initial build on a mobile device. As you may recall, over the last weeks I have made changes to reduce the load from physics calculations on my device. This week I will talk about the cost of instantiation versus pooling.

  1. Mesh Collisions are expensive (especially with overly complex geometry)
  2. Physics are expensive
  3. Object creation the easy way (instantiation vs pooling)

The discussion over the last few weeks has been about the reduction of physics load on our low-end devices. This week I will dive into an issue I ran into while playing the game for a longer session. The easy way for me to create and destroy objects in Unity is to use Unity’s Instantiate() and Destroy() functions. You may or may not know that creating objects and destroying objects on a resource-starved device can be extremely taxing.

C# is a managed language that cleans up unused objects once they no longer have references. When using a managed language in game development the cleaning mechanism, garbage collection in C#, can take a lot of CPU cycles. This short burst of heavy resource use can cause performance issues. The fact that you do not control the timing of this burst is also problematic. This burst was causing a perceivable drop in frame rate during my action-oriented game. One easy way to prevent large amounts of garbage from being accumulated is to create a pooling system.

Pooling is where you disable objects that are no longer needed and enable objects that you currently have a use for. In this game, there are hundreds of asteroids, shot particles, and materials floating in the scene. If we create several of each object at the start of the game and only create new ones if we are out of pre-created objects, we can reduce the load on the garbage collection when it is needed.

Through the use of pooling, I was able to control the need to collect unused objects. I now pool shot particles, asteroids, materials, and ships. With a little bit of work, we have now saved ourselves some headaches later down the road.

Below is a simple pooling sample to show the idea written in code. This is a very simple pooling class. Typically I would use an enum for the types but for brevity I used strings. Also, I typically use static methods and variables to prevent the need to have references to the instance to get and pool objects.

 
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;

public class Pooler
{
    //Set this in inspector to number you wish
    //You can make multiple of these for different values per object
    public int numberOfObjects;

    //Set the prefab gameobjects in the inspector
    public GameObject asteroidPrefab;
    public GameObject shotPrefab;


    //Create Lists of objects by type
    List<GameObject> asteroids;
    List<Gameobject> shotParticles;

    void OnEnable()
    {
        GameObject temp;
        asteroids = new List<GameObject>();
        shotParticles = new List<GameObject>();

        for(int i = 0; i < numberOfObjects; i ++) 
        { 
            temp = Instantiate(asteroidPrefab); 
            temp.transform.SetParent(transform); 
            asteroids.Add(temp); 
            temp = Instantiate(shotPrefab); 
            temp.transform.SetParent(transform); 
            shotParticles.Add(temp); 
         } 
     } 

     public GameObject GetObject(string objectType) 
     { 
            GameObject objectToReturn = null; 
            if(objectType == "Asteroid") { if(asteroids.Count > 0)
            {
                objectToReturn = asteroids[0];
                objectToReturn.SetParent(null);
                asteroids.RemoveAt(0);
            }
            else
            {
                objectToReturn = Instantiate(asteroidPrefab);
            }
        }
        else if(objectType == "Shot")
        {
            if(shotParticles.Count > 0)
            {
                objectToReturn = shotParticles[0];
                objectToReturn.SetParent(null);
                shotParticles.RemoveAt(0);
            }
            else
            {
                objectToReturn = Instantiate(shotPrefab);
            }
        }

        if(objectToReturn != null)
            objectToReturn.SetActive(true);

        return objectToReturn;
    }

    //Set object to inactive before pooling for performance reasons.
    //Prefabs must have tag set to proper string used to retrieve the object.  Typically I use a enum for this.
    public void PoolObject(GameObject objectToPool)
    {
        if(objectToPool.tag == "Asteroid")
        {
            asteroids.Add(objectToPool);
        }
        else if(objectToPool.tag == "Shot")
        {
            shotParticles.Add(objectToPool);
        }

        objectToPool.SetParent(transform);
    }
}

Share this post