Object Pooling

Hello all! I know I know, more than 1 post in the span of a month? How lucky are you! Well the answer is you’re not, because my last post was a month and a week ago now… so calm down, you. But I do bring some cool stuff to share! Ever heard of Object Pooling in Unity? In brief: If you have a GameObject which you’re constantly creating new copies of and then destroying right after, such as a projectile or an effect, it can be really unnecessarily taxing on your CPU. So an Object Pool just instantiates all necessary instances of the GameObject at the start of the game, and just recycles them, setting them active and inactive when needed, and expanding if necessary, much like an ArrayList, to accommodate however many instances of the object your game needs.

I’m just beginning work as an engineer on a game with some of my friends at school. For now we have dubbed the game “Project Rat”, and this game is why I’ve built this Object Pooler! It’s a really easy object to build but I’ll talk a little about it. I’d like to also start by saying that I read and learned about Object Pooling from Mark Placzek. To start, we define an ObjectPoolItem Class which contains an Int that defines the initial size, a GameObject which will hold the prefab that is to be pooled, and then a bool to represent if we want this size to be dyamic, or a fixed value.

From here we define the ObjectPooler Class, which is a monoBehaviour. Here we declare the List that will be our pool, as well as the List that will contain all of the pools, for easy access by the GameManager. The pool list is then instantiated in Start(), where we then have a loop instantiating as many copies of the GameObject as defined in our ObjectPoolItem (both the GameObject and the size are defined there). In this loop the newly instantiated GameObject is set inactive then added to our List.

And… That’s really it! Then just create a function that takes a string of the tag of item being sought, and returns the first instance of an item with that tag which isn’t already .activeInHeirarchy(). It’s here that we can also create a check on the size of the List and compare it to how far we have to go into our List to get a new GameObject, and decide if we want the List to expand or not, using the bool we defined in the ObjectPoolItem, and just instantiating a new GameObject as we did in Start().

Sweet! Now you just need to keep in mind that the Object Pooler is storing inactive GameObjects, so however you call them you must remember to set them active! And then set inactive once they are no longer in use so that they may be recycled by our object pool! Here’s some code:

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


//Declares necessary values
[System.Serializable]
public class ObjectPoolItem
{
    public int amountToPool;
    public GameObject objectToPool;
    public bool expand = true;

}

public class ObjectPooler : MonoBehaviour
{

    public static ObjectPooler SharedInstance;

    //Declares List to hold pools
    public List<ObjectPoolItem> listOfPools;

    //Declares List to hold GameObjects 
    private List<GameObject> objectPool;


    void Awake()
    {
        SharedInstance = this;
    }

    void Start()
    {
        //Instantiates List
        objectPool = new List<GameObject>();

        foreach (ObjectPoolItem item in listOfPools)
        {
            // Instantiates the "Object to pool". sets inactive, and stores in List for number in amountToPool
            for (int rep = 0; rep < item.amountToPool; rep++)
            {
                GameObject obj = (GameObject)Instantiate(item.objectToPool);
                obj.SetActive(false);
                objectPool.Add(obj);
            }
        }
    }

    //Returns an INACTIVE instance of the pooled object, need to match tag
    //Can also specify when to expand and when not to.
    public GameObject GetPooledObject(string tag)
    {
        //Cycles through active and inactive elements in List
        for (int rep = 0; rep < objectPool.Count; rep++)
        {
            //If Inactive
            if (!objectPool[rep].activeInHierarchy && objectPool[rep].tag == tag)
            {
                return objectPool[rep];
            }

        }

        //expands necesary lists all at once
        foreach (ObjectPoolItem item in listOfPools)
        {
            if (item.objectToPool.tag == tag)
            {
                if (item.expand)
                {
                    GameObject obj = (GameObject)Instantiate(item.objectToPool);
                    obj.SetActive(false);
                    objectPool.Add(obj);
                    return obj;
                }
            }
        }

        return null;
    }
}

Unity Crash Destroys My Happiness

Well, shit… After spending a few days working on an order taking software based off of my job at In N Out, a Unity crash proved to be all it took for me to lose all progress. Since the software was only 1 scene, that scene has been completely wiped. Despite my good saving habits, and having exited out of Unity3D multiple times, and still having my project, this one crash seems to have completely wiped all my progress (except for my code). Everything carries a lesson and the lesson i’ve learned from this is after a Unity crash, to not re open Unity, but instead find out  of the automatic backups stored in the “temp” folder under assets. Save yourself time, & sanity, and learn from my mistakes.

 

