// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections.Generic; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Utilities.GameObjectManagement { /// /// Used to recycle Unity GameObjects. When ever you create GameObjects during runtime some overhead is incurred. Additionally /// memory can become highly fragment as well as possibly causing the garbage collector to perform a collection (which is also /// a performance hit). This is especially prevalent when you are spawning and destroying GameObjects of the same type /// very quickly in large quantities (such as bullets). The GameObject pool allows you to recycle objects so they can be /// reused upon request. /// /// /// Setup code for using the generic prefab instance creator: /// /// GameObjectPool pool = new GameObjectPool(); /// GenericPrefabInstanceCreator creator = new GenericPrefabInstanceCreator(); /// creator.Prefab = MyProjectilePrefab; /// pool.AddCreator(creator, "projectile1"); /// /// Requesting a game object from the pool: /// /// var myProjectileObj = pool.GetGameObject("projectile1"); /// /// Recycling the game object: /// /// pool.Recycle(myProjectileObj, "projectile1"); /// /// /// Note that the GameObjectPool is not thread safe. It should only be used in Unity's main thread. public class GameObjectPool { /// /// Initializes a new instance of the class. /// public GameObjectPool() { _pool = new Dictionary>(); _creators = new Dictionary(); } /// /// GameObjects are created by an implementation of IGameObjectCreator in this GameObjectPool. This /// method adds your implementation of the IGameObjectCreator to use for objects that share a specific /// object identifier. /// /// The implementation of IGameObjectCreator to use for GameObjects associated with the objectIdentifier. /// The identifier you want to use to identify the kind of game objects you want to create. public void AddCreator(GameObjectCreator creator, string objectIdentifier) { _creators.Add(objectIdentifier, creator); } /// /// Adds a game object under a specific object identifier to the GameObjectPool. /// /// The GameObject to recycle. /// The identifier you want to use to identify the kind of game object you are recycling. public void Recycle(GameObject gameObject, string objectIdentifier) { EnsureListForObjectID(objectIdentifier); if (_creators.ContainsKey(objectIdentifier)) { _creators[objectIdentifier].PrepareForRecycle(gameObject); } gameObject.SetActive(false); _pool[objectIdentifier].Enqueue(gameObject); } /// /// Gets a game object for a specific object identifier from the GameObjectPool. If the kind of game object /// being requested is not in the pool, then it will get created by a IGameObjectCreator that was /// added to the pool for handling objects associated with the objectIdentifier. /// /// The identifier you want to use to identify the kind of game object you want to retrieve. /// The position that the game object should have before it is activated. /// The rotation that the game object should have before it is activated. public GameObject GetGameObject(string objectIdentifier, Vector3 position, Quaternion rotation) { GameObject obj = null; GameObjectCreator creator = null; if (_creators.ContainsKey(objectIdentifier)) { creator = _creators[objectIdentifier]; } else { Debug.Log("Unable to create a GameObject for object ID '" + objectIdentifier + "' because no IGameObjectCreator implementation can be found for it."); return null; } EnsureListForObjectID(objectIdentifier); Queue objects = _pool[objectIdentifier]; if (objects.Count > 0) { obj = objects.Dequeue(); } else { obj = creator.Instantiate(); } if (obj != null) { creator.PrepareForUse(obj); obj.SetActive(true); } return obj; } /// /// Same as calling GetGameObject(objectIdentifier, Vector3.zero, Quaternion.identity) /// /// The identifier you want to use to identify the kind of game object you want to retrieve. public GameObject GetGameObject(string objectIdentifier) { return GetGameObject(objectIdentifier, Vector3.zero, Quaternion.identity); } /// /// Gets the number of game objects in the pool for a specific identifier. /// public int Count(string objectIdentifier) { EnsureListForObjectID(objectIdentifier); return _pool[objectIdentifier].Count; } /// /// Removes and destroys all game objects in the pool associated with the specified objectIdentifier. /// /// The identifier you want to use to identify the kind of game objects to remove from the pool. public void EmptyPool(string objectIdentifier) { EnsureListForObjectID(objectIdentifier); Queue objects = _pool[objectIdentifier]; foreach (GameObject obj in objects) { GameObject.Destroy(obj); } objects.Clear(); } /// /// Removes and destroys all game objects in the pool. /// public void EmptyPool() { foreach (string objectID in _pool.Keys) { EmptyPool(objectID); } _pool.Clear(); } #region Private /// /// The pool for game objects /// private Dictionary> _pool; /// /// All creators that this pool can handle by identifier /// private Dictionary _creators; /// /// Ensures there is a list for the specified identifier /// private void EnsureListForObjectID(string objectIdentifier) { if (!_pool.ContainsKey(objectIdentifier)) { _pool.Add(objectIdentifier, new Queue()); } } #endregion } }