U3D客户端框架(资源管理篇)之主资源加载器模块设计实现

news2024/9/20 14:35:51

一、主资源加载器模块设计实现

作用

主资源加载器是为面向用户而设计的上层应用层类,用户使用主资源加载器作为文件加载器加载文件,
加载器可以通过Assetbundle方式加载,Http方式加载资源。


UML类图设计

在这里插入图片描述

前置模块

主资源加载器需要引用到一些基础模块和一些基础的加载器:可写区域管理器(LocalAssetsManager),资源加载器(ResourceLoaderManager),任务管理器(TaskManager),资源池(AssetPool),资源包池(AssetBundlePool),可寻址管理器(AddressableManager)如果还没有实现以上的前置模块,可以点击下方的引用连接,先实现基础部分,再来实现上层部分,就像是造楼房一样,如果没有地基,是不可能直接从2楼开始造楼房的。!!还漏了一些,文章后面就补上!!

前置模块
任务管理器(TaskManager) :https://blog.csdn.net/qq_33531923/article/details/128545543


加载资源(Asset)设计

主资源加载器可以用来加载Assetbundle中的Asset资源, 主资源加载加载Asset时候会使用对象池池技术优化资源,主加载器对资源进行了分类,以便于对资源的管理和分类.

1).会先从对象池中获取该资源是否存在池中,如果存在,认为资源存在,则会直接回调OnComplete回调,完成资源加载;
2).加载时会从打包时候记录的AssetInfo.bytes文件中获取该文件的依赖assetbundle信息,会优先加载依赖的assetbundle/asset,
然后最后加载主资源,
3).引用资源加载完成后,会将引用资源存到AssetBundlePool内,使关闭当前任务执行器关闭;
4).主资源加载完成后,会将主资源保存下来,使关闭当前任务执行器关闭;
5).然后开启任务执行器,行进轮询加载,把所有要加载的资源存放到一个任务组内,进行轮询更新处理;
6).当所有的依赖文件和主文件加载完毕后,会调用用户给定的OnComplete函数,会再次检查一次资源池中是否存在相同资源, 必须要检查,否则可能引起引用计数会出错,如果存在相同资源说明同时有两处在加载相同资源,直接使用池中资源即可;如果不存在,则注册资源数据到资源池中,以便下次取用;


