目录
前言
基本结构
对象池代码
对象池管理器代码
使用
总结
前言
经过上一篇对象池1的了解,已经做到了使用Unity自带的ObjectPool进行内存优化。本篇自己构建一个对象池管理器(Manager),实现对象池的创建、删除、加载资源等功能。这不是多此一举,因为在定义这个对象池管理器过程中,可以了解一个管理器Manager类的脚本,大致应该是如何的结构,之后还会写到很多的管理器Manager。
基本结构
建立一个对象池的管理器(分配器),因为在场景中有可能同时存在多个对象池,需要一个管理器来管理从池中取出对象、回收对象等方法。也就是说,对象池管理器是一个工具集,包含了对象池操作的一般方法。
对象池管理设计的总体结构如下图,有一个总的管理器(ChunkAllocator),它是一个管理器(Manager),因此是单例模式(即场上只有唯一一个实例);在ChunkAllocator建立一个字典(Dictionary<kew,value>)用来存储所有的对象池(Chunk),每个对象池(Chunk)都有一个列表List<T>,存储所有的对象。
在管理器(ChunkAllocator)和对象池(Chunk)中,建立了以下场景中使用的工具方法:
对象池代码 Chunk.cs
针对单个对象池的管理(想象上一篇中的Trophy对象池),需要实现的功能:1. 建立对象列表;2. 取一个对象;3. 消除一个对象;
public class Chunk
{
List<Object> objectList; //建立一个列表,作为对象池的容器
public bool IsHave => objectList.Count > 0; //判断列表中有没有对象
//当IsHave为True时才能使用GetObj()调取对象
public Chunk()
{//构造中先初始化容器(列表)
objectList = new List<Object>();
}
public Object GetObj()
{//从容器(列表)中取出对象
Object obj = objectList[objectList.Count - 1]; //取出第一个对象
objectList.RemoveAt(objectList.Count - 1); //容器(列表)中移除这个被取出的对象
return obj;
}
public void RevertObj(Object obj)
{//回收对象到容器(列表)
(obj as GameObject).transform.parent= null; //将对象父节点设为空
(obj as GameObject).SetActive(false); //将对象失活
objectList.Add(obj); //最后添加回容器(列表)中
}
}
对象池管理器代码 ChunkAllocator.cs
管理器Manager代码继承单例(所有的管理类都是single)。对象池管理器实现的功能:1. 建立对象池字典;2. 判断对象池是否已经存在;3. 从对象池中获取一个对象并加载到场景内; 4. 通过资源管理器向对象池加入一个对象,并将它加载到场景内;5. 清空对象池
*注:1. 这里使用的Resload(从Asset文件夹中加载资源)、Single(单例)等类,后面单开篇幅说明,这些是一个项目中的基本类;
2. 方法还不够全面,仅测试参考。
//对象池分配器,单例
public class ChunkAllocator : Single<ChunkAllocator>
{
Dictionary<string, Chunk> chunkList; //定义一个字典存储所有的对象池
public ChunkAllocator()
{//构造中先实例化字典
chunkList = new Dictionary<string, Chunk>();
}
private bool IsHavePool(string poolName)
{//用对象池的名字查询是否已存在
return chunkList.ContainsKey(poolName);
}
//获取对象
public Object GetObject(string poolName)
{//判断池子是否存在
if (!IsHavePool(poolName))
{
return new Object();//不存在就直接实例化一个Object
}
return chunkList[poolName].GetObj(); //存在则通过名字找到它,并拿取对象
}
//回收对象到池中
public void Revert(string poolName,Object obj)
{
if(IsHavePool(poolName)) //先判断对象池是否存在
{
chunkList[poolName].RevertObj(obj); //如果存在就直接回收进池
}
else
{
Chunk chunk= new Chunk(); //没有对应的对象池则先新建一个
chunk.RevertObj(obj); //再用新对象池回收该对象
chunkList.Add(poolName, chunk); //并把这个新对象池加入到字典中
}
}
//直接从对象池中获取对象
public GameObject GetGameObject(string poolName,GameObject gameObj,out bool isFirst,Transform parent=null)
{//(对象池名称,对象,是否第一次创建,父节点)
GameObject go = null;
if(!IsHavePool(poolName)) //先判断对象池是否存在
{//对象池不存在
go = GameObject.Instantiate(gameObj); //实例化一个物体
isFirst=true; //将“第一次创建”的标记设为True
}
else
{//对象池已存在
isFirst = !chunkList[poolName].IsHave; //池内有物体,就把“第一次创建”设false
if (chunkList[poolName].IsHave)
{
go = chunkList[poolName].GetObj() as GameObject;//池内有物体就获取它
}
else
{
go = GameObject.Instantiate(gameObj); //池内没有物体就实例化一个
}
}
if(parent != null)
{//如果有父节点的要求,就将物体设置到父节点下
go.transform.SetParent(parent);
}
go.transform.localScale = Vector3.one; //设置物体大小
go.transform.position = Vector3.zero; //位置
go.name= poolName; //名字
go.SetActive(true); //激活物体
return go; //返回物体
}
//从资源管理器中获取资源对象
public T GetObj<T>(string poolName,string resName) where T : Object
{
if (!IsHavePool(poolName))
{//如果对象池不存在,就用Resload加载Asset文件夹中的对象
return Resload.Instance.LoadAsset<T>(resName);
}
return chunkList[poolName].GetObj() as T; //如果对象池存在,则直接调取对象
}
//从资源管理器中获取预制体
public GameObject GetPrefab(string poolName,string resName,Transform parent=null)
{
GameObject go = null;
if (!IsHavePool(poolName))
{//如果对象池不存在,就用Resload加载
go = Resload.Instance.LoadPrefab(resName);
}
else
{//如果有对象池,先判断对象池中是否有东西
if (chunkList[poolName].IsHave)
go = chunkList[poolName].GetObj() as GameObject;//如果有,就从池中获取
else
go = Resload.Instance.LoadPrefab(resName);//否则就通过路径加载
}
if(parent!=null)
{//设置父节点
go.transform.SetParent(parent);
}
go.transform.localScale = Vector3.one;
go.transform.position = Vector3.zero;
go.SetActive(true);
go.name = poolName;
return go;
}
//清空对象池
public void ClearPool(string poolName="")
{
if(poolName=="")
{
chunkList.Clear();
return;
}
if(IsHavePool(poolName))
{
chunkList.Remove(poolName); //如果对象池已存在,就清除
}
}
}
使用
我们还是使用在上一篇的奖品(Trophy)中,场景结构不变,仅修改TrophyManager.cs
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Pool;
public class TrophyManager : MonoBehaviour
{
public GameObject[] trophies;
public int number = 50;
public bool useObjectPool;
public bool useChunkAllocator;
private ObjectPool<GameObject> trophyPool;
void Start()
{
}
void Update()
{
for (int i = 0; i < number; i++)
{
bool isfirst = false;
GameObject trophy = ChunkAllocator.Instance.GetGameObject("trophyPool", trophies[Random.Range(0, trophies.Length)], out isfirst, transform);
trophy.transform.localPosition = Random.insideUnitSphere;
trophy.AddComponent<Trophy>();
if (isfirst)
{
trophy.GetComponent<Trophy>().destroyEvent.AddListener(() =>
{
ChunkAllocator.Instance.Revert("trophyPool", trophy);
});
}
}
总结
可以看出,管理器Manager是一个方法的集合,也就是把常用的一些方法放到一个类中。而且这个管理器的实现方式也有很多种,并根据不同的项目需要加入不同的工具方法,但是这个思路和结构是之后建立的项目通用的。