This technique is a performance optimization technique that reuses created objects to avoid frequent memory allocation and deallocation. Similar to memory pool, it focuses on recycling fully initialized object instances. It is ideal for objects that repeatedly appear and disappear.
⚠️ Note: This technique improves performance by reducing CPU overhead through reuse, though it may increase memory usage due to pre-allocation.
Structure Diagram
White border: process flow
Yellow border: objects container
Practical Application Examples
1. Project: Infinite Runners
Infinite Runners is one of my old project, used here to demonstrate object pooling.
In Infinite Runners, the MapGenerator class uses an array-based pool structure to randomly reuse maps through Object Pooling, allowing for varied map sequences.
MapGenerator Class - Execution Flow
These methods are called once at the start of the game to set up the map pool:
private void CreateMapPool(){ m_MapPool = new GameObject[m_MapPoolSize]; for (int i = 0; i < m_MapPoolSize; i++) { GameObject obj = Instantiate(m_MapPrefabs[i % m_MapPrefabs.Length], Vector3.zero, Quaternion.identity, transform); obj.SetActive(false); m_MapPool[i] = obj; MapBehaviour mapBehaviour = obj.GetComponent<MapBehaviour>(); mapBehaviour.SetPoolIdx(i); }}
All map objects are instantiated once and stored in an array. They are set inactive by default, and each map is assigned a unique pool index via its MapBehaviour component for future tracking.
private void InitializeFreeIndices(){ m_FreeIndices = new List<int>(m_MapPoolSize); for (int i = 0; i < m_MapPoolSize; i++) { m_FreeIndices.Add(i); }}
This function fills a list with all the indices of the pre-instantiated map objects. It acts as a tracker for which maps are currently available for reuse.
private void SpawnMapsAtStart(){ for (int i = 0; i < m_SpawnAmount; i++) { int randListIdx = GetRandomFreeIdx(); int poolIdx = m_FreeIndices[randListIdx]; m_FreeIndices.RemoveAt(randListIdx); m_NextSpawnPoint += new Vector3(0.0f, 0.0f, m_GroundLength); m_MapPool[poolIdx].transform.position = m_NextSpawnPoint; m_MapPool[poolIdx].SetActive(true); }}
A set number of maps (m_SpawnAmount) are randomly taken from the pool, positioned sequentially, and activated to create the starting environment.
This function is called when a collider enters the trigger zone. If the collider belongs to a map (tagged "Map") and has a MapBehaviour component, its pool index is passed to ReplaceMap(). Otherwise, the object is destroyed.
private void ReplaceMap(int oldIdx){ int randListIdx = GetRandomFreeIdx(); int newIdx = m_FreeIndices[randListIdx]; m_FreeIndices[randListIdx] = oldIdx; m_NextSpawnPoint = m_MapPool[oldIdx].transform.position + new Vector3(0.0f, 0.0f, m_GroundLength * m_SpawnAmount); m_MapPool[newIdx].transform.position = m_NextSpawnPoint; m_MapPool[newIdx].SetActive(true); m_MapPool[oldIdx].SetActive(false);}
This function recycles maps by selecting a free index, repositioning and activating a new map, and deactivating the old one while returning its index to the pool for future reuse. It ensures the pool maintains reusable maps without frequent instantiation.
2. Project: Spaceship Battle
Spaceship Battle is one of my old project, used here to demonstrate object pooling.
View Repository
In SpaceShip Battle, the BulletManager class uses a queue-based pool structure to reuse bullets, as there’s no need for random selection like in Infinite Runners.
No Pool vs. Pool - Performance Comparison
As seen in the images above, the FPS difference shows the left side without pooling performs worse, while the right side with pooling shows a significant performance boost.
Conclusion
Object Pooling is a powerful technique for managing frequently created and destroyed objects at runtime. However, pre-creating too many objects can lead to significant memory waste. It’s essential to estimate how many objects will be needed during runtime. Based on that estimate, an appropriate pool size should be selected to avoid over-allocation. Finally, as seen in my two projects demonstrating object pooling, selecting the right data structure for the pool is crucial, depending on the specific use case.