XAsset官网
当前版本:XAsset6.0首发版
目录说明
入门教程
现在让我从零开始配置资源更新流程
打包配置
-
创建空项目,然后导入示例包。
-
导入后我们可以发现默认已经为我们创建了打包配置,我们需要将其删除
-
点击工具栏下的Groups,打开配置面板。
-
在窗口内右击,创建配置组。
-
创建成功后我们可以右击,进行重命名
Preload
。在配置组创建成功的同时,我们可以看到在工程目录下也生成了对应配置文件。
资源服务配置
-
下载hfs:https://www.rejetto.com/hfs/hfs.exe
-
打开hfs,将我们出包文件夹(unity项目下的Bundls文件夹,也是我们上步打包的输出目录),拖入到hfs中。
会询问你要创建什么样的资源文件夹,我们选择Real folder(真实文件夹,非虚拟目录)。
-
创建完成后我们可以看见出现了一个服务网址,复制该网址到浏览器中打开,会出现如下界面(如果没有则说明你之前某一部操作有误)
运行
接下来我们来运行Demo
-
打开启动场景Startup,选择场景中的对象,在其Inspector面板中填入我们上一步生成的资源服务器地址。
常见问题
-
运行游戏报错找不到Menu_Sense的资源包。
可能是已下载的资源出现丢失问题,点击工具栏下的View/DownloadPath ,跳转到当前本地下载的资源包目录,删除目录后的所有资源,重新加载游戏。
-
运行LoadAsset场景报错:找不到Components2_V2.prefab资源。
这里需要我们新建一个配置组,然后将工程目录下Arts/Prefabs/UIC目录拖入,在配置组的inspector面板上勾选IncludeInBuild(这一步可以使unity在打包App时将该组的资源一起打入包体中),重新生成分组,再打包(运行Build下拉菜单的Group和Bundles)。
-
非常重要一点,卸载Asset时不能使用
.Unload()
方法,而应该使用.Release()
;
功能说明
菜单栏功能说明
从上到下依次为:
- Groups:显示组配置面板
- Build:快捷打包,方便不用打开配置面板
- Groups:快捷生成组
- Bundles:快捷生成Bundles
- Player:快捷发布App
- Compute CRC:查看选中文件的冗余校验码(百度CRC)
- View:快捷跳转一些目录
- Settings:跳转到总配置文件
- Build Path:打开资源打包目录
- Download Path:打开编辑器下资源下载后保存的目录
- Temporary:打开资源临时存放目录
- Clear Progress Bar:清除进度条
- Copy Path:拷贝当前在工程面板下选中对象的地址(例如:Assets/VEngine/Data/Groups/Auto.asset)
Groups分组编辑器
工具栏
- Create:创建组,有两种模式(Bundled和Raw,两种模式对比后续讲解)
- View:同菜单栏,用于跳转目录
- Refresh:刷新
- Simulation Mode:仿真模式,可以在编辑器下且未打包的情况下运行游戏
- 当前打包包体的大小
标题栏
- Path:当前分组的路径
- Size:当前分组所占包体总大小
- Label:
单个文件的Bundle标签,相同标签的文件将打入同一个包这里和Unity自带的打包标签不同,有待后续理解 - Bundle:打包后所属Bundle的名称
- Time:
分组配置
从上到下依次是:
- Bundle Mode:Bundle划分模式
- Pack Together:分组中的除了场景外的所有资源按分组的名字一起打包,每个场景会按 分组名字 + 场景名字 进行打包。
- PackByAssetName:资源按分组名字 + 源名字打包,子目录的资源会按一级资源的名字打包。
- PackByLabel:同一个分组相同 label 的资源会打包到一起。
- Assets:所有打包的文件(文件夹)
- Include In Build:勾选该选项后,该分组的资源会在Unity发布资源时一起发布到包体中
- Size:大小
- Read Only:资源只读?
- Target:资源目标
- Label:资源标签
- Bundles:
- Dependencies:当前对象的相关依赖
- Time:时间
- Read Only:只读
- Children:包含的子对象
- Path:路径
- Root Path:根目录
- Parent Group:所属分组
- Auto:自动分组?
版本配置文件
从上到下依次是:
- Build Number:打包版本
- Assets:同上
- Budles:版本中所有的Budles
- Budles To Build:版本中所有变更的Budles
- Groups:版本中的所有分组
- Build Asset Bundle Options:资源包构建选项
- Simulation Mode:是否开启仿真模式,对应了Groups窗口中的Simulation Mode按键
- Startup Scene:启动场景,用于快捷发布App时指定启动场景
- Time Format:分组编辑器中的时间显示格式
- Size:上次打包的资源大小
- Assets:对应了单个分组的Assets设置信息
- Name:资源名
- Name With Aooend Hash:哈希名
- Size
- Crc:冗余校验码
- Time:
版本文件概要
在了解加载机制之前,先聊聊Bundle的版本控制文件manifest。他的内部构造见下图。
manifest文件在打包时会生成两个,一个在Unity发布成包的时候打入包体的StreamingAssets目录(StreamingAssets是只读的),用于取代了Unity的Resources目录功能(所以不管什么文件都不要用Resources加载),第二个在Build的时候放入服务器资源下载目录。
在框架启动时,会先用UnityWebRequest将文件manifest从包体目录(StreamingAssets)中加载出来,重命名为manifestInBuild后保存到资源下载目录中。所以manifestInBuild会一直维持为Unity发布成安装包时的版本。
(需要注意的是,在编辑器下这一理论不正确,因为编辑器下会从Build的发布路径获取manifest文件,而不是从包体目录(StreamingAssets)中,这意味,版本文件永远是最新的。但一切以发包后为主,需要的可以发布后再进行测试)
在调用Versions.UpdateAsync
方法后(版本更新Demo‘ManualUpdate和AutoUpdate都有用到),会将服务器下载目录下的manifest文件更新到下载目录下,名称不变。(不调用,是不会更新资源的)
每次新发布版本,Build后会将新版本的manifest文件自动更新到服务器下载目录中
所以,一个修改项目后正确的修改姿势应该如下:
-
点击Build下拉菜单的Bundles按钮。
-
在客户端执行
Versions.UpdateAsync
方法——运行Secen:ManualUpdate或AutoUpdate。 -
再加载资源——运行LoadAsset场景。
(如果没有调用
Versions.UpdateAsync
而直接加载资源,将导致你的资源停留再上个版本)
流程解析
初始化流程
- 场景中挂载了Startup组件,在其
Start
方法内调用了Versions.InitializeAsync()
Versions.InitializeAsync()
New了一个InitializeVersions对象,并调用了它的Start
方法,最后将InitializeVersions对象返还给第一步的Startup组件,用于监听初始化状态- 在InitializeVersions的
Start
方法中做了两件事-
创建了一个挂在有Updater组件的Unity对象,该对象主要作用是用update方法驱动后面一系列下载与加载方法的委托(包括自身的Update方法,也间接性由其驱动)
-
使用UnityWebRequest,在
Update
方法的驱动下下载版本清单文件manifestInBuild。manifest文件在打包时会生成两个,一个在Unity发布成包的时候打入包体的StreamingAssets目录(这里取代了Unity的Resources目录),第二个在Build的时候放入资源目录。 下面的manifestInBuild就是从包体目录下读取出来然后写入资源下载目录的,因为StreamingAssets是只读的,且只能用WWW加载。 另外资源下载目录下还有一个manifest文件,该文件是在版本更新的时候从资源服务器下载的。
-
- 在下载完成后会调用
OnComplete
方法,对文件内容进行解析,把资源依赖和地址内容加载到内存中。最后调用Finish
方法,这使最开始的Startup组件监听携程不再阻塞。
场景加载流程
Startup调用
- 在完成初始化后,接下来第一个实际作用演示——加载场景就来了
Scene.LoadAsync()
- 在
LoadAsync
中创建了一个Scene(构造参数传入要加载的Scene路径),并调用Scene的Load
方法(实际指向OnLoad
方法)
Scene调用
- 在Scene的
OnLoad
方法中创建Dependencies(构造方法中传入通过传入的加载路径解析到的AssetInfo信息),并调用其Load
方法(实际指向OnLoad
方法)。 - 在Update中监听Dependencies对象的加载状态是否完成,完成后调用
SceneManager.UnloadSceneAsync
加载场景。
Dependencies调用
-
通过AssetInfo创建一个BundleInfo,再调用
Bundle.LoadInternal
方法加载Bundle并返回一个Bundle对象,将Bundle添加到Bundle列表中,由OnUpdate
方法维护在这一步中会调用
Versions.GetBundlePathOrURL
来获取地址,GetBundlePathOrURL
会先对本地资源和最新资源文件记录的版本进行对比(失败返回服务器下载地址),然后从包体目录中查找(成功返回StreamingAssets地址),最后从本地已下载资源目录查找该文件,如果文件不存在,就返回服务器下载地址(带http)。该Bundle对象是它的子类,根据返回地址是否带
Http
而选择的加载模式不同,所选子类也不同,在该按理加载过程中为DownloadBundle(不带Http
即为本地加载LocalBundle)。创建后调用其
Load
方法(实际指向OnLoad
方法) -
重复上一步加载AssetInfo中的对应Bundle和所有引用Bundle
-
在
OnUpdate
中,检查Bundle列表中所有Bundle是否都加载完毕,如果是则改变自身加载状态,为加载完毕。
DownloadBundle调用
- 调用
Download.DownloadAsync
方法开始下载文件返回Download对象,并添加下载完成调用的回调方法OnDownloaded
OnDownloaded
方法中将下载对象进行解析,构建成AB包,并设置Versions中的资源路径- 在
Update
方法中,监听Download对象的下载进度
Download静态调用
- 创建DownloadInfo对象,并调用
DownloadAsync
方法下载文件 DownloadAsync
创建下载对象Download,放入下载队列中,通过Update
方法维护。- 在
UpdateDownloads
方法中调用下载对象Download的Start
方法下载文件,并监听下载进度统计下载带宽。
Download调用
- 在
Start
方法中创建一个新线程,调用Run
方法 - 在Run方法中先调用
Downloading
方法下载文件,然后调用CheckStatus
方法校验下载文件正确性 Downloading
方法中使用HttpWebResponse创建下载请求下载数据流,写入到文件中
LoadAsset
异步加载
-
在LoadAsset的
Start
方法中调用Asset.LoadAsync
方法,传入加载路径和加载对象类型异步加载资源底层支持从服务器下载最新资源
-
在
LoadAsync
方法中调用LoadInternal
方法,指定非同步加载 -
在
LoadInternal
方法中先根据路径从缓存中查找Asset资源添加到队列中,如果没有找到则创建BundledAsset,并调用其Load
方法(实际指向OnLoad
方法) -
在
Update
方法中自动监听Asset对象是否被引用,如果没有引用则调用对象的Unload
方法
BundledAsset调用
-
在
OnLoad
方法中创建Dependencies对象,并调用其Load
方法(实际指向OnLoad
方法)。后续跳转场景加载流程
中的Dependencies调用 -
在
Update
方法中监听Dependencies对象的加载依赖是否完成,如果完成则开始异步加载AB包请求。在AB包加载请求完成后设置设置Asset为自身Asset
同步加载
-
在LoadAsset的
Start
方法中调用Asset.Load
方法,传入加载路径和加载对象类型同步加载不支持从服务器直接下载资源
-
在
Load
方法中调用LoadInternal
方法,指定同步加载 -
在
LoadInternal
方法中先根据路径从缓存中查找Asset资源添加到队列中,如果没有找到则创建BundledAsset,并调用其Load
方法(实际指向OnLoad
方法)。而后调用BundledAsset的
LoadImmediate
方法
BundledAsset调用
-
在
OnLoad
方法中创建Dependencies对象,并调用其Load
方法(实际指向OnLoad
方法)。后续跳转场景加载流程
中的Dependencies调用 -
在
Update
方法中监听Dependencies对象的加载依赖是否完成,如果完成则开始异步加载AB包请求。在AB包加载请求完成后设置设置Asset为自身Asset
-
LoadImmediate
直接加载方法会检测Dependencies方法是否加载完毕,如果没有加载完成则调用Dependencies的LoadImmediate
方法
Dependencies调用
OnLoad
调用和异步加载相同。LoadImmediate
方法会调用所有由OnLoad
创建的Bundle(LocalBundle)对象的LoadImmediate
方法
LocalBundle调用
- OnLoad方法会直接调用
AssetBundle.LoadFromFileAsync
方法加载本地AB包,将返回的请求保存到本地 - LoadImmediate方法会将请求中的Asset保存到本地,并调用加载结束方法。
异步实例化
优势:大量对象并行异步加载的处理,可以更平滑
- 在LoadAsset的
Start
方法中调用InstantiateObject.InstantiateAsync
方法,传入加载路径。 - 在
InstantiateAsync
方法中调用Start方法 - 接下来在
Start
方法中调用了Asset.LoadAsync
(内部调用可直接跳转异步加载) - 最后在
Update
方法中监听是否加载完毕,加载完毕后调用Object.Instantiate
实例化对象
ManualUpdate
-
在ManualUpdate的
Start
方法中调用Versions.UpdateAsync
方法更新版本在
UpdateAsync
方法中创建了UpdateVersions对象,并调用其Start
方法 -
接下载会调用
Versions.CheckAsync
方法,对资源进行版本检验和更新,返回CheckVersions对象在
CheckAsync
方法中,会创建CheckVersions对象并调用其Start
方法 -
检查上一步CheckVersions对象中的下载队列是否存在文件,如果存在调用
Versions.DownloadAsync
方法对文件进行下载
UpdateVersions调用
- 在UpdateVersions的
Start
中会先对version文件进行下载,然后下载manifest文件
CheckVersions调用
- 在
Start
方法中会调用Versions.Manifest.GetBundlesWithGroups
方法对所需要检查的分组名进行解析,并获取对应的所有BundleInfo存入列表 - 在
Update
方法中,会对列表中Bundle版本号和最新资源文件中的版本号进行对比,提取需要更新的资源并创建DownloadInfo存入队列中。
AutoUpdate
- 在AutoUpdate的Start方法中调用
Versions.UpdateAsync
方法更新版本,与ManualUpdate中第一步相同 - 调用
LoadAsync
加载资源,并在完成回调中实例化资源对象。与ManualUpdate不同,这里不再调用CheckVersions来对所有资源进行校验和更新。在加载过程中直接进行了版本对比和覆盖更新。