// 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
}
}