对象池的目的:
减少内存分配和垃圾回收: 创建和销毁游戏对象需要分配和释放内存,而频繁的内存分配和垃圾回收可能导致性能问题和帧率下降。通过使用对象池,您可以预先创建一组对象,并在需要时重复使用它们,从而减少了内存分配和垃圾回收的负担。
提高创建速度: 对象池避免了创建新对象的开销,因为您可以重复使用已经存在于池中的对象。这可以显著提高游戏对象的创建速度,尤其是在需要频繁创建和销毁对象的情况下,例如生成粒子、敌人、子弹等。
为了更好的理解抽象的概念 我将对象池(List)喻为抽屉 用衣柜(字典)存储这些所有的抽屉(对象池)
对象池也分不同的类型 比如游戏中的子弹是一种类型,特效是一种类型,敌人,粒子等等
这些不同类型的对象都需要不同的对象池来存储 所以我用Lsit存储这些单个类型对象池s
字典存放所有类型的对象池
首先 先构建一个存放对象的对象池,也就是抽屉List
/// <summary>
/// 抽屉 单个类型对象的对象池
/// </summary>
public class PoolData
{
//抽屉父对象 窗口中管理抽屉中所有的子对象
public GameObject fatherObj;
public List<GameObject> poolList;
/// <summary>
/// 创建一个新的抽屉(对象池),并设置窗口面板层级关系
/// </summary>
/// <param name="obj"></param>
/// <param name="poolObj"></param>
public PoolData(GameObject obj, GameObject poolObj)
{
fatherObj = new GameObject(obj.name);
fatherObj.transform.parent = poolObj.transform;
//抽屉容器 用于存放抽屉里的对象
poolList = new List<GameObject>() { obj };
//将对象添加到List当中
PushObj(obj);
}
//往抽屉里添加对象
public void PushObj(GameObject obj)
{
//先失活
obj.SetActive(false);
poolList.Add(obj);
obj.transform.parent = fatherObj.transform;
}
//从抽屉中取出对象
public GameObject GetObj()
{
GameObject obj = null;
//取出第一个
obj = poolList[0];
obj.SetActive(true);
poolList.RemoveAt(0);
//中断父子关系
obj.transform.parent = null;
return obj;
}
}
柜子(字典) 用于存放和管理所有类型的对象池
/// <summary>
/// 柜子
/// </summary>
public class Pool : SingletonAutoMono<Pool>
{
//对象池容器(柜子) 存储所有类型的对象池(抽屉)
public Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();
//对象池 窗口管理(柜子) 父对象
private GameObject poolObj;
/// <summary>
/// 得到对应类型对象池中的对象
/// </summary>
/// <param name="name">对象池的名字/对象名</param>
/// <param name="path">资源加载路径</param>
/// <returns></returns>
public GameObject GetObj(string name, string path)
{
//先申明一个对象
GameObject obj = null;
//如果字典中有该类型的对象池(柜子中有这个对象的抽屉) 且 对象池中有库存
if (poolDic.ContainsKey(name) && poolDic[name].poolList.Count > 0)
{
//从抽屉中得到对象
obj = poolDic[name].GetObj();
}
else
{
//加载并创建
obj = Instantiate(Resources.Load<GameObject>(path));
//将对象名和对象池名一致
obj.name = name;
}
return obj;
}
/// <summary>
/// 放回对象池或新建对象池
/// </summary>
/// <param name="name">对象池的名字</param>
/// <param name="obj">当前对象</param>
public void PushObj(string name, GameObject obj)
{
//第一次会新建一个柜子父物体
//所有对象池的父物体
if (poolObj == null)
poolObj = new GameObject("Pool");
//如果有抽屉
if (poolDic.ContainsKey(name))
{
poolDic[name].PushObj(obj);
}
//没有就在柜子里添加一个抽屉
//在字典中新建一个该类型的对象池
else
{
poolDic.Add(name, new PoolData(obj, poolObj));
}
}
/// <summary>
/// 转场景的时候清空对象池 在转场景后 新场景没有对象 但对象池中可能还包含对上个场景对象的引用
/// </summary>
public void Clear()
{
poolDic.Clear();
poolObj = null;
}
在创建的对象中挂载这个脚本即可
public class DelyPush : MonoBehaviour
{
void OnEnable()
{
//延迟1秒 将对象放回对象池/新建对象池进行存放
Invoke("Push", 1);
}
void Push()
{
Pool.Instance.PushObj(gameObject.name, gameObject);
}
}