Object Pooling: A Design Pattern for Games in Unity
Introduction
Do you want to make your game faster and smoother without compromising the quality and quantity of your game objects? If you do, then you should learn about Object Pooling, a design pattern that can help you optimize the performance of your game by saving the CPU and memory from creating and destroying too many objects. 🔄
In this article, I will show you what object pooling
is, how to use it in Unity, and what the pros and cons of using it are. By the end of this article, you will be able to apply object pooling
to your own game projects. 🚀
What is Object Pool Pattern?
Object Pool pattern is a cool way to make your game run faster and smoother by saving the CPU from creating and destroying too many objects. It can works by recycling the objects that you already have instead of making new ones every time you need them. 🔄
The best thing about Object Pool pattern is that it helps the CPU to relax and not work too hard when it has to create and destroy new objects quickly. This can make your game more fun and responsive, especially when you have a lot of objects that you use and throw away often, like bullets, enemies, particles, or UI elements. 🔫👾💥🎮
Another benefit of using Object Pool pattern is that it reduces the memory fragmentation and garbage collection issues that can occur when creating and destroying objects dynamically. Memory fragmentation is when the memory space becomes divided into small and unusable chunks, which can slow down the allocation and deallocation of memory. Garbage collection is when the system automatically frees up the memory space occupied by unused objects, which can cause a noticeable pause or stutter in your game.🐌🗑️
By using the Pool pattern, you can avoid these problems by pre-allocating a fixed number of objects at the start of your game and reusing them throughout the game. This way, you can maintain a constant and efficient use of memory and avoid unnecessary allocations and deallocations. 🧹🚀
When Should Use, Pros and Const
You can use Object Pool pattern when:
- You have objects that are expensive or slow to create, such as database connections, network sockets, or graphic resources. 💰🐢🖥️
- You have objects that are frequently used and discarded, such as bullets, enemies, particles, or UI elements. 🔫👾💥🎮
- You have a limited number of objects that can be created, such as licenses, threads, or file handles. 🔒🧵📁
The pros of using Object Pool pattern are:
- It lowers the burden on the CPU and memory by avoiding unnecessary allocations and deallocations of objects. 🙌🧠
- It improves the frame rate and responsiveness of your game by reducing the latency and overhead of creating and destroying objects. 🚀🕹️
- It prevents memory fragmentation and garbage collection issues that can cause pauses or stutters in your game. 🧩🗑️
The cons of using Object Pool pattern are:
- It complicates the object lifecycle and management, as you have to keep track of which objects are available and which are in use. 🤯🔎
- It introduces additional complexity and overhead in the code, as you have to implement the pool logic and handle the creation, validation, and destruction of objects. 😓💻
- It may introduce bugs or errors if the objects are not properly reset or cleaned up when they are returned to the pool. 🐛❌
This is how Object Pool works in a nutshell: 🔁
- You start with a bunch of objects ready to use 🏁
- Whenever you need an object, you just ask the Object Pool to lend you one 🙋
- When you are done with the object, you don’t destroy it but give it back to the Object Pool for later use 🙌
Of course, there are some things you need to think about, like: 🤔
- What if the Object Pool runs out of objects? Should it make new ones or give you nothing? ❓
- I think it’s better to make new ones. That way, you can have more objects in case you need them and you don’t have to worry about null references. 👍
- How many objects should you start with? 🤷
It depends on what kind of objects you are pooling. You can do some tests to see how many objects you need at most and use that as a guide so you don’t have to make them on runtime. 🧪
// Check if the queue is empty added new object
if (queue.Count == 0)
{
GameObject objAgain = Instantiate(prefab);
objAgain.SetActive(false);
queue.Enqueue(objAgain);
}
Example Object Pooling Pattern in Unity
In this example, we are going to go through a simple game. In this game, bullets are fired from a spaceship.
As you can see in the GIF above, objects are recreated as they are fired at, consuming resources continuously.
But in this example above, since an object pooling structure is used, a certain limit of objects are generated and those objects are reused alternately to save resources.
Example: Object Pooling class;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPoolManager : MonoBehaviour
{
// A dictionary to store the pools of different types of objects
private Dictionary<string, Queue<GameObject>> pools;
// A singleton instance of this class
public static ObjectPoolManager Instance { get; private set; }
// Awake method to initialize the singleton instance and the pools dictionary
private void Awake()
{
// Make sure there is only one instance of this class
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
// Initialize the pools dictionary
pools = new Dictionary<string, Queue<GameObject>>();
}
// A method to create a new pool for a given object type and size
public void CreatePool(string objectType, GameObject prefab, int size)
{
// Check if the pool already exists
if (pools.ContainsKey(objectType))
{
Debug.LogWarning("The pool for " + objectType + " already exists.");
return;
}
// Create a new queue to store the objects
Queue<GameObject> queue = new Queue<GameObject>();
// Instantiate the objects and add them to the queue
for (int i = 0; i < size; i++)
{
GameObject obj = Instantiate(prefab);
obj.SetActive(false);
queue.Enqueue(obj);
}
// Add the queue to the pools dictionary with the object type as the key
pools.Add(objectType, queue);
}
// A method to get an object from a pool by its type
public GameObject GetObject(string objectType, GameObject prefab)
{
// Check if the pool exists
if (!pools.ContainsKey(objectType))
{
Debug.LogError("The pool for " + objectType + " does not exist.");
return null;
}
// Get the queue of the pool
Queue<GameObject> queue = pools[objectType];
// Check if the queue is empty added new object
if (queue.Count == 0)
{
GameObject objAgain = Instantiate(prefab);
objAgain.SetActive(false);
queue.Enqueue(objAgain);
}
// Dequeue an object from the queue and return it
GameObject obj = queue.Dequeue();
return obj;
}
// A method to return an object to a pool by its type
public void ReturnObject(string objectType, GameObject obj)
{
// Check if the pool exists
if (!pools.ContainsKey(objectType))
{
Debug.LogError("The pool for " + objectType + " does not exist.");
return;
}
// Get the queue of the pool
Queue<GameObject> queue = pools[objectType];
// Set the object to inactive and enqueue it to the queue
obj.SetActive(false);
queue.Enqueue(obj);
}
}
You can examine the project by accessing the example described in this article via the github link here.
Conclusion
Object Pooling is a cool way to make your game run faster and smoother by saving the CPU and memory from creating and destroying too many objects. It works by recycling the objects that you already have instead of making new ones every time you need them. 🔄
By using Object Pooling, you can help the CPU and memory to relax and not work too hard, improve the fun and responsiveness of your game, and prevent pauses or stutters caused by memory issues. 🙌
However, Object Pooling also makes the object lifecycle and management more tricky, adds more complexity and overhead to the code, and may cause bugs or errors if the objects are not properly reset or cleaned up when they are returned to the pool. 🤯
Therefore, you should use Object Pooling wisely and carefully, depending on your game needs and scenarios. 🤔
I hope this article helps you understand some of the Object Pooling in Unity better. 😊 Thank you for reading! 👋