Unity 学习笔记之一AssetBundle
记录一下近期学习AssetBundle相关的一些知识点,作为备忘。
主要分为两部分,第一部分是关于Unity官方AssetBundleManager的实现解析,第二部分是关于打包后的AssetBundle文件格式。
1. AssetBundleManager结构和代码解析
AssetBundleManager是Unity官方提供的封装AssetBundle打包和加载接口的一个插件,集成在Unity的菜单中,便于AssetBundle正式上传部署到服务器之前进行本地的测试。
图1 AssetBundleManager代码组织
如图1,它主要分为两部分,一部分是Editor菜单的实现部分,另一部分是AssetBundleManager主要逻辑实现。
1.1 Editor实现
Editor菜单有三个选项,分别用于本地AssetBundle测试的Simulation Mode,用于打包AssetBundle的Build功能以及用于本地创建的服务器进行测试的AssetBundle Server。
图2 AssetBundles菜单项
用于定义Editor以及功能实现的所有脚本都定义在AssetBundles命名空间。整个Editor部分功能的实现都是比较简单直接的。
AssetbundlesMenuItems.cs功能比较简单,主要定义Simulation Mode以及Build AssetBundles两个菜单项的响应函数。Simulation Mode与AssetBundleManager.cs中的属性SimulateAssetBundleInEditor相关;当点击Build AssetBundles菜单时,会执行BuildScript中的BuildAssetBundles函数执行打包动作。
LaunchAssetBundleServer.cs&ExecuteInternalMono.cs是用于实现运行本地AssetBundle服务器功能的。在LaunchAssetBundleServer.cs中主要是执行AssetBundleServer.exe,通过保存进程PID来检测以及终止服务器进程,这里可以了解一些关于Unity中与进程相关的操作。
BuildScript.cs实现了基本的AssetBundle打包功能,主要是调用BuildPipeline.BuildAssetBundles函数,该函数的详细描述可以参考
https://docs.unity3d.com/ScriptReference/BuildPipeline.BuildAssetBundles.html
1.2 AssetBundleManager主逻辑实现
主要包括Utility.cs、AssetBundleManager.cs、AssetBundleLoadOperation.cs三个脚本实现。
Utility.cs主要是根据当前是否处于Editor模式分别获取BuildTarget以及RuntimePlatform,由于WebPlayer在Unity 5.4版本后就不再支持,所以这里避免Warning,暂时屏蔽。
图3 获取当前所处平台
AssetBundleManager.cs功能较为繁杂,主要分为初始化、加载Asset、卸载Asset,以及Update处理函数。
从调用Initialize初始化函数开始,就会执行加载AssetBundle的操作。对于开启Simulation Mode的情况,比较简单,就不做过多描述。重点看AssetBundle从服务器下载的流程。
图4 初始化函数调用示意图
首先会获取加载AssetBundle所处的平台,比如Windows。紧接着会判断当前是否正在加载AssetBundle,如果没有,则判断需要加载的Bundle名是否已经在已加载的Bundle对象集合中,如果已经在m_LoadedAssetBundles中,则直接返回,否则从服务器进行下载。如果是加载manifest,则直接调用new WWW(url)进行下载;其他的bundle,则调用
LoadFromCacheOrDownload函数,并提供相应的版本号进行判断是否已经有缓存,如果有缓存,则不需要再从服务器重新下载减少带宽占用,并将当前的下载任务添加到
m_DownloadingWWWs集合中,该变量会在Update函数中用到。
如果已经加载过manifest文件,并且在assetbundle还没有处理完成时,会加载相应的依赖。加载依赖的过程分为三步,第一步是通过manifest文件获取所有的依赖,第二步是判断这些依赖是否有variants,第三步就通过调用前面提到过的LoadAssetBundleInternal加载依赖。
AssetBundleManager继承了MonoBehaviour,其实现了Update函数,其逻辑也比较清晰。在每一帧主要做三件事,第一是判断m_DownloadingWWWs中是否有下载完成的,如果有下载完成的,则把AssetBundle添加到m_LoadedAssetBundles;第二是从m_DownloadingWWWs中删除并释放已经下载完成的WWW对象;第三是遍历所有的加载操作对象,执行相应的Update函数。
每个Update函数的定义都各不相同,这一部分位于AssetBundleLoadOperation.cs,CodeMap如下,展示了定义的六个类的关系,其中绿色箭头表示继承,粉色箭头表示调用。
图5 AssetBundleLoadOperation CodeMap示意图
可以看出AssetBundleLoadOperation为基类,它本身继承自IEnumerator,并实现了MoveNext函数,这里的目的是用于Coroutine调用。最重要的两个核心调用分别是在类AssetBundleLoadLevelOperation的Update函数中实现了加载场景的功能,在类AssetBundleLoadAssetOperationFull的Update函数中实现了异步加载Asset的操作。
图6 加载场景以及Asset调用
1.3加载AssetBundle实例
在AssetBundle中还附带了四个使用的例子,以其中的LoadAsset.cs为例,其余三个大同小异。
在LoadAsset.cs中,实现了Start函数,创建了两个协程,分别为Initialize和
InstantiateGameObjectAsync,定义如下:
图7 Start函数定义
Initialize函数主要是调用AssetBundleManager的同名函数执行加载AssetBundle的初始化操作,由于返回的仍然是IEnumerator类型,所以可以调用StartCoroutine作为协程进行调用。这里对于Unity中的协程,后面准备再深入写一篇文章来分析。
图8 Initialize函数定义
InstantiateGameObjectAsync函数执行真正的加载Asset的操作,加载完成后,调用Instantiate函数创建prefab对象的一个实例,显示在场景中。
图9 InstantiateGameObjectAsync函数定义
2. AssetBundle存储格式
AssetBundle在Build以后,文件类似于如下的形式,
图10 AssetBundle文件目录组织
使用WinHex查看其二进制格式如下
图11 压缩的AssetBundle文件二进制格式
其文件头为UnityFS,后面的字段大概可以猜测到为类型、支持的版本、Hash128,以及压缩后的真正数据。其压缩算法有LZ4或LZMA。本来打算在没有Unity源码的情况下,逆向一下Unity解析bundle文件的过程,后来发现有一些现成的工具已经可以实现对Unity AssetBundle文件的解析,比如Unity Studio,除了解析,还可以预览某些类型的资源。
图12 Unity Studio解析AssetBundle
但是对于游戏自定义加密处理过的AssetBundle就无法解析了,好在Unity Studio是开源的,可以根据特定游戏的需要做相应的修改。
图13 Unity Studio解析代码
3. 参考文章
https://unity3d.com/cn/learn/tutorials/topics/scripting/assetbundles-and-assetbundle-manager?playlist=17117
http://www.xuanyusong.com/archives/2405
https://github.com/Perfare/UnityStudio
https://github.com/Unity-Technologies/AssetBundles-Browser
如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引