缓冲池(对象池)模块
缓存池(对象池)的主要作用是优化资源管理,提高程序性能。主要通过重复利用已经创建的对象,避免频繁的创建和销毁过程,从而减少系统的内存分配和垃圾回收带来的开销。
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 缓存池(对象池)模块 管理器
/// </summary>
public class PoolMgr : BaseManager<PoolMgr>
{
//声明柜子(Dictionary)和抽屉(List)容器
private Dictionary<string,List<GameObject>> poolDic = new Dictionary<string,List<GameObject>>();
private PoolMgr() { }
/// <summary>
/// 拿东西的方法
/// </summary>
/// <param name="name"> 抽屉容器的名字 </param>
/// <returns></returns>
public GameObject GetObjFromPool(string name)
{
GameObject obj;
//有抽屉 并且 抽屉中有对象
if (poolDic.ContainsKey(name) && poolDic[name].Count > 0)
{
//直接返回第一个对象
obj = poolDic[name][0];
poolDic[name].RemoveAt(0);
obj.SetActive(true);
}
//否则应该创建
else
{
//没有的话 通过资源加载 实例化一个对象并返回
obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
obj.name = name;
}
return obj;
}
/// <summary>
/// 往缓存池放入对象
/// </summary>
/// <param name="obj">希望放入的对象</param>
public void PushObjToPool(GameObject obj)
{
//并不是直接移除对象,而是将对象失活 再用时再激活
obj.SetActive(false);
//如果存在对应的抽屉容器 可以直接放
if (poolDic.ContainsKey(obj.name))
{
poolDic[obj.name].Add(obj);
}
//如果没有对应的容器可以先创建再放
else
{
poolDic.Add(obj.name, new List<GameObject>());
poolDic[obj.name].Add(obj);
}
}
/// <summary>
/// 用于清除整个柜子当中的数据
/// 使用场景主要好是 切换场景
/// </summary>
public void ClearPool()
{
poolDic.Clear();
}
}
窗口布局优化
现在直接关活对象,当之后项目做大了,抽屉多了,对象多了,游戏中成百上千个对象,在开发测试时不方便从Hierarchy窗口中查看对象获取信息,因此我们希望能优化一下Hierarchy窗口中的布局,将对象和抽屉的关系可视化
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 抽屉(池子中的数据)对象
/// </summary>
public class PoolData
{
//用来存储抽屉中的对象
private List<GameObject> dataList = new List<GameObject>();
//抽屉跟对象,用来布局
private GameObject rootObj;
//获取容器中对象的数量
public int Count => dataList.Count;
public PoolData(GameObject root,string name)
{
if (PoolMgr.isOpenLayout)
{
//创建抽屉父对象
rootObj = new GameObject(name);
//将抽屉父对象与柜子父对象设置为父子关系
rootObj.transform.SetParent(root.transform);
}
}
/// <summary>
/// 取出抽屉中的对象
/// </summary>
/// <returns></returns>
public GameObject GetGameObject()
{
//取出对象
GameObject obj = dataList[0];
dataList.RemoveAt(0);
//激活对象
obj.SetActive(true);
//取出去的时候断开父子关系
if (PoolMgr.isOpenLayout)
obj.transform.SetParent(null);
return obj;
}
/// <summary>
/// 将对象放入抽屉中
/// </summary>
/// <param name="obj"></param>
public void Push(GameObject obj)
{
//失活对象
obj.SetActive(false);
//设置父子关系
if (PoolMgr.isOpenLayout)
obj.transform.SetParent(rootObj.transform);
//添加进list
dataList.Add(obj);
}
}
/// <summary>
/// 缓存池(对象池)模块 管理器
/// </summary>
public class PoolMgr : BaseManager<PoolMgr>
{
//声明柜子(Dictionary)和抽屉(List)容器
private Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();
//池子根对象
private GameObject poolObj;
//是否开启布局功能
public static bool isOpenLayout = true;
private PoolMgr() { }
/// <summary>
/// 拿东西的方法
/// </summary>
/// <param name="name"> 抽屉容器的名字 </param>
/// <returns></returns>
public GameObject GetObjFromPool(string name)
{
GameObject obj;
//有抽屉 并且 抽屉中有对象
if (poolDic.ContainsKey(name) && poolDic[name].Count > 0)
{
//取出抽屉中的对象
obj = poolDic[name].GetGameObject();
}
//否则应该创建
else
{
//没有的话 通过资源加载 实例化一个对象并返回
obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
obj.name = name;
}
return obj;
}
/// <summary>
/// 往缓存池放入对象
/// </summary>
/// <param name="obj">希望放入的对象</param>
public void PushObjToPool(GameObject obj)
{
//如果根对象为空才创建
if (poolObj == null && isOpenLayout)
poolObj = new GameObject("Pool");
//并不是直接移除对象,而是将对象失活 再用时再激活
obj.SetActive(false);
//失活之后设置父对象
if(isOpenLayout)
obj.transform.SetParent(poolObj.transform);
//如果没有对应的容器可以先创建再放
if (!poolDic.ContainsKey(obj.name))
poolDic.Add(obj.name, new PoolData(poolObj, obj.name));
poolDic[obj.name].Push(obj);
}
/// <summary>
/// 用于清除整个柜子当中的数据
/// 使用场景主要好是 切换场景
/// </summary>
public void ClearPool()
{
poolDic.Clear();
poolObj = null;
}
}
对象上限优化
目前我们制作的缓存池模块
理论上来说,当动态创建的对象长时间不放回抽屉
每次从缓存池中动态获取对象时,会不停的新建对象
那么也就是对象的数量是没有上限的
场景上的某种对象可以存在n个
而对象上限优化指的就是
我们希望控制对象数量有上限
对于不重要的资源我们没必要让其无限加量
而是将"使用最久"的资源直接抢来用
主要目的:
更加彻底的复用资源 ,对对象的数量上限加以限制 ,可以优化内存空间,甚至优化性能(减少数量上限,可以减小渲染压力)
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 抽屉(池子中的数据)对象
/// </summary>
public class PoolData
{
//用来存储抽屉中的对象 记录的是没有使用的对象
private List<GameObject> dataList = new List<GameObject>();
//抽屉跟对象,用来布局
private GameObject rootObj;
//抽屉上限,场景上同时存在的对象的上限个数
private int maxNum;
//记录使用中的对象
private List<GameObject> usedList = new List<GameObject>();
//获取容器中对象的数量
public int Count => dataList.Count;
public int UsedCount => usedList.Count;
public bool NeedCreate => usedList.Count < maxNum;
public PoolData(GameObject root,string name, GameObject usedObj)
{
if (PoolMgr.isOpenLayout)
{
//创建抽屉父对象
rootObj = new GameObject(name);
//将抽屉父对象与柜子父对象设置为父子关系
rootObj.transform.SetParent(root.transform);
}
//创建抽屉时 外部会动态创建一个对象
//我们需要将它记录在使用中的容器中
usedList.Add(usedObj);
//DelayRemove 挂载在需要对象池创建的对象身上的脚本
//主要定义了maxNum,对象在对象池中的上限以及延迟调用PushObjToPool函数让对象失活
DelayRemove delayRemove = usedObj.GetComponent<DelayRemove>();
if (delayRemove == null)
{
Debug.LogError("请为使用对象池的预设体挂载DelayRemove脚本");
return;
}
maxNum = delayRemove.maxNum;
}
/// <summary>
/// 取出抽屉中的对象
/// </summary>
/// <returns></returns>
public GameObject GetGameObject()
{
//取出对象
GameObject obj;
if (Count > 0) //如果抽屉中有未使用的话直接使用
{
obj = dataList[0];
dataList.RemoveAt(0);
usedList.Add(obj);
}
else //抽屉中没有未使用的
{
//取使用中的List抽屉中的第一个,代表使用时间最长的
obj = usedList[0];
//并把它从list中移除掉
usedList.RemoveAt(0);
//由于还要使用,再把它记录到List的最后一个
usedList.Add(obj);
}
//激活对象
obj.SetActive(true);
//取出去的时候断开父子关系
if (PoolMgr.isOpenLayout)
obj.transform.SetParent(null);
return obj;
}
/// <summary>
/// 将对象放入抽屉中
/// </summary>
/// <param name="obj"></param>
public void Push(GameObject obj)
{
//失活对象
obj.SetActive(false);
//设置父子关系
if (PoolMgr.isOpenLayout)
obj.transform.SetParent(rootObj.transform);
//添加进list
dataList.Add(obj);
//这个对象已经不再使用 需要从使用中容器中移除
usedList.Remove(obj);
}
/// <summary>
/// 往使用中的容器添加对象
/// </summary>
/// <param name="obj"></param>
public void PushUsedList(GameObject obj)
{
//添加进list
usedList.Add(obj);
}
}
/// <summary>
/// 缓存池(对象池)模块 管理器
/// </summary>
public class PoolMgr : BaseManager<PoolMgr>
{
//声明柜子(Dictionary)和抽屉(List)容器
private Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();
//池子根对象
private GameObject poolObj;
//是否开启布局功能
public static bool isOpenLayout = true;
private PoolMgr() { }
/// <summary>
/// 拿东西的方法
/// </summary>
/// <param name="name"> 抽屉容器的名字 </param>
/// <returns></returns>
public GameObject GetObjFromPool(string name)
{
if (poolObj == null && isOpenLayout)
poolObj = new GameObject("Pool");
GameObject obj;
#region 没有加入上限时的逻辑
////有抽屉 并且 抽屉中有对象
//if (poolDic.ContainsKey(name) && poolDic[name].Count > 0)
//{
// //取出抽屉中的对象
// obj = poolDic[name].GetGameObject();
//}
////否则应该创建
//else
//{
// //没有的话 通过资源加载 实例化一个对象并返回
// obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
// obj.name = name;
//}
#endregion
if (!poolDic.ContainsKey(name) ||
(poolDic[name].Count == 0 && poolDic[name].NeedCreate))
{
//没有的话 通过资源加载 实例化一个对象并返回
obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
obj.name = name;
if (!poolDic.ContainsKey(name))//创建抽屉
poolDic.Add(name, new PoolData(poolObj, name, obj));
else
poolDic[name].PushUsedList(obj);//实例化出来的对象需要添加到使用中的List
}
//当抽屉中有对象 或者 使用中的对象超上限的情况下
else
{
obj = poolDic[name].GetGameObject();
}
return obj;
}
/// <summary>
/// 往缓存池放入对象
/// </summary>
/// <param name="obj">希望放入的对象</param>
public void PushObjToPool(GameObject obj)
{
//如果根对象为空才创建
if (poolObj == null && isOpenLayout)
poolObj = new GameObject("Pool");
//并不是直接移除对象,而是将对象失活 再用时再激活
obj.SetActive(false);
//失活之后设置父对象
if(isOpenLayout)
obj.transform.SetParent(poolObj.transform);
//如果没有对应的容器可以先创建再放
poolDic[obj.name].Push(obj);
}
/// <summary>
/// 用于清除整个柜子当中的数据
/// 使用场景主要好是 切换场景
/// </summary>
public void ClearPool()
{
poolDic.Clear();
poolObj = null;
}
}