欢迎加入光光的奇妙冒险,我是你们的煎饼光子老师。
对象池,我在最初学习时觉得这一定是个非常复杂的东西,但其实从现在看过去,对象池其实非常简单。
首先对象池也叫做缓存池,是常见的一种优化内存的手段(划重点,常用,一定要学会哦)
(如果想要直接获取代码的话,可以直接免费下载在文章上方的相关资源)
再来看,对象池主要用于面对以下问题:
1.对象的频繁创建
频繁的实例化对象会带来一定的性能开销
2.对象的频繁销毁
对象的频繁销毁会产生大量的内存垃圾,会造成GC(垃圾回收)的频繁触发
GC的触发,内存的释放,可能会带来卡顿感,影响玩家体验
省流:就是游戏中如果有需要频繁创建和销毁对象你就要考虑使用对象池了
既然知道面对什么问题,那对象池的作用也就不难理解了
缓存池(对象池)的主要作用是优化资源管理,提高程序性能。
主要通过重复利用已经创建的对象,避免频繁的创建和销毁过程
从而减少系统的内存分配和垃圾回收带来的开销。
知道作用了,原理自然也是要知道的
缓存池 (对象池) 的基本原理:
目标:
通过重复利用已经创建的对象,避免频繁的创建和销毁过程
从而减少系统的内存分配和垃圾回收带来的开销
实现思路:
用一个"柜子"中的"各种抽屉"来装"东西"
用时去拿(没有就创造,存在就获取)
不用就还( 将"东西"分门别类的放入"抽屉" 中)
光看上面的文字原理,可能还是很复杂,接下来我直接把代码端上来,大家通过代码并结合上面的文字原理,相信就能够自己写出一个对象池了
首先,需要一个泛型单例基类,对象池会继承这样一个基类
(在我上传的资源中,也包括我整理的单例模式基类框架和对象池框架,大家有需要可以去看看)
using System;
using System.Reflection;
using UnityEngine;
/// <summary>
/// 单例模式基类
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class BaseMgr<T>where T: class//注意基类是抽象类
{
//不继承mono的单例子类 还要声明一个私有的无参构造,为了不让外面调用
private static T instance;
//通过属性获取
public static T Instance
{
get
{
if (instance == null)
{
//利用反射得到无参私有的构造函数 来用于对象的实例化
Type type = typeof(T);
ConstructorInfo info = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,
null,
Type.EmptyTypes,
null);
if (info != null)
instance = info.Invoke(null) as T;
else
Debug.LogError("没有找到对应的无参构造函数");
}
return instance;
}
}
}
然后是我们的对象池“本池”
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoolMgr : BaseMgr<PoolMgr>
{
//不继承mono的单例还要声明一个私有的无参构造,为了不让外面调用
private PoolMgr() { }
//柜子(字典),键是抽屉名,值是抽屉(存储容器)
//注意:对象名和抽屉名相同
public Dictionary<string, Stack<GameObject>> poolDic = new();
//取
public GameObject PoolGet(string name)
{
GameObject obj;
//如果柜子里有这个抽屉,并且抽屉里有对象,就返回一个对象
if (poolDic.ContainsKey(name) && poolDic[name].Count > 0)
{
//弹出栈中对象,返回给外部使用
obj = poolDic[name].Pop();
obj.SetActive(true);
}
else
{
//没有就通过资源加载 实例化出对象返回给外部
//切记预制体要实例化才能使用
obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
//重命名对象,避免对象名字重复,系统为其加clone后缀
obj.name = name;
}
return obj;
}
//放
public void PoolRelease(GameObject obj)
{
//先将对象失活,再放到抽屉里
obj.SetActive(false);
//如果没有抽屉就创建抽屉
//注意:对象名和抽屉名相同
if(!poolDic.ContainsKey(obj.name))
poolDic.Add(obj.name, new Stack<GameObject>());
//将对象压到栈里
poolDic[obj.name].Push(obj);
}
//清空整个柜子
//主要是切换场景时使用
public void PoolClear()
{
poolDic.Clear();
}
}
完成上面的工作后,还需要创建一个脚本挂载在生成的对象上,脚本中包含一个方法:
在对象生成1s后,调用对象池的回收方法将对象回收
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DelayRemove : MonoBehaviour
{
private void OnEnable()
{
Invoke("RemoveMe", 1f);
}
private void RemoveMe()
{
PoolMgr.Instance.PoolRelease(this.gameObject);
}
}
最后,写一个测试脚本来测试一下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
private void Start()
{
TestMgr.Instance.TestLog();
//TestMgr2.Instance.TestLog();
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
PoolMgr.Instance.PoolGet("Test/Cube");
}
if (Input.GetMouseButtonDown(1))
{
PoolMgr.Instance.PoolGet("Test/Sphere");
}
}
}
如果成功的话,接下来运行游戏后,你点击左键就可以生成一个立方体,点击右键会生成一个球体,并且过1s后,它们就会被对象池回收,这样就实现了反复利用已经创建的对象,从而避免了频繁的创建和销毁。
这就是对象池,怎么样,很简单吧。