using System.Collections.Generic; using UnityEngine; // ReSharper disable InconsistentNaming // ReSharper disable CommentTypo // ReSharper disable ForCanBeConvertedToForeach namespace DestroyIt { /// /// Creates a pool of objects to pull from, instead of using expensive Instantiate/Destroy calls. /// This class is a Singleton, meaning it is called using ObjectPool.Instance.SomeFunction(). /// [DisallowMultipleComponent] public class ObjectPool : MonoBehaviour { // Hide the default constructor (use ObjectPool.Instance instead). private ObjectPool() { } public List prefabsToPool; public bool suppressWarnings; private GameObject[][] Pool; private Dictionary autoPooledObjects; // Destroyed Prefabs which are auto-added to the pool on start. private GameObject container; // Here is a private reference only this class can access private static ObjectPool _instance; private bool isInitialized; // This is the public reference that other classes will use public static ObjectPool Instance { get { if (_instance == null) CreateInstance(); if (!_instance.isInitialized) _instance.Start(); return _instance; } } private static void CreateInstance() { ObjectPool[] objectPools = FindObjectsOfType(); if (objectPools.Length > 1) Debug.LogError("Multiple ObjectPool scripts found in scene. There can be only one."); if (objectPools.Length == 0) Debug.LogError("ObjectPool script not found in scene. This is required for DestroyIt to work properly."); _instance = objectPools[0]; } void Start() { if (isInitialized) return; if (prefabsToPool == null) return; // Check if the object pool container already exists. If so, use it. GameObject existingContainer = GameObject.Find("DestroyIt_ObjectPool"); container = existingContainer != null ? existingContainer : new GameObject("DestroyIt_ObjectPool"); container.SetActive(false); autoPooledObjects = new Dictionary(); // Instantiate game objects from the PrefabsToPool list and add them to the Pool. Pool = new GameObject[prefabsToPool.Count][]; for (int i = 0; i < prefabsToPool.Count; i++) { PoolEntry poolEntry = prefabsToPool[i]; Pool[i] = new GameObject[poolEntry.Count]; for (int n=0; n(); for (int i = 0; i < destObjectsInObject.Length; i++) AddDestructibleObjectToPool(destObjectsInObject[i]); newObj.transform.parent = container.transform; newObj.name = destObj.destroyedPrefab.name; newObj.AddTag(Tag.Pooled); DestructibleHelper.TransferMaterials(destObj, newObj); // See if we will need to check for clinging debris. (Optimization) ClingPoint[] clingPoints = newObj.GetComponentsInChildren(); if (clingPoints.Length == 0) destObj.CheckForClingingDebris = false; // Add references to Rigidbodies and Rigidbody GameObjects to Destructible objects for better performance. (Optimization) destObj.PooledRigidbodies = newObj.GetComponentsInChildren(); destObj.PooledRigidbodyGos = new GameObject[destObj.PooledRigidbodies.Length]; for (int i = 0; i < destObj.PooledRigidbodies.Length; i++) destObj.PooledRigidbodyGos[i] = destObj.PooledRigidbodies[i].gameObject; newObj.SetActive(false); autoPooledObjects.Add(destObj.GetInstanceID(), newObj); } } // Spawn a game object from the original prefab, not from the pool. Used for resetting destroyed objects back to their original state. public GameObject SpawnFromOriginal(string prefabName) { foreach (PoolEntry entry in prefabsToPool) { if (entry.Prefab != null && entry.Prefab.name == prefabName) { GameObject obj = Instantiate(entry.Prefab); obj.name = prefabName; return obj; } } return null; } private static GameObject InstantiateObject(GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) { GameObject obj = Instantiate(prefab, position, rotation); if (obj == null) return null; obj.transform.parent = parent; if (parent != null) // If a parent transform was specified, set the position local to the parent. obj.transform.localPosition = position; else // Otherwise, set the position in relation to world space. obj.transform.position = position; return obj; } /// Spawns an object from the object pool, setting the object's parent to what you pass in. public GameObject Spawn(GameObject originalPrefab, Vector3 position, Quaternion rotation, Transform parent, int autoPoolID = 0) { // If an AutoPoolID was passed in, try to find it in the AutoPool dictionary. if (autoPooledObjects != null && autoPoolID != 0 && autoPooledObjects.ContainsKey(autoPoolID)) { GameObject pooledObj = autoPooledObjects[autoPoolID]; if (pooledObj != null) { pooledObj.transform.parent = parent; if (parent != null) // If a parent transform was specified, set the position local to the parent. { pooledObj.transform.localPosition = position; pooledObj.transform.localRotation = rotation; } else // Otherwise, set the position in relation to world space. { pooledObj.transform.position = position; pooledObj.transform.rotation = rotation; } pooledObj.SetActive(true); return pooledObj; } } string origPrefabName = originalPrefab.name; for (int i = 0; i < prefabsToPool.Count; i++) { GameObject prefab = prefabsToPool[i].Prefab; if (prefab == null) continue; if (prefab.name != origPrefabName) continue; if (Pool != null && Pool[i].Length > 0) { // Find the first available object to spawn from the pool. for (int j = 0; j < Pool[i].Length; j++) { if (Pool[i][j] != null) { GameObject pooledObj = Pool[i][j]; Pool[i][j] = null; pooledObj.transform.parent = parent; if (parent != null) // If a parent transform was specified, set the position local to the parent. pooledObj.transform.localPosition = position; else // Otherwise, set the position in relation to world space. pooledObj.transform.position = position; pooledObj.transform.rotation = rotation; pooledObj.SetActive(true); return pooledObj; } } } if (Pool == null) { GameObject pooledObj = InstantiateObject(prefabsToPool[i].Prefab, position, rotation, parent); Debug.LogWarning("[" + origPrefabName + " was instantiated instead of spawned from pool. Reason: Pool is null."); return pooledObj; } if (!prefabsToPool[i].OnlyPooled) { GameObject pooledObj = InstantiateObject(prefabsToPool[i].Prefab, position, rotation, parent); pooledObj.name = prefabsToPool[i].Prefab.name; pooledObj.AddTag(Tag.Pooled); if (!suppressWarnings) Debug.LogWarning("[" + origPrefabName + " was instantiated instead of spawned from pool. Reason: No objects remaining in the pool (size: " + Pool[i].Length + "). Consider increasing the pool size."); return pooledObj; } return null; } if (!suppressWarnings) Debug.LogWarning("[" + origPrefabName + "] was instantiated instead of spawned from pool. Reason: Prefab not found in pool."); return InstantiateObject(originalPrefab, position, rotation, parent); } /// Spawns an object from the object pool. The object will not be a child of any other object. public GameObject Spawn(GameObject originalPrefab, Vector3 position, Quaternion rotation, int autoPoolID = 0) { return Spawn(originalPrefab, position, rotation, null, autoPoolID); } /// Put object back in the pool. You can force children to be reenabled, if desired. public void PoolObject(GameObject obj, bool reenableChildren = false) { for (int i = 0; i < prefabsToPool.Count; i++) { if (prefabsToPool[i].Prefab == null) continue; if (prefabsToPool[i].Prefab.name != obj.name) continue; // Object was found. Deactivate it, stop/clear particle effects, and put it in the pool. obj.transform.parent = container.transform; ParticleSystem[] particleSystems = obj.GetComponentsInChildren(); for (int j = 0; j < particleSystems.Length; j++) { particleSystems[j].Stop(); particleSystems[j].Clear(); var emission = particleSystems[j].emission; emission.enabled = true; } if (reenableChildren) { Transform[] trans = obj.GetComponentsInChildren(true); for (int j = 0; j < trans.Length; j++) trans[j].gameObject.SetActive(true); } obj.AddTag(Tag.Pooled); obj.SetActive(false); // Try to find an empty spot for the object to be placed in. for (int j=0; j