using System.Collections.Generic;
using UnityEngine;
namespace KhawajjaGames.Utilities
{
///
/// High-performance, zero-garbage-collection Object Pooling System for Unity.
/// Eliminates Instantiate/Destroy overhead for bullets, rockets, explosions, and UI blips.
/// Created by Khawajja Games Studio (https://khawajjagames.com)
///
public class ObjectPooler : MonoBehaviour
{
public static ObjectPooler Instance { get; private set; }
[System.Serializable]
public class PoolItem
{
[Tooltip("Unique pool identifier tag (e.g. 'Bullet', 'Missile', 'Explosion')")]
public string tag;
[Tooltip("Prefab to instantiate for this pool")]
public GameObject prefab;
[Tooltip("Initial number of objects to prewarm in memory")]
public int initialSize = 20;
[Tooltip("Allow pool to expand if all objects are in active use")]
public bool shouldExpand = true;
}
[Header("Pool Configurations")]
public List pools = new List();
private Dictionary> _poolDictionary;
private Dictionary _poolConfigs;
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
return;
}
InitializePools();
}
private void InitializePools()
{
_poolDictionary = new Dictionary>();
_poolConfigs = new Dictionary();
foreach (var pool in pools)
{
Queue objectPool = new Queue();
_poolConfigs[pool.tag] = pool;
GameObject parentGroup = new GameObject($"Pool_{pool.tag}");
parentGroup.transform.SetParent(transform);
for (int i = 0; i < pool.initialSize; i++)
{
GameObject obj = Instantiate(pool.prefab, parentGroup.transform);
obj.SetActive(false);
objectPool.Enqueue(obj);
}
_poolDictionary.Add(pool.tag, objectPool);
}
}
///
/// Spawns a pooled object by tag at the given position and rotation.
///
public GameObject SpawnFromPool(string tag, Vector3 position, Quaternion rotation)
{
if (!_poolDictionary.ContainsKey(tag))
{
Debug.LogWarning($"ObjectPooler: Pool with tag '{tag}' doesn't exist.");
return null;
}
Queue poolQueue = _poolDictionary[tag];
if (poolQueue.Count == 0)
{
if (_poolConfigs[tag].shouldExpand)
{
GameObject newObj = Instantiate(_poolConfigs[tag].prefab, transform.Find($"Pool_{tag}"));
newObj.transform.position = position;
newObj.transform.rotation = rotation;
newObj.SetActive(true);
return newObj;
}
else
{
Debug.LogWarning($"ObjectPooler: Pool with tag '{tag}' is exhausted and cannot expand.");
return null;
}
}
GameObject objToSpawn = poolQueue.Dequeue();
objToSpawn.transform.position = position;
objToSpawn.transform.rotation = rotation;
objToSpawn.SetActive(true);
// Trigger IPooledObject interface if implemented
var pooledInterface = objToSpawn.GetComponent();
if (pooledInterface != null)
{
pooledInterface.OnObjectSpawn();
}
return objToSpawn;
}
///
/// Returns an active object back to its respective pool.
///
public void ReturnToPool(string tag, GameObject obj)
{
if (!_poolDictionary.ContainsKey(tag))
{
Destroy(obj);
return;
}
obj.SetActive(false);
_poolDictionary[tag].Enqueue(obj);
}
}
///
/// Optional interface for objects that need custom initialization when spawned from pool.
///
public interface IPooledObject
{
void OnObjectSpawn();
}
}