All that aside, I still have some interesting code and was working with some interesting ways in which I could store & display an order in progress, as well as a completed order. Using a generic type “BurgerObject”, that take the specific type of burger as a string, and have the ingredients stored in a List, I am able to accurately recreate how order taking works across all In N Out store locations. In addition to this, using the buttons that I had placed across the scene to represent each item would add a new text to the screen, representing the burger/item added, adding it to the entirety of the order. I found it difficult to decide how I was going to best display the entire order, since the order is stored in a List and displaying a List seems to be pretty much undoable in C#, but found this way to be likely the most promising. Here’s some code (please know that this is not nearly completed, just posting what I had completed pre crash):

 

Screen Shot 2018-07-26 at 11.59.28 AMScreen Shot 2018-07-26 at 11.58.07 AMScreen Shot 2018-07-26 at 11.58.14 AM

Unity Sample: Scripts for Object Interaction in C#

The following are 2 scripts that I have written to allow a user using a first person camera in Unity to interact with “intractable” objects. Right now, the code doesn’t do much, it’s really just a template. If attached to an object with a rigidBody component, the interact script will simply turn the object red when it is within interacting range (in this case, 1 meter) and on the pressing of ‘e’ it will make the object jump.

Essentially, this is just a skeleton for object interaction. Attatching the Select script to the first person camera object and the interact script to an object, such as a door, would enable a user to input an “opening” and “closing” animation to the door when the key ‘e’ is pressed.

While the object is out of range

Screen Shot 2018-01-28 at 11.56.01 AM.png

While the object is in rangeScreen Shot 2018-01-28 at 11.57.11 AM

This object’s specific interactionScreen Shot 2018-01-28 at 12.00.40 PM.png

using UnityEngine;
using System.Collections;

public class Select : MonoBehaviour {
	public RaycastHit hit;

	// Use this for initialization
	void Start () {

	}

	// Update is called once per frame
	void Update () {

		//create a ray from the center of the screen
		Ray ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width/2, Screen.height/2,0));

		//if the ray hits something within 1 meters, do:
		if (Physics.Raycast (ray, out hit, 1)) {

			//Checks to see if Interact script is attatched to the object being looked upon
			if (hit.collider.gameObject.GetComponent () != null) {

				//Calls the OnLookEnter funtion for the object with this script attatched
				hit.collider.gameObject.GetComponent ().OnLookEnter ();

			}
		} 

		//When not being looked at
		else {

			Interact.OnLookExit ();
		}
	}
}
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class Interact : MonoBehaviour {
	static public bool selected = false;

	// Use this for initialization
	void Start () {

	}

	// Update is called once per frame
	void Update () {

	}

	//When object is within interaction range, Do something
        //Turns the object blue
	public void OnLookEnter(){

		//DO THIS
		//sets the color to blue
		GetComponent ().material.color = Color.blue;

		//sets bool to true
		selected = true;
	}

	//When not being looked at, the bool is set to false
	public static void OnLookExit(){

		selected = false;
	}

	//On key press, do something
        //on the press of key 'e', adjusts the height
	void OnGUI(){

		//event e will be the pressing of the e key
		Event e = Event.current;

		//if e = a keyPress, && a characer 'e', && object is in range
		if(e.isKey && e.character == 'e' && selected){

			//uses the objects rigidbody and adjusts it's height.
			GetComponent().AddForce(Vector3.up*100);
		}

	}
}

Unity Sample: Player Shoot Method

Below is a simple framework for identifying what a player “shoots” at. A click on an object in unity will send a message to the log if an object is stuck by the “bullet”. If no object is struck, nothing will happen. From this framework we could very simply apply damage to the object struck, as well as any other desired effects upon shooting an object or player. The Serialized Field PlayerWeapon is where you could establish the damage, range, fire rate, and other unique attributes of each weapon object.

 

using UnityEngine;

public class PlayerShoot : MonoBehaviour {

[SerializeField]
private PlayerWeapon weapon;

[SerializeField]
private Camera cam;

[SerializeField]
private LayerMask mask;

void Start () {
if (cam == null)
{
Debug.LogError ("No camera referenced in PlayerShoot");
this.enabled = false;
}
}

void Update () {
if (Input.GetButtonDown ("Fire1"))
{
Shoot ();
}
}

void Shoot () {
RaycastHit _hit;
// If we hit something
if (Physics.Raycast (cam.transform.position, cam.transform.forward, out _hit, weapon.range, mask))
{
EnemyShot (_hit.collider.name);
}
}

void EnemyShot (string _colliderName) {
Debug.Log ("Enemy " + _colliderName + " has been Shot");
}
}