unity 易用对象池

好久没写博客了,变懒了,最近想写的东西不多,这次来说说对象池吧。

对象池就是缓存池,提前把对象创建出来加入缓存池,需要的时候直接取出来用,删除的时候把物体放入到缓存池而不是真的删除,通过内存来换效率,减少画面的卡顿。


对象池是属于项目的底层模块,会被很多地方调用。所以一定得好用(我们得站在使用者的角度上来看),参数应该尽量少,简单明了。接下来我们一步步来制作:

1·对象池类肯定是个单例这个没得说了:

public class GameObjectsPool
    {
        GameObject ObjParent = null;
        private static readonly GameObjectsPool _instance = new GameObjectsPool();
        public static GameObjectsPool instance
        {
            get
            {
                return _instance;
            }
        }


2·我们希望对象池可以进行分类,就像垃圾的分类回收一样,这样我们可以轻松的对某类对象进行处理、统计,比如删除某类型的所有对象,在获取对象时也只是从其分类下去找,简单、高效,所以我们得有这个一个容器:

private Dictionary<string, List<GameObject>> _dictGameObjects = new Dictionary<string, List<GameObject>>();


3·定义好了数据容器,那我们就可以来写创建接口了。因为对象分类,所以肯定要传入类型信息,对象要加载进来,需要相应地址。所以我们的入参有类型和路径就够了:

private GameObject CreateObject(string strType, string strPath)
        {
            if (_dictGameObjects.ContainsKey(strType)
                && _dictGameObjects[strType].Count > 0)
            {
                GameObject Obj = _dictGameObjects[strType][0];
                _dictGameObjects[strType].RemoveAt(0);
                return Obj;
            }

            return LoadPrefab(strType, strPath);
        }

        private GameObject LoadPrefab(string strType, string strPath)
        {
            GameObject obj = Resources.Load(strPath) as GameObject;
            if (null == obj)
            {
                return null;
            }

            GameObject InsObj = GameObject.Instantiate(obj);
            if (null == InsObj)
            {
                return null;
            }

            GlObjInfo comObjInfo = InsObj.AddComponent<GlObjInfo>();
            if (null != comObjInfo)
            {
                comObjInfo.strTypeName = strType;
            }
            return InsObj;
        }

函数开头先判断,如果缓存池里面有相应的对象,那么直接返回List中的第一个元素,如果已无多余的对象,那么根据路径加载一个新的对象。先忽视这个ObjInfo,马上就会提到它。


4·有了创建自然要有删除,因为对象必须要有类型信,所以我们的接口一般来说是长这样的:

public void Destroy(string strType, GameObject desGameObject)

但是这样我觉得还是不够完美,因为这个接口跟Unity自带的Destroy入参不一致,第一次看到这个接口的时候,使用者可能不太明白这个strType是什么东西。然后多了个入参使用起来也更不方便。所以在每个对象创建的时候都给其挂上了GlObjInfo的脚本,用来存储对象的类型信息:

public void Destroy(GameObject desGameObject)
        {
            if (null == desGameObject)
            {
                return;
            }

            GlObjInfo comObjInfo = desGameObject.GetComponent<GlObjInfo>();
            if (null == comObjInfo)
            {
                return;
            }

            string strType = comObjInfo.strTypeName;
            if (!_dictGameObjects.ContainsKey(strType))
            {
                _dictGameObjects.Add(strType, new List<GameObject>());
            }

            if (_dictGameObjects[strType].Contains(desGameObject)) //重复删除的情况
            {
                return;
            }

            if (null == ObjParent) //需要把删除的GameObject挂载到某个节点下方
            {
                ObjParent = new GameObject("GameObjetsPoolNode");
                GameObject.DontDestroyOnLoad(ObjParent);
            }

            desGameObject.transform.SetParent(ObjParent.transform);
            desGameObject.SetActive(false);
            _dictGameObjects[strType].Add(desGameObject); //加入容器
        }

删除的时候通过获取GlObjInfo的组件获取其类型信息,代码都有些注释,相信大家都能看得懂。


写到这里,对象池主要的功能创建、删除都有了,其他的只要在这基础上增加就行了。但是仍然感觉不太好,因为CreateObject接口的第二个参数是个地址,每次调用都得找到对应的Resource相对地址,那肯定很不爽,所以这也是上面的接口为Private的原因,我们应该在上面再封装一层,让使用者能够更简单的使用。我的方法是这样的,为每个类型的资源单独写一个Create接口,只用传入相应的枚举,利用枚举的Attribute信息来得知资源的加载路径,以UI为例:

 public class GameObjPoolType
    {
        public enum UI
        {
            [Description("UIPrefab/Dlg_A")]
            DLG_A,
        }
    }

        public GameObject CreateObjByUIType(GameObjPoolType.UI eUIType)
        {
            string strInfo = eUIType.ToString();
            return this.CreateObject(strInfo, strEditorUIPath + GetEnumDescription(eUIType.GetType().GetField(strInfo)));
        }

        private string GetEnumDescription(FieldInfo objFieldInfo)
        {
            if (null == objFieldInfo)
            {
                return string.Empty;
            }

            object[] objs = objFieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
            if (objs == null || objs.Length == 0)
            {
                return string.Empty;
            }

            DescriptionAttribute da = objs[0] as DescriptionAttribute;
            if (null == da)
            {
                return string.Empty;
            }

            return da.Description;
        }

这样,我们就把可以把传入strType和strPath的CreateObject函数完全消化在类内部,对外只暴露一个GameObjPoolType下的UI枚举就好了,对象的创建和销毁也变得非常简单:

            GameObject objTest = GameObjectsPool.instance.CreateObjByUIType(GameObjPoolType.UI.DLG_A);
            GameObjectsPool.instance.Destroy(objTest);
至于其他例如预加载等锦上添花功能,只要在这个基础上继续编写就好了,非常简单。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值