理解
缓存池实际上是一个存放循环重复使用的容器。
例子:场景中的子弹,弹痕等
- 缓存池(好比一个橱子)
-
池子1(好比是一个橱子中的某一个抽屉)
- 池子1中的对象们
-
池子2(好比是一个厨子中的某一个抽屉)
- 池子2中的对象们
图示:
1.缓存池中没有,则创建
2.缓存池中存在,则取
3.加载采用Resources.load(name)根据名字加载,所以创建新对象时的名字,就是再次重用所取的名字,后续参数名字的一致保证了缓存池的实现。
-
缓存池中的抽屉类
缓存池中有多个抽屉对应不同的预制体对象类别,预制体分类存储。
public class PoolData {
public GameObject parentObj; //抽屉标识(用来分类对象)
public List<GameObject> poolList; //对象列表
/// <summary>
/// 构造函数
/// </summary>
/// <param name="obj">挂载的父对象</param>
/// <param name="poolObj">对象池根节点</param>
public PoolData(GameObject obj,GameObject poolObj)
{
parentObj = new GameObject(obj.name); //抽屉的名字
parentObj.transform.parent = poolObj.transform;
poolList = new List<GameObject>();
}
//往具体池子里放
public void Push(GameObject obj)
{
poolList.Add(obj);
obj.transform.parent = parentObj.transform;
obj.SetActive(false);
}
//往池子中取对象
public GameObject GetObj()
{
GameObject obj = null;
obj = poolList[0];
poolList.RemoveAt(0);
obj.SetActive(true);
obj.transform.parent = null;
return obj;
}
}
单例模板
public abstract class Singleton<T> : MonoBehaviour where T:MonoBehaviour
{
private static T instance;
public static T Instance {
get {
return instance;
}
}
protected virtual void Awake()
{
if (instance == null)
{
instance = this as T;
}
}
}
缓存池管理
public class PoolMgr : Singleton<PoolMgr>
{
protected override void Awake()
{
base.Awake();
}
//缓存池(缓存池下面是抽屉)
public Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();
//缓存池的父对象
private GameObject poolObj;
/// <summary>
/// 从池子中的抽屉中取
/// </summary>
/// <param name="name">抽屉的名字</param>
/// <returns></returns>
public GameObject GetObj(string name)
{
GameObject obj = null;
//存在这个抽屉且抽屉里面有东西
if (poolDic.ContainsKey(name)&&poolDic[name].poolList.Count>0)
{
obj = poolDic[name].GetObj();
}
else //动态加载生成
{
obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
//要和抽屉的名字一样
obj.name = name;
}
obj.SetActive(true);
obj.transform.parent = null;
return obj;
}
/// <summary>
/// 放入池子中的对应抽屉里
/// </summary>
/// <param name="name">抽屉名字</param>
/// <param name="obj">放入的物体</param>
public void Push(string name,GameObject obj)
{
if (poolObj == null)
poolObj = new GameObject("poolObj");
if (!poolDic.ContainsKey(name))
{
poolDic.Add(name, new PoolData(obj,poolObj));
}
poolDic[name].Push(obj);
}
完整例子
说明:这里将每个【抽屉】的名字与具体对象的名字同名,这里的作用是往缓存池中放对象时,直接能根据对象的名字在poolDic字典中找到对应的抽屉,如果有就放入,如果没有就新建一个抽屉。
using System.Collections.Generic;
using UnityEngine;
//缓存池中的抽屉
public class PoolData
{
public GameObject parentObj; //抽屉标识(用来分类对象)
public List<GameObject> poolList; //对象列表
/// <summary>
/// 构造函数
/// </summary>
/// <param name="obj">挂载的父对象</param>
/// <param name="poolObj">对象池根节点</param>
public PoolData(GameObject obj, GameObject poolObj)
{
parentObj = new GameObject(obj.name); //抽屉的名字
parentObj.transform.parent = poolObj.transform;
poolList = new List<GameObject>();
}
//往具体池子里放
public void Push(GameObject obj)
{
poolList.Add(obj);
obj.transform.parent = parentObj.transform;
obj.SetActive(false);
}
//往池子中取对象
public GameObject GetObj()
{
GameObject obj = null;
obj = poolList[0];
poolList.RemoveAt(0);
obj.SetActive(true);
obj.transform.parent = null;
return obj;
}
}
/// <summary>
/// 缓存池管理
/// </summary>
public class PoolMgr : Singleton<PoolMgr>
{
protected override void Awake()
{
base.Awake();
}
//缓存池(缓存池下面是抽屉)
public Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();
//缓存池的父对象
private GameObject poolObj;
/// <summary>
/// 从池子中的抽屉中取
/// </summary>
/// <param name="name">抽屉的名字</param>
/// <returns></returns>
public GameObject GetObj(string name)
{
GameObject obj = null;
//存在这个抽屉且抽屉里面有东西
if (poolDic.ContainsKey(name) && poolDic[name].poolList.Count > 0)
{
obj = poolDic[name].GetObj();
}
else //动态加载生成
{
obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
//要和抽屉的名字一样
obj.name = name;
}
obj.SetActive(true);
obj.transform.parent = null;
return obj;
}
/// <summary>
/// 放入池子中的对应抽屉里
/// </summary>
/// <param name="name">抽屉名字</param>
/// <param name="obj">放入的物体</param>
public void Push(string name, GameObject obj)
{
if (poolObj == null)
poolObj = new GameObject("poolObj");
if (!poolDic.ContainsKey(name))
{
poolDic.Add(name, new PoolData(obj, poolObj));
}
poolDic[name].Push(obj);
}
//清空缓存池
//切换场景的时候可能为null
public void Clear()
{
poolDic.Clear();
poolObj = null;
}
}
测试脚本
public class Test : BasePanel
{
private GameObject obj;
public Button GetBtn1;
public Button GetBtn2;
public Button PushBtn;
private void Start()
{
//往池子中取 1
GetBtn1.onClick.AddListener(() =>
{
obj = PoolMgr.Instance.GetObj("cube");
});
//往池子中放2
GetBtn2.onClick.AddListener(() =>
{
PoolMgr.Instance.GetObj("Sphere");
});
//往池子中放
PushBtn.onClick.AddListener(() =>
{
PoolMgr.Instance.Push("cube", obj);
});
}
}
将Test脚本挂载场景物体中,自己创建三个按钮,我这里用的Cube和Sphere做的测试,建一个Resources文件夹,将这两个物体拖进去制成预制体用于首次的动态加载。
缓存池这个小例子就可以测试了。