前言
本文主要是记录资源框架学习的笔记,内容较多,可能周期会较长,因而可能会开个专题,又或者在此文章慢慢更新,大家可以在状态中观看文章状态,建议在完结后再进行观看,因为还在学习中,会不断完善笔记,纠正错误,进行扩充。
状态
完结撒花🎉
什么是Unity资源?
在Unity的Assets目录下,我们在游戏运行过程中可以通过目录加载并显示到屏幕上的东西都可以称之为Unity资源,例如Prefab,纹理,材质,声音,文本,场景等等。
简单理解,就是只要可以通过AssetDatabase加载,并且是合法的类型,就可以称之为Unity资源
唯一要注意的是,代码热更和资源热更是两件事
什么是AB包?
在实际的开发中,我们会遇到需要对资源进行上传下载,对资源进行加载到内存和从内存中卸载的需要。
因而,就有了AssetBundle(AB包),AssetBundle就是Unity官方支持的一种用于存储资源的压缩格式打包后的集合,它可以存储任意一种Unity可以识别的资源类型。
通过AB包,我们就可以将项目要使用的资源进行加载和卸载,打包压缩成集合进行上传下载。
AB包实现了游戏资源的热更新,减少游戏安装包大小,优化资源加载,有着诸多好处,因而也就有了我们要去学习的理由。
Unity加载资源常见方法与比较
通过上图,我们就可以看到AB包的优越,集灵活,功能强大于一身,但同时AB包也有一些缺点需要我们注意,比如处理依赖关系。
打AB包的步骤
- 1. 给资源标记要打入的AB包名
- 2.使用Unity的API将标记了的资源打包输出
UnityAPI: BuildPipeline.BuildAssetBundles()
这样我们就能够简单的打出AB包了,当然,仅仅是这样是远远不够的,作为程序员,我们需要构建的是一个工作流,因而我们将会把使用AB包的整个流程自动化。
核心分为以下四个内容:
- 打包
- 下载
- 解压
- 加载
打包
可视化界面
这个其实是编辑器的知识,同学们观看老师的课即可,我个人到时也会将编辑器相关的知识放到一篇文章或者专题中,限于篇幅和主题与本文不符,就不在这里赘述了
注意事项-引用问题
在打包过程中,会出现这样的问题。
我们需要使用到A包或者B包中的资源E,而这个资源E可能又引用于C包的资源F
比如资源E是一个Prefab,其引用了一个材质资源F
那么此时,我们如果要使用A包或B包的时候,还要先加载C包到内存中。
注意事项-冗余问题
仍旧用上图去理解,假如在打A包和B包的时候,我们不考虑冗余问题,这样就会导致C包同时打入了A包和B包中,相当于产生了多份重复的资源,这样的后果就是增加了资源的包体,造成了内存上的浪费,因而我们一般的解决方案就是将C包做成公共包,通过获取A包和B包对其的依赖进行加载,且在加载的时候检查是否已经加载过了。
注意事项-引用计数
引用计数是什么呢?还用上图理解,在游戏运行的过程中,我们可能加载了A包和B包,此时C包相当于是被两个包同时引用着,这个时候,如果我们卸载A包,那么不使用引用计数的方法,我们就不能得知此时C包是否还被引用着,就无法判断是否对C包进行卸载,如果此时卸载C包,那么此时B包中使用了C包的资源就可能会丢失掉对C包中资源的引用。
就可能出现比如我们常见的材质丢失的情况,如下图
因而我们在C包加载时,对其添加一个引用计数,比如A包加载C包,此时C包的引用计数变为1,B包也加载C包,此时C包的引用计数变为2,卸载A包,引用计数变为1,卸载B包,引用计数变为0,通过引用计数,我们可以得知C包不再被任何包所引用,此时我们就可以安心地对其进行卸载了。
打包策略
根据上图,我们可以得知在右边的prefab对左边的Texture的引用,因而就有了我们常用的打包策略
如上图,从左到右分别是Texture,Effect,Prefab,而依赖关系则是从右往左的(右侧资源依赖于左侧)
思路就是从依赖关系弱到依赖关系强的依次进行打包。(也就是先打Texure,再打Effect,最后打Prefab)
打包流程基本思路
框架中已经写好了三种打包方式,但为了方便学习和理解,我这里会写讨论如果只打一个文件夹作为AB包的过程是怎样的,只要理解了一个文件夹如何打成AB包,其实其他的方式无非是在这个基础上拓展而已。
首先我们创建一个模块Test
打包整体思路如下:
1.获取要打成AB包的文件夹路径,比方说我的是
D:\MyUnityLearn\LearnGame\LearnDemo\MRFZDemo\Assets\GameData\Test\Amiya2Textures
在打包器面板中,我们这样写,如下图所示
我们自定义包名为ATest,并且记录了文件夹的路径为Assets/GameData/Test/Amiya2Textures,这样我们就可以在打包过程中获取到这个路径如下图所示
最终打出的AB包名是模块名+ _ + 自定义包名,所以是test_atest(打包过程中处理为小写)如下图所示
2.记录所有要打的AB包的文件列表
通过第一步,我们获取到文件夹的路径,而后我们对这些路径进行记录
我们用一个List去存取本次打包的所有文件列表,如下图 所示
为了防止重复打文件,我们在添加文件到List之前,需要先检查List是否已经保存过该文件了,如下图所示
我们用一个字典去存取某个AB包中所有的文件列表,如下图所示
在文件添加到List之后,我们就将其打入相应的AB包字典中,如下图所示
3.修改打包的所有资源的AB包名
有了前面的铺垫,我们修改打包所有资源的AB包名的思路就很简单了
在第二步,我们得到了一个键为AB包名,值为文件路径的字典
那么我们只要遍历这个字典,将文件的AB包名修改为字典的键就可以了,如下图所示
4.生成打包的所有资源的配置
1.构建字典
通过前面的步骤修改AB包的名字后,我们通过AssetDataBase,可以构建出一个字典(字典创建步骤如上图)
这个字典将记录文件路径与AB包的对应关系
2.生成配置对象
遍历这个字典,键就是文件路径,通过去掉.meta后缀的文件,我们就可以将所有的文件进行遍历,并创建其对应的配置对象BundleInfo,记录其所需信息
3.依赖关系的解决(重点!)
用相关API,我们可以获得一个字符串数组depence,这个数组的值是当前文件所依赖的所有文件的路径
通过遍历这个数组,我们将依赖的所有文件的AB包获得,并且不重复地添加到bundleDependce这个列表中,这样后面资源加载的时候,就可以通过遍历这个列表,得知该文件加载需要依赖的AB包
注意上面的获取依赖关系的api,默认是递归获取所有的依赖关系,下图所示
总结
整个打包的步骤就是:
1.获取打成AB包的文件夹
2.记录所有要打的AB包的文件列表
3.修改打包的所有资源的AB包名
4.生成打包的所有资源的配置文件 (有三个子步骤 (1)构建文件路径与AB包的字典 (2)生成配置对象(3)解决依赖关系)
通过上面打一个文件夹的步骤,相信你就大概了解AB包从编辑到打出的过程,其他方法无外乎思考如何获取更多想要打的文件列表以及如何分类。
举个例子来说,比如我想把某个文件夹下的预制体打成一个AB包,那么其实只要想办法把预制体的文件路径收集起来即可,如下图
同理,你还可以有不同的搜索和分类方式,这个就看个人的策略了
(补充) 什么是模块?
在这个资源框架下,模块就是代表一个功能模块,一个模块将包含很多的贴图,特效,预制体等资源,这些资源会打包成AB包,然后放在模块命名的文件夹下,例如
路径中的Hall代表这是Hall模块
路径中的Android代表这是用于Android平台的
最后隶属于Hall模块的这些AB包,它们的命名规则是模块名_包名(比如上图中的hall_hallmainui)
同时,我们可以看到模块中,有一个命名为:模块名+bundleconfig的文件(比如上图中的hallbundleconfig)
这个文件就是该模块的配置文件,它将记录所有AB包的信息,在下载的时候,它将会被第一个下载。
具体模块应该如何设计,还是根据自己项目的功能去规划
反正基本的思路就是对于希望资源上可以热更,同时有互相独立的功能,就可以拎出来做一个模块。