二、主资源加载器(MainAssetLoaderRoutine)代码实现

    //主资源加载器
    public class MainAssetLoaderRoutine
    {
        //当前的资源信息实体
        private AssetEntity m_CurrAssetEntity;

        //当前的资源实体
        private ResourceEntity m_CurrResourceEntity;

        //主资源包
        private AssetBundle m_MainAssetBundle;

        //依赖资源包名字哈希
        private HashSet<string> m_DependsAssetBundleNames = new HashSet<string>();

        //主资源加载完毕(资源加载完毕;完整资源加载完毕)
        private BaseAction<ResourceEntity> m_OnComplete;


        //真正的加载主资源
        public void LoadMainAsset()
        {
            //1.从分类资源池(AssetPool)中 查找某个资源
            m_CurrResourceEntity = GameEntry.Pool.AssetPool[m_CurrAssetEntity.Category].Spawn(m_CurrAssetEntity.AssetFullName);

            //如果要找的资源,在资源池中
            if (m_CurrResourceEntity != null)
            {
                m_OnComplete?.Invoke(m_CurrResourceEntity);
                return;
            }

            //2.加载这个资源所依赖的资源包
            List<AssetDependsEntity> dependsAssetList = m_CurrAssetEntity.ListDependsAsset;
            if (dependsAssetList != null)
            {
                foreach (AssetDependsEntity assetDependsEntity in dependsAssetList)
                {
                    //根绝资源分类和资源完整名字,去资源加载器里查找这个资源的信息
                    AssetEntity assetEntity = GameEntry.Resource.ResLoaderManager.GetAssetEntity(assetDependsEntity.Category, assetDependsEntity.AssetFullName);

                    //查询的到就把包名记录下来
                    if (assetEntity != null)
                    {
                        m_DependsAssetBundleNames.Add(assetEntity.AssetBundleName);
                    }
                }
            }

            //3.循环 依赖的资源哈希表,加入任务组
            TaskGroup taskGroup = GameEntry.Task.CreateTaskGroup();
            foreach (string assetBundleName in m_DependsAssetBundleNames)
            {
                TaskRoutine taskRoutine = GameEntry.Task.CreateTaskRoutine();
                taskRoutine.CurrTask = () =>
                {
                    //依赖资源,只是加载资源包
                    GameEntry.Resource.ResLoaderManager.LoadAssetBundle(assetBundleName, onComplete: (AssetBundle bundle) =>
                      {
                          taskRoutine.Leave();
                      });
                };

                taskGroup.AddTask(taskRoutine);
            }

            //4.加载主资源包
            TaskRoutine taskRoutineLoadMainAssetBundle = GameEntry.Task.CreateTaskRoutine();
            taskRoutineLoadMainAssetBundle.CurrTask = () =>
              {
                  GameEntry.Resource.ResLoaderManager.LoadAssetBundle(m_CurrAssetEntity.AssetBundleName, onComplete: (AssetBundle bundle) =>
                    {
                        m_MainAssetBundle = bundle;
                        taskRoutineLoadMainAssetBundle.Leave();
                    });
              };

            //把主资源任务 最后一个加载任务组里
            taskGroup.AddTask(taskRoutineLoadMainAssetBundle);

            //依赖的assetbundle和主资源包加载完成后的任务回调
            taskGroup.OnComplete = () =>
              {
                  if (m_MainAssetBundle == null)
                  {
                      GameEntry.LogError(" MainAssetBundle not exist:" + m_CurrAssetEntity.AssetFullName);
                  }

                  GameEntry.Resource.ResLoaderManager.LoadAsset(m_CurrAssetEntity.AssetFullName, m_MainAssetBundle, onComplete: (UnityEngine.Object obj) =>
                     {
                         //再次检查,  很重要,不检查引用计数会出错
                         //再获取一次,如果又又了,可能是其他地方已经加载完注册进去了
                         m_CurrResourceEntity = GameEntry.Pool.AssetPool[m_CurrAssetEntity.Category].Spawn(m_CurrAssetEntity.AssetFullName);
                         if (m_CurrResourceEntity != null)
                         {
                             m_OnComplete?.Invoke(m_CurrResourceEntity);
                             Reset();

                             //如果这个资源在这之前可能被加载了,被注册了,则不用再new一次了,否则之前的引用计数会丢失,导致引用计数出现问题
                             //返回
                             return;
                         }

                         m_CurrResourceEntity = GameEntry.Pool.DequeueClassObject<ResourceEntity>();
                         m_CurrResourceEntity.Category = m_CurrAssetEntity.Category;
                         m_CurrResourceEntity.IsAssetBundle = false;
                         m_CurrResourceEntity.ResourceName = m_CurrAssetEntity.AssetFullName;
                         m_CurrResourceEntity.Target = obj;

                         GameEntry.Pool.AssetPool[m_CurrAssetEntity.Category].Register(m_CurrResourceEntity);
                         m_OnComplete?.Invoke(m_CurrResourceEntity);
                         Reset();
                     });
              };

            taskGroup.Run(true);
        }

        //加载主资源
        public void LoadAsset(AssetCategory assetCategory, string assetFullName, BaseAction<ResourceEntity> onComplete = null)
        {
#if DISABLE_ASSETBUNDLE && UNITY_EDITOR
            m_CurrResourceEntity = GameEntry.Pool.DequeueClassObject<ResourceEntity>();
            m_CurrResourceEntity.Category = assetCategory;
            m_CurrResourceEntity.IsAssetBundle = false;
            m_CurrResourceEntity.ResourceName = assetFullName;
            m_CurrResourceEntity.Target = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetFullName);
            onComplete?.Invoke(m_CurrResourceEntity);
#else
            m_OnComplete = onComplete;
            m_CurrAssetEntity = GameEntry.Resource.ResLoaderManager.GetAssetEntity(assetCategory,assetFullName);
            if (m_CurrAssetEntity == null)
            {
                Debug.LogError("assetFullName no exists "+assetFullName);
                return;
            }
            LoadMainAsset();
#endif
        }

        //重置
        private void Reset()
        {
            m_OnComplete = null;
            m_CurrAssetEntity = null;
            m_CurrResourceEntity = null;
            m_MainAssetBundle = null;
            m_DependsAssetBundleNames.Clear();
            GameEntry.Pool.EnqueueClassObject(this);
        }
    }

我爱写代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/139077.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt扫盲-QHash理论总结

QHash理论总结一、概述二、使用1. 添加 元素2. 获取元素3. 遍历元素4. 删除元素5. qHash()的散列函数6.算法复杂性一、概述 QHash是Qt的通用容器类之一。它和QMap一样是用来存储(键&#xff0c;值)对的工具&#xff0c;并提供快速查找与键相关联的值的功能。 QHash提供了与QMa…

密码学的一些常识01

序 作为一个小白&#xff0c;对称加密&#xff0c;非对称加密&#xff0c;数字签名&#xff0c;数字证书&#xff0c;CA&#xff0c;diff-helman&#xff0c;DES,AED,RSA……这些都不会。所以&#xff0c;百度启动&#xff0c;先初步了解。 实际应用 我是个小白……0基础的&…

sklearn中精确率、召回率及F1值得micro,macro及weighted算法

为什么要用精确率和召回率 有这样一个训练集&#xff0c;1000个人参加了结直肠癌CRC的检测&#xff0c;实际有0.5%的人得了CRC&#xff08;即5个人&#xff09;。用神经网络算法得到检测这样一个训练集能达到99%的准确率。从数值上判断该算法是不错的&#xff0c;因为只有1%的误…

springboot+disruptor再体验

Disruptor是一个高性能队列&#xff0c;常见的还有kafka、rabbitmq等&#xff0c;下面体验一下~ 1、Disruptor简介 Disruptor 是英国外汇交易公司LMAX开发的一个高性能队列&#xff0c;研发的初衷是解决内存队列的延迟问题&#xff08;在性能测试中发现竟然与I/O操作处于同样的…

[C++]STL之string的模拟实现

上一章我们对string的常见接口及使用进行了讲解&#xff0c;接下来我们将对一些常见的接口&#xff0c;包括构造函数&#xff0c;析构函数&#xff0c;运算符重载等等进行模拟实现.方便我们理解string接口实现的原理. 在讲解之前先说一下string的成员变量. 首先是字符串内容_…

微信小程序picker组件遇到的问题以及解决办法

一、picker基本概念二、遇到的问题三、如何解决四、延伸五、效果图一、picker基本概念 先来看一下官方文档中picker的基本概念&#xff1a; 从底部弹起的滚动选择器&#xff0c;现支持三种选择器&#xff0c;通过mode来区分&#xff0c;分别是普通选择器&#xff0c;时间选择器…

Bochs下载安装

文章目录下载Bochs配置BochsBochs Bochs是一个x86硬件平台的开源模拟器。它可以模拟各种硬件的配置。Bochs模拟的是整个PC平台&#xff0c;包括I/O设备、内存和BIOS。更为有趣的是&#xff0c;甚至可以不使用PC硬件来运行Bochs。事实上&#xff0c;它可以在任何编译运行Bochs的…

【Unity3D编辑器扩展】Unity3D中实现Text的字体的替换

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 在开发中会遇到要将场景中的Text的字体全部替换的情况。 所以…

NetInside网络分析帮您解决系统性能问题(一)

前言 某大学信息中心负责人表示&#xff0c;有用户反馈&#xff0c;在通过VPN访问某一IP的80端口时连接时断时续。同时信息中心给到的信息是通过VPN&#xff1a;XXX.XXX.253.5访问IP地址XXX.XXX.130.200的80端口出现访问时断时续问题。 需要通过分析系统看一下实际情况&#…

云原生周刊 | 人类、机器人与 Kubernetes

近日 Grafana 官网发表了一篇博客介绍了 2022 年比较有意思、脑洞大开的一些 Grafana 使用案例&#xff0c;比如监控特斯拉 Model 3 的充电状态、OTA 更新状况等等。 海事技术供应商 Royal IHC 利用 Grafana 展示客户船队的关键性能指标&#xff0c;例如燃料消耗、服务时间、大…

Allegro174版本新功能介绍之打开坐标超链接功能

Allegro174版本新功能介绍之打开坐标超链接功能 Allegro在升级到174的时候默认打开时,报表中的坐标是不带超链接的,如下图 直接点击坐标,是无法自动跳转到坐标所在位置的 但是Allegro174是开放了打开超链接的功能的,具体操作如下 选择Setup选择User Preferences

【 Vue3 + Vite + setup语法糖 + Pinia + VueRouter + Element Plus 第一篇】(持续更新中)

【 Vue3 Vite setup语法糖 Pinia VueRouter Element Plus 第一篇】(持续更新中) 1.使用 Vite脚手架创建 Vue3 项目 终端输入命令 npm create vite 项目名选择 Vue项目并回车根据自己的爱好&#xff0c;选择配置即可 2. 开启 Network 访问地址 npm run dev后 提示 use -…

磨金石教育||商业插画的发展现状如何?学习插画可以月入过万吗?

商业插画是什么&#xff1f;现如今&#xff0c;商业插画已经在生活中随处可见。你买的所有带包装的产品&#xff0c;上面的各种有趣的产品插图&#xff0c;就是插画师做的产品插画。特别是一些零食类的产品&#xff0c;在包装箱上&#xff0c;我们常可以看到各种大眼睛拟人化的…

电脑出现0xc00000e9错误代码的解决方法

每当假期结束回来&#xff0c;经常发现Windows系统的电脑一段时间不开机&#xff0c;开机就出现0xc00000e9的错误代码。为什么明明没有任何操作却出现错误呢&#xff1f;驱动人生带大家一文了解。 出现0xc00000e9错误代码的原因 先来了解一下电脑出现0xc00000e9错误代码的主要…

数字孪生架构

很多同学对数字孪生特别感兴趣&#xff0c;经常有同学问我&#xff1a;数据孪生系统怎么做&#xff1f;有没有教程&#xff1f;除了Unity开发&#xff0c;开发数字孪生还需要掌握什么技能&#xff1f;有人介绍了一个数字孪生的外包&#xff0c;从来没做过&#xff0c;能不能接&…

Spring 中常用的几个工具类

AnnotatedElementUtils 类 获取某个类的某个方法上是否有标注注解&#xff0c;并可以通过其他 API 获取到这个类注解上的属性值&#xff0c;该工具类其他 API 下面截图可以查看。 public static boolean isBeanAnnotated(Method method) {return AnnotatedElementUtils.hasAn…

Redis 应用问题解决

缓存穿透 key 对应的数据在数据源并不存在&#xff0c;每次针对此key的请求从缓存中获取不到&#xff0c;请求会都压到数据源&#xff0c;从而可能压垮数据源。 解决方案 一个一定不存在的缓存及查询不到的数据&#xff0c;由于缓存是不命中时被动写的&#xff0c;并且处于容…

docker 19.03构建跨平台的镜像包并推送到私有仓库

默认的docker构建image镜像是不能跨平台的,如果需要构建跨平台的镜像,需要docker的版本在19.03版本以上,并开启buildx。以下为具体的步骤 版本:docker 19.03。 一.安装/开启 buildx 1.1.手动开启dockerx开关 docker 19.3 暂默认不开启dockerx,需要手动开启 vim /etc/pro…

Scala 数据结构-集合

文章目录Scala 数据结构-集合一、集合简介1、不可变集合继承图2、可变集合继承图二、数组1、不可变数组(1) 创建数组(2) 访问数组(3) 遍历数组(4) 添加元素Scala 数据结构-集合 一、集合简介 1&#xff09;Scala的集合有三大类&#xff1a;序列seq&#xff0c;集合Set&#x…

解决fstab丢失,重启系统变为只读模式

现象描述&#xff1a; 背景&#xff1a;openEuler20.03 在/etc/fstab文件丢失、重启系统后&#xff0c;系统变为只读模式 [rootlocalhost ~]# echo 111 > 1.txt -bash: 1.txt: Read-only file system 解决方法&#xff1a; 查看系统信息&#xff0c;确认挂载信息&#…