Unity框架(缓存池)

前情提要

当开发人员要开发一款游戏时,需要在游戏引擎中不断地生成创建销毁

在这个过程中就会产生一些问题:

1.对象的频繁创建

频繁的实例化对象会带来一定的性能开销

2.对象的频繁销毁

对象的频繁销毁会造成大量的内存垃圾,会造成GC的频繁触发,GC的触发,内存的释放,可能会带来卡顿感,影响玩家的体验。

缓存池的主要作用

优化资源管理,提高程序性能,通过重复利用创建过的对象,避免频繁的创建和销毁过程,来减少系统的内存分配和垃圾回收带来的开销

代码实例:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 抽屉(池子中的数据)对象
/// </summary>
public class PoolData
{
    //用来存储抽屉中的对象 记录的是没有使用的对象
    private Stack<GameObject> dataStack = new Stack<GameObject>();

    //用来记录使用中的对象的 
    private List<GameObject> usedList = new List<GameObject>();

    //抽屉上限 场景上同时存在的对象上限个数
    private int maxNum;

    //抽屉根对象 用来进行布局管理的对象
    private GameObject rootObj;

    //获取容器中是否有对象
    public int Count => dataStack.Count;

    public int UsedCount => usedList.Count;

    /// <summary>
    /// 进行使用中对象数量和最大容量进行比较 小于返回true 需要实例化
    /// </summary>
    public bool NeedCreate => usedList.Count < maxNum;

    /// <summary>
    /// 初始化构造函数
    /// </summary>
    /// <param name="root">柜子(缓存池)父对象</param>
    /// <param name="name">抽屉父对象的名字</param>
    public PoolData(GameObject root, string name, GameObject usedObj)
    {
        //开启功能时 才会动态创建 建立父子关系
        if(PoolMgr.isOpenLayout)
        {
            //创建抽屉父对象
            rootObj = new GameObject(name);
            //和柜子父对象建立父子关系
            rootObj.transform.SetParent(root.transform);
        }

        //创建抽屉时 外部肯定是会动态创建一个对象的
        //我们应该将其记录到 使用中的对象容器中
        PushUsedList(usedObj);

        PoolObj poolObj = usedObj.GetComponent<PoolObj>();
        if (poolObj == null)
        {
            Debug.LogError("请为使用缓存池功能的预设体对象挂载PoolObj脚本 用于设置数量上限");
            return;
        }
        //记录上限数量值
        maxNum = poolObj.maxNum;
    }

    /// <summary>
    /// 从抽屉中弹出数据对象
    /// </summary>
    /// <returns>想要的对象数据</returns>
    public GameObject Pop()
    {
        //取出对象
        GameObject obj;

        if (Count > 0)
        {
            //从没有的容器当中取出使用
            obj = dataStack.Pop();
            //现在要使用了 应该要用使用中的容器记录它
            usedList.Add(obj);
        }
        else
        {
            //取0索引的对象 代表的就是使用时间最长的对象
            obj = usedList[0];
            //并且把它从使用着的对象中移除
            usedList.RemoveAt(0);
            //由于它还要拿出去用,所以我们应该把它又记录到 使用中的容器中去 
            //并且添加到尾部 表示 比较新的开始
            usedList.Add(obj);
        }

        //激活对象
        obj.SetActive(true);
        //断开父子关系
        if (PoolMgr.isOpenLayout)
            obj.transform.SetParent(null);

        return obj;
    }

    /// <summary>
    /// 将物体放入到抽屉对象中
    /// </summary>
    /// <param name="obj"></param>
    public void Push(GameObject obj)
    {
        //失活放入抽屉的对象
        obj.SetActive(false);
        //放入对应抽屉的根物体中 建立父子关系
        if (PoolMgr.isOpenLayout)
            obj.transform.SetParent(rootObj.transform);
        //通过栈记录对应的对象数据
        dataStack.Push(obj);
        //这个对象已经不再使用了 应该把它从记录容器中移除
        usedList.Remove(obj);
    }


    /// <summary>
    /// 将对象压入到使用中的容器中记录
    /// </summary>
    /// <param name="obj"></param>
    public void PushUsedList(GameObject obj)
    {
        usedList.Add(obj);
    }
}

/// <summary>
/// 缓存池(对象池)模块 管理器
/// </summary>
public class PoolMgr : BaseManager<PoolMgr>
{
    //柜子容器当中有抽屉的体现
    //值 其实代表的就是一个 抽屉对象
    private Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();

    //池子根对象
    private GameObject poolObj;

    //是否开启布局功能
    public static bool isOpenLayout = false;

    private PoolMgr() { }

    /// <summary>
    /// 拿东西的方法
    /// </summary>
    /// <param name="name">抽屉容器的名字</param>
    /// <returns>从缓存池中取出的对象</returns>
    public GameObject GetObj(string name)
    {
        //如果根物体为空 就创建
        if (poolObj == null && isOpenLayout)
            poolObj = new GameObject("Pool");

        GameObject obj;

        #region 加入了数量上限后的逻辑判断
        if(!poolDic.ContainsKey(name) ||
            (poolDic[name].Count == 0 && poolDic[name].NeedCreate))
        {
            //动态创建对象
            //没有的时候 通过资源加载 去实例化出一个GameObject
            obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
            //避免实例化出来的对象 默认会在名字后面加一个(Clone)
            //我们重命名过后 方便往里面放
            obj.name = name;

            //创建抽屉
            if(!poolDic.ContainsKey(name))
                poolDic.Add(name, new PoolData(poolObj, name, obj));
            else//实例化出来的对象 需要记录到使用中的对象容器中
                poolDic[name].PushUsedList(obj);
        }
        //当抽屉中有对象 或者 使用中的对象超上限了 直接去取出来用
        else
        {
            obj = poolDic[name].Pop();
        }

        #endregion
        return obj;
    }


    /// <summary>
    /// 往缓存池中放入对象
    /// </summary>
    /// <param name="name">抽屉(对象)的名字</param>
    /// <param name="obj">希望放入的对象</param>
    public void PushObj(GameObject obj)
    {
        poolDic[obj.name].Push(obj);
    }

    /// <summary>
    /// 用于清除整个柜子当中的数据 
    /// 使用场景 主要是 切场景时
    /// </summary>
    public void ClearPool()
    {
        poolDic.Clear();
        poolObj = null;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值