内存池模块
一,适用场景:
游戏中多次生成然后又销毁的物体,例如子弹,重复刷的怪物
二,内存池的好处
每当程序实例化(如Instantiate() )一个对象的时候,就在内存上占用了位置,而当destory了物体
内存并没有因此释放,内存会直到内存满载或者满足很多条件的时候才会去释放没有被使用的内存,
而这个释放内存的过程中,会不断调用垃圾收集(Garbage Collection)技术(即GC),而GC很吃cpu性能
内存池术将尚未用到的对象放在游戏流程之前——比如在读取界面的时候——进行预先实例化。这样游戏可以从『池子』中重用对象,而不需在游戏流程中不断地创建和销毁
即用内存换cpu的效率
三,内存池代码
思路:
- 需要是单例模式来方便访问内存池(注:每个需要内存池的物体都需要挂上内存池脚本,但内存池的实例就一个)
- 使用Dictionary<string 物体的类别名,List
>来做成抽屉存放相应的物体 - 内存池允许取出与放入,分别两个方法完成
- 使用Resources类加载物体,并用Instantiate() 实例化(克隆出来后才出现在场景中)
改进思路:
-
加入一个pool在场景中收纳失活的物体,使inspector面板变干净
-
加入一个clear方法,用于切换场景时,清空缓存池。(防止切换场景时,游戏内物体已经删除,但pooldic中仍含有它的引用的情况(会在GetObj()报错))
-
(选改)若想要让inspector中的pool中的失活物体有一个分类(即再加入一个父物体进行容纳),则可以创建一个类存储父物体与List进行操作(这将把Dictionary <string,List
>更改为Dictionary <string,className)详见https://www.bilibili.com/video/BV1C441117wU?p=4
三个脚本,一个主体,两个次要(可按压入的时机,与取出的时机的要求改动)
1.主体
public class poolMgr : test1<poolMgr>
{
public Dictionary <string,List<GameObject>> pooldic=new Dictionary<string, List<GameObject>>();
public GameObject poolObj;////-------------改进1
public GameObject GetObj(string keyName)
{
GameObject obj = null;
if(pooldic.ContainsKey(keyName)&&pooldic[keyName].Count>0)//判断已经存在这种抽屉且抽屉内含有keyName类别的物体
{
obj = pooldic[keyName][0];//赋值取出
pooldic[keyName].RemoveAt(0);//删除抽屉中的引用
}
else//无这种抽屉或者已经没有这种物体的话,实例化一个出来(第一次实例化时不必先创建抽屉,抽屉在Push时在创建)
{
obj = GameObject.Instantiate(Resources.Load<GameObject>(keyName));
//Instantiate克隆出去的名字都带有(clone)后缀,故需要把obj的名字改成和抽屉(即原物体的名字)一致
obj.name = keyName;
}
obj.SetActive(true);//取出来的时候要激活
////改进1
obj.transform.parent = null;//解除父子关系
////
return obj;
}
public void PushObj(string keyName,GameObject obj)//外面物体的方法会在激活的一段时间后自动invoke PushObj这个方法
{ ////-------------改进1;
if (poolObj==null)//若收容的pool不存在,新建一个
{
poolObj = new GameObject("pool");
}
obj.transform.SetParent(poolObj.transform);//挂载
////
if (pooldic.ContainsKey(keyName))//如果有抽屉则将该物体压入list中;
{
pooldic[keyName].Add(obj);
}
else//如果没有抽屉
{
pooldic.Add(keyName, new List<GameObject> { obj});//创建抽屉,且把这个物体放进去
}
obj.SetActive(false);//放进去记得要失活
}
////改进2
public void clear()
{
pooldic.Clear();
}
////
}
注:该版本的内存池Resoures文件夹下物体的名字,与放入该物体的内存池抽屉keyName一致
次要一:挂在需要内存池存放的预设体上
public class PushDelay : MonoBehaviour
{
private void OnEnable()//位于OnEnable,每次激活都调用
{
Invoke("Push", 1f);
}
void Push()
{
poolMgr.GetInstance().PushObj(gameObject.name,gameObject);
}
}
次要二:挂在场景中的固定存在的物体上(如资源管理器,pool(空物体))
public class Get : MonoBehaviour
{
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
poolMgr.GetInstance().GetObj("Cube");
}
}
///改进2,当场景切换,该物体正在销毁时执行
private void OnDestroy()
{
poolMgr.GetInstance().clear();
}
///
}