Skip to content

Instantly share code, notes, and snippets.

@zaun
Created February 28, 2017 04:47
Show Gist options
  • Save zaun/067b87ecd6cc1a2d896f14508c04f968 to your computer and use it in GitHub Desktop.
Save zaun/067b87ecd6cc1a2d896f14508c04f968 to your computer and use it in GitHub Desktop.
Unity3d Smart Object Pooling
This is a smart pool manager very much based on the pool manager by Sebastian Lague.
https://www.patreon.com/SebastianLague
https://www.youtube.com/channel/UCmtyQOKKmrMVaKuRXz02jbQ
His video's are amazing. I highly recommend you watch them all and support him as well.
The pool managet by Sebastian Lague sets up a queue of GameObjects that get reused. If there
is a pool of `n` object, when the `n+1`th object is requested it reuses the first object. This
reuse happens regardless if the object was still in use from the last request.
This pool manager lets a queue be setup but when the `n+1` object is requested, a new object
is created and added to the queue. Additionally the `PoolObject` has a `Done` method that
returns the object to the pool for reuse. In practive the user would call `Done()` rather than
calling `Destroy`.
The Bullet script is an example PoolObject, and the Test script is a quick testing script.
1. Create a new empty game object, name it Pool Manager.
2. Add the PoolManager script to the Pool Manager object
3. Create a sphere, name it Bullet.
4. Add the Bullet script to the Bullet object
5. Create a Prefab object of the Bullet (Drag it out to the Project pane)
6. Delete the Bullet object
7. Create a new game object, name it Test.
8. Add the Test script to the Test object
9. Set the Bullet prefab on the Test script in the inspector.
Run the project. Open all the object in the inspector. You will see a pool object for the bullets and
five bullet objects under it. All bullets are inactive. Press the spacebar and you will see one bullet
go active. Press the space bar five time quickly and you'll see all five bullets go active. After two
seconds the bullets will be returned to the queue and set inactive.
Press the space bar quickly more than five times and you will see the bullet pool grow in size as needed.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : PoolObject {
Rigidbody myRigidbody;
public override void OnAwake() {
if (myRigidbody == null) {
myRigidbody = GetComponent<Rigidbody> ();
}
// Clear old values
myRigidbody.velocity = Vector3.zero;
myRigidbody.angularVelocity = Vector3.zero;
// Set new values
myRigidbody.velocity = transform.forward * 60;
Done (2f);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoolManager : MonoBehaviour {
Dictionary<string, GameObject> poolPrefabs = new Dictionary<string, GameObject> ();
Dictionary<string, GameObject> poolParents = new Dictionary<string, GameObject> ();
Dictionary<string,Queue<ObjectInstance>> poolFreeDictionary = new Dictionary<string, Queue<ObjectInstance>> ();
Dictionary<string,Dictionary<int, ObjectInstance>> poolUsedDictionary = new Dictionary<string, Dictionary<int, ObjectInstance>> ();
static PoolManager _instance;
public static PoolManager instance {
get {
if (_instance == null) {
_instance = FindObjectOfType<PoolManager> ();
}
return _instance;
}
}
public void CreatePool(string poolKey, GameObject prefab, int poolSeedSize) {
if (!poolPrefabs.ContainsKey (poolKey)) {
poolPrefabs.Add (poolKey, prefab);
if (!prefab.GetComponent<PoolObject> ()) {
prefab.AddComponent<PoolObject> ();
}
prefab.GetComponent<PoolObject> ().poolKey = poolKey;
poolFreeDictionary.Add (poolKey, new Queue<ObjectInstance> ());
poolUsedDictionary.Add (poolKey, new Dictionary<int, ObjectInstance> ());
GameObject poolParent;
if (poolParents.ContainsKey (poolKey)) {
poolParent = poolParents [poolKey];
} else {
poolParent = new GameObject (poolKey + " pool");
poolParent.transform.parent = transform;
poolParents.Add (poolKey, poolParent);
}
for (int i = 0; i < poolSeedSize; i++) {
ObjectInstance newObject = new ObjectInstance(Instantiate (prefab) as GameObject);
poolFreeDictionary [poolKey].Enqueue (newObject);
newObject.SetParent (poolParent.transform);
}
}
}
public GameObject GetObject(string poolKey, Vector3 position, Quaternion rotation) {
if (poolPrefabs.ContainsKey (poolKey)) {
if (poolFreeDictionary [poolKey].Count > 0) {
ObjectInstance obj = poolFreeDictionary [poolKey].Dequeue ();
poolUsedDictionary [poolKey].Add (obj.gameObject.GetInstanceID (), obj);
obj.Awake (position, rotation);
return obj.gameObject;
} else {
ObjectInstance newObject = new ObjectInstance (Instantiate (poolPrefabs [poolKey]) as GameObject);
poolUsedDictionary [poolKey].Add (newObject.gameObject.GetInstanceID (), newObject);
newObject.SetParent (poolParents [poolKey].transform);
newObject.Awake (position, rotation);
return newObject.gameObject;
}
}
return null;
}
public void ReturnObjectToQueue(GameObject gameObject) {
if (gameObject.GetComponent<PoolObject> ()) {
string poolKey = gameObject.GetComponent<PoolObject> ().poolKey;
gameObject.SetActive (false);
ObjectInstance obj = poolUsedDictionary [poolKey] [gameObject.GetInstanceID()];
poolUsedDictionary [poolKey].Remove (gameObject.GetInstanceID ());
poolFreeDictionary [poolKey].Enqueue (obj);
}
}
private class ObjectInstance {
public GameObject gameObject;
Transform transform;
PoolObject poolObjectScript;
public ObjectInstance(GameObject objectInstance) {
gameObject = objectInstance;
transform = gameObject.transform;
gameObject.SetActive(false);
poolObjectScript = gameObject.GetComponent<PoolObject>();
}
public void Awake(Vector3 position, Quaternion rotation) {
gameObject.SetActive (true);
transform.position = position;
transform.rotation = rotation;
poolObjectScript.OnAwake ();
}
public void SetParent(Transform parent) {
transform.parent = parent;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoolObject : MonoBehaviour {
public string poolKey;
public virtual void OnAwake() {
Debug.Log ("PoolObject Awake");
}
protected void Done(float time) {
Invoke("Done", time);
}
protected void Done() {
PoolManager.instance.ReturnObjectToQueue (gameObject);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour {
public GameObject bullet;
// Use this for initialization
void Start () {
PoolManager.instance.CreatePool ("bullet", bullet, 5);
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.Space)) {
PoolManager.instance.GetObject("bullet", transform.position, transform.rotation);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment