对象池是为了有效避免重复资源利用造成的内存碎片等问题,优化性能消耗
思想:对象池的思路→
一个人物或者一个对象可能不止拥有一个对象池子,所以,需要一个容器装载不同种类的对象池
- 需要一个装载不同种类的对象池的容器,在每一种池子中,需要有该种池子的名字name,该池子是需要缓存什么样的对象的池子(GameObject)、还需要知道当前最多需要缓存几个对象(GameObject)
设计好池子类后,为了方便,可以在外部设置池子的内容为 什么,所以需要一个动态增容的容器来存储配置了几个池子!(简单方便,优先考虑List)
那这种时候我们肯定是希望能通过池子的名字找到对应的池子的,所以可以考虑使用字典来实现,键就是 池子名,值应该是什么呢,先想到,池子中的对象是一个个循环利用起来的,那么我们就自然而然的想到了队列,也就是说,我们字典的值就是队列!
所以我们实例化一个字典对象出来:
2.private Dictionary<string,Queue<GameObject>> _poolCenter = new Dictionary<string,Queue<GameObject>>();
好了,基本的框架已经构造好了,那么接下来就是考虑初始化的问题了,初始化应该实现什么样的内容呢? 考虑到要在外部配置对象池,所以在初始化中先加一个判断是否外面有配置对象池数据,没有的话直接return掉
3.那接下来该实现什么?嗯……--- 既然配置好了指定数量的对象池种类,我们在初始化中,根据对象池实现的目的,我们需要把每一个种类的池子中的每一个预先设置好的对象给实例化出来,然后放进各自对应的池子里,隐藏住,等到我们需要用的时候再拿出来!!然后在满足一定条件后,又给它放回池子里面,等待下一次的使用
初始化写完了,作为对象池,还需要提供一个方法给外部,让外部可以访问这个对象池,那这又是一个对象池管理器,所以我直接可以给它设计成单例模式,只存在唯一的实例,
那这个提供给外部调用的方法,它的参数怎么考虑呢,想想……当外部调用的时候,我得告诉对象池管理器我调用的是哪个名字(种类)的池子,那参数1就是string类型的呗,用于存储名字,那还需不需要其它参数呢, 这里就可以考虑到两种情况
4.1 我在访问对象池的时候,我调用一遍对象池管理器提供给我的方法,就拿出了一个对象,那我可以直接传入位置和角度,直接在函数内部一步到位设置好对象的所有信息
4.2 我只传string,位置和角度我在外面自己设置,那这样的话函数的返回值就应该是一个GameObject对象了,以便于我去设置位置和角度
关于回收利用的问题,在这里我直接在取出来后又给它放回池子里面了,好像有点问题?
但我觉得应该在满足指定的条件下,把放入池子中的item又给置回false,毕竟拿出来的时候才给它置成true嘛
补充的回收代码,如果用这个的话,那下面的取出后立马放回池子的代码就得删掉
/// <summary>
/// 回收对象到对象池中
/// </summary>
/// <param name="name">对象池名称</param>
/// <param name="item">要回收的对象</param>
public void Recycle(string name, GameObject item)
{
if (_poolCenter.ContainsKey(name))
{
item.SetActive(false);
item.transform.SetParent(_poolItemParent.transform);
_poolCenter[name].Enqueue(item);
}
else
{
Debug.Log("试图回收到不存在的池子:" + name);
}
}
using……
public class GamePoolManager : Singleton<GamePoolManager>
{
//1.需要先缓存我们需要缓存的对象(我们在外面配置对象)
//2.缓存
//3.让外部可以获取缓存好的对象
//4.回收对象回到对象池
[System.Serializable]
private class PoolItem
{
public string ItemName;
public GameObject Item;
public int InitMaxCount;
}
[SerializeField] private List<PoolItem> _configPoolItem = new List<PoolItem>();
private Dictionary<string,Queue<GameObject>> _poolCenter = new Dictionary<string,Queue<GameObject>>();
private GameObject _poolItemParent;
private void Start()
{
_poolItemParent = new GameObject("对象池Item的父对象");
_poolItemParent.transform.SetParent(this.transform);
InitPool();
}
private void InitPool()
{
//1.先检查外面的配置是不是空的
if (_configPoolItem.Count == 0) return;
//外循环处理有几个不同的池子
for(var i = 0; i < _configPoolItem.Count; i++)
{
//内循环处理每个池子要缓存多少个对象
for(int j = 0; j < _configPoolItem[i].InitMaxCount; j++)
{
var item = Instantiate(_configPoolItem[i].Item);
item.SetActive(false); //隐藏好缓存的对象
item.transform.SetParent(_poolItemParent.transform); //设置为子对象方便管理
//判断池子当中有没有一个这个对象
if (!_poolCenter.ContainsKey(_configPoolItem[i].ItemName))
{
//如果当前对象池中没有一个叫做ATKSound的池子,那就创建一个
_poolCenter.Add(_configPoolItem[i].ItemName, new Queue<GameObject>());
//然后把item加入到池子中去
_poolCenter[_configPoolItem[i].ItemName].Enqueue(item);
}
else
{
//否则 就说明已经有一个叫做ATKSound的池子了,那我直接把item放进该池子
_poolCenter[_configPoolItem[i].ItemName].Enqueue(item);
}
}
}
}
/// <summary>
/// 获取池子,直接传入参数设置对象的位置
/// </summary>
/// <param name="name"></param>
/// <param name="position"></param>
/// <param name="rotation"></param>
public void TryGetPoolItem(string name,Vector3 position,Quaternion rotation)
{
if (_poolCenter.ContainsKey(name))
{
//判断有没有一个名为name的池子存在
var item = _poolCenter[name].Dequeue();
item.transform.position = position;
item.transform.rotation = rotation;
item.SetActive(true);
_poolCenter[name].Enqueue(item); //显示出来后就又给他放到队列的末尾,重新开始排队
}
else
{
Debug.Log("当前申请的池子" + name + "不存在");
}
}
/// <summary>
/// 获取池子中的内容并自己在外部设置对象的位置
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public GameObject TryGetPoolItem(string name)
{
if (_poolCenter.ContainsKey(name))
{
//判断有没有一个名为name的池子存在
var item = _poolCenter[name].Dequeue();
item.SetActive(true);
_poolCenter[name].Enqueue(item); //显示出来后就又给他放到队列的末尾,重新开始排队
return item;
}
Debug.Log("当前申请的池子" + name + "不存在");
return null;
}
//具体的回收函数在每个对象里面自己去实现,这里我们一出列就又入列了,所以在这里不用写回收函数
}