Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现
目录
Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现
一、简单介绍
Unity 工具类,自己整理的一些游戏开发可能用到的模块,单独独立使用,方便游戏开发。
简单的对象池管理类,对象池管理是一种广泛通用的内存优化手段。在首次建立对象时,便将其存储在池子中,需要再次使用时,不是每次都实例化一个新的对象,而是查找回收池中是否存在闲置的对象,将其重新激活利用。连续的生成和销毁游戏物体也是耗性能的。
二、实现原理
1、单例类,保证整个场景中只有一个类管理对象池;
2、把需要的对象以对象池形式管理起来,需要的时候 拿出来显示即可,不需要的时候隐藏以来即可;
3、ObjectPoolManager.Instance.WarmPool 孵化一些预制的对象池对象;
4、ObjectPoolManager.Instance.SpawnObject 取出未使用的对象池中的对象;
5、ObjectPoolManager.Instance.ReleaseObject 释放使用完的对象到对象池中;
三、注意事项
1、当对象池中的孵化的对象,不够用,会重新孵化新的;
2、注意游戏对象池的对象,在游戏结束切换场景后,有必要的话可以全部释放并销毁掉,视情况而定;
四、效果预览
五、实现步骤
1、打开Unity,新建一个工程,如下图
2、新建脚本,单例用于保证场景中只一个管理类,池对象的属性类管理对象的使用情况,对象池管理对象群的使用情况,对象池管理类管理所有对象池使用情况,如下图
3、新建测试对象池管理的脚本,和一个Move 移动和释放池对象的脚本,以及两个Cube 和 Sphere 预制体,并绑定 Move 脚本,如下图
4、运行场景,效果如下图
六、关键代码
1、TestScript
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestScript : MonoBehaviour
{
public GameObject cube;
public GameObject sphere;
// Start is called before the first frame update
void Start()
{
ObjectPoolManager.Instance.WarmPool(cube,5);
ObjectPoolManager.Instance.WarmPool(sphere, 5);
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.C)) {
ObjectPoolManager.Instance.SpawnObject(cube);
}
if (Input.GetKeyDown(KeyCode.S))
{
ObjectPoolManager.Instance.SpawnObject(sphere);
}
}
}
2、Move
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move : MonoBehaviour
{
// Start is called before the first frame update
void OnEnable()
{
Invoke("Release", 3);
}
// Update is called once per frame
void Update()
{
this.transform.Translate(transform.right * Time.deltaTime * 10);
}
void Release()
{
ObjectPoolManager.Instance.ReleaseObject(this.gameObject);
}
}
3、ObjectPoolManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPoolManager : MonoSingleton<ObjectPoolManager>
{
// 是否打印对象池对象使用情况(默认不打印)
private bool isLogUsedPoolObject = false;
// 预制体(对象池对象)生成对象池的字典
private Dictionary<GameObject, ObjectPool<GameObject>> prefabPoolDictinary;
// 正在使用的对象的字典
private Dictionary<GameObject, ObjectPool<GameObject>> usedPoolObjectbDictinary;
// 对象池是否更新使用的标志
private bool dirty = false;
protected override void Awake() {
base.Awake();
// 初始化字典
prefabPoolDictinary = new Dictionary<GameObject, ObjectPool<GameObject>>();
usedPoolObjectbDictinary = new Dictionary<GameObject, ObjectPool<GameObject>>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (isLogUsedPoolObject == true && dirty == true)
{
PrintUsedPoolObjectStatue();
dirty = false;
}
}
/// <summary>
/// 是否打印对象池对象使用情况(默认不打印)
/// </summary>
/// <param name="isLogUsedPoolObject"></param>
public void SetIsLogUsedPoolObject(bool isLogUsedPoolObject) {
this.isLogUsedPoolObject = isLogUsedPoolObject;
}
/// <summary>
/// 孵化器孵化指定个数对象池对象
/// </summary>
/// <param name="prefab">预制体</param>
/// <param name="count">要预生成对象池对象</param>
public void WarmPool(GameObject prefab, int count) {
if (prefabPoolDictinary.ContainsKey(prefab)) {
Debug.Log("Pool for prefab " + prefab.name + " has already been created");
}
ObjectPool<GameObject> pool = new ObjectPool<GameObject>(()=> {
return InstantiatePrefab(prefab);
}, count);
// 添加到字典中
prefabPoolDictinary[prefab] = pool;
// 更新使用数据标志
dirty = true;
}
/// <summary>
/// 从对象池拿出指定对象使用
/// </summary>
/// <param name="prefab">要使用的对象</param>
/// <returns>对象池返回的可用对象</returns>
public GameObject SpawnObject(GameObject prefab) {
return SpawnObject(prefab,Vector3.zero,Quaternion.identity);
}
public GameObject SpawnObject(GameObject prefab, Vector3 position, Quaternion rotation)
{
// 如果该预制体没有孵化,则先进行孵化 1 个
if (prefabPoolDictinary.ContainsKey(prefab) == false) {
WarmPool(prefab, 1);
}
// 从对象池中获取对象
ObjectPool<GameObject> pool = prefabPoolDictinary[prefab];
GameObject clone = pool.GetObjectPoolContainerItem();
// 设置对象的位置旋转,显示物体
clone.transform.position = position;
clone.transform.rotation = rotation;
clone.SetActive(true);
// 把拿出来的对象添加到已使用的字典中
usedPoolObjectbDictinary.Add(clone, pool);
// 更新使用数据标志
dirty = true;
return clone;
}
/// <summary>
/// 释放使用的对象池对象
/// </summary>
/// <param name="clone">对象</param>
public void ReleaseObject(GameObject clone) {
clone.SetActive(false);
// 已使用的字典中
if (usedPoolObjectbDictinary.ContainsKey(clone))
{
usedPoolObjectbDictinary[clone].ReleaseItem(clone);
usedPoolObjectbDictinary.Remove(clone);
// 更新使用数据标志
dirty = true;
}
else {
Debug.Log("No pool contains the object: " + clone.name);
}
}
/// <summary>
/// 打印吃对象使用情况
/// </summary>
private void PrintUsedPoolObjectStatue() {
foreach (KeyValuePair<GameObject, ObjectPool<GameObject>> keyVal in prefabPoolDictinary)
{
Debug.Log(string.Format("Object Pool for Prefab: {0} In Use: {1} Total {2}", keyVal.Key.name, keyVal.Value.CountUsedItems, keyVal.Value.Count));
}
}
/// <summary>
/// 生成函数,父物体为被物体
/// </summary>
/// <param name="prefab">预制体</param>
/// <returns></returns>
private GameObject InstantiatePrefab(GameObject prefab)
{
var go = Instantiate(prefab) as GameObject;
go.transform.parent = this.transform;
go.SetActive(false);
return go;
}
}
4、ObjectPool
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 对象池
/// </summary>
public class ObjectPool <T>
{
// 对象池中的对象列表
private List<ObjectPoolContainer<T>> listObjects;
// 对象池中使用了的对象字典
private Dictionary<T, ObjectPoolContainer<T>> usedObjectDictionary;
// 回调事件
private Func<T> factoryFuc;
// 对象列表索引
private int lastObjectIndex =0;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="factoryFunc">工厂回调函数</param>
/// <param name="initialSize">初始化个数</param>
public ObjectPool(Func<T> factoryFunc, int initialSize)
{
this.factoryFuc = factoryFunc;
listObjects = new List<ObjectPoolContainer<T>>();
usedObjectDictionary = new Dictionary<T, ObjectPoolContainer<T>>();
Warm(initialSize);
}
/// <summary>
/// 孵化器,生成对象池实例
/// </summary>
/// <param name="capacity">实例个数</param>
private void Warm(int capacity)
{
for (int i =0; i < capacity; i++)
{
CreateContainer();
}
}
/// <summary>
/// 生成对象池实例
/// </summary>
/// <returns></returns>
private ObjectPoolContainer<T> CreateContainer()
{
ObjectPoolContainer<T> container = new ObjectPoolContainer<T>();
// 生成实例
container.Item = factoryFuc.Invoke();
// 实例添加到对象池列表中
listObjects.Add(container);
return container;
}
/// <summary>
/// 从对象列中获取可用的对象
/// </summary>
/// <returns>返回可用的对象</returns>
public T GetObjectPoolContainerItem() {
ObjectPoolContainer<T> container = null;
for (int i = 0; i < listObjects.Count; i++)
{
// 对象列表索引递增,并且防止越界
lastObjectIndex++;
lastObjectIndex %= listObjects.Count;
// 如果列表中的对象正在使用,则进行下一循环,否则返回该对象,并退出循环
if (listObjects[lastObjectIndex].Used)
{
continue;
}
else {
container = listObjects[lastObjectIndex];
break;
}
}
// 如果没有可用的对象,重新生成一个对象
if (container == null) {
container = CreateContainer();
}
// 标记当前对象已经被使用,并添加到使用列表中
container.Consume();
usedObjectDictionary.Add(container.Item,container);
return container.Item;
}
/// <summary>
/// 释放正在使用的对象
/// </summary>
/// <param name="item">要释放的对象</param>
/// <returns>true:释放成功/false: 释放失败</returns>
public bool ReleaseItem(object item) {
return ReleaseItem((T)item);
}
/// <summary>
/// 从已使用字典中,释放正在使用的对象
/// </summary>
/// <param name="item">释放对象</param>
/// <returns>true:释放成功/false: 释放失败</returns>
public bool ReleaseItem(T item) {
// 判断是否存在已使用对象字典中
if (usedObjectDictionary.ContainsKey(item))
{
// 存在,即释放该对象,并从已使用字典中移除
ObjectPoolContainer<T> container = usedObjectDictionary[item];
container.Release();
usedObjectDictionary.Remove(item);
return true;
}
else {
Debug.Log("This object pool does not contain the item provided: " + item);
return false;
}
}
/// <summary>
/// 获取对象池对象列表中的个数
/// </summary>
public int Count {
get {
return listObjects.Count;
}
}
/// <summary>
/// 获取对象池已经使用的对象个数
/// </summary>
public int CountUsedItems {
get {
return usedObjectDictionary.Count;
}
}
}
5、ObjectPoolContainer
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 对象池中的对象使用情况
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObjectPoolContainer<T>
{
// 获取实例
private T item;
public T Item { get => item; set => item = value; }
// 是否使用
public bool Used { get; private set; }
/// <summary>
/// 使用该对象池的对象
/// </summary>
public void Consume() {
Used = true;
}
/// <summary>
/// 释放对象池中的对象
/// </summary>
public void Release() {
Used = false;
}
}
6、MonoSingleton
using UnityEngine;
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance = null;
private static readonly object locker = new object();
private static bool bAppQuitting;
public static T Instance
{
get
{
if (bAppQuitting)
{
instance = null;
return instance;
}
lock (locker)
{
if (instance == null)
{
// 保证场景中只有一个 单例
T[] managers = Object.FindObjectsOfType(typeof(T)) as T[];
if (managers.Length != 0)
{
if (managers.Length == 1)
{
instance = managers[0];
instance.gameObject.name = typeof(T).Name;
return instance;
}
else
{
Debug.LogError("Class " + typeof(T).Name + " exists multiple times in violation of singleton pattern. Destroying all copies");
foreach (T manager in managers)
{
Destroy(manager.gameObject);
}
}
}
var singleton = new GameObject();
instance = singleton.AddComponent<T>();
singleton.name = "(singleton)" + typeof(T);
singleton.hideFlags = HideFlags.None;
DontDestroyOnLoad(singleton);
}
instance.hideFlags = HideFlags.None;
return instance;
}
}
}
protected virtual void Awake()
{
bAppQuitting = false;
}
protected virtual void OnDestroy()
{
bAppQuitting = true;
}
}
七、参考工程
八、参考资料
参考的 Github 工程:https://github.com/thefuntastic/unity-object-pool