1. AssetBundle原理
AssetBundle是Unity中的一种资源包,这种资源包可以是游戏内要用到的几乎所有资源,例如:模型、纹理、预设、场景等大部分文件更新,并且可以在运行时动态加载。
值得注意的是AssetBundle是可以多个文件一起打包,例如将要打包的资源做成预设后再进行打包,这样可以将预设上面附带的组件、纹理、子对象等一并打包,加载后直接实例化就能直接使用了(并不是建议大家这么做,因为涉及的纹理材质可能会造成资源冗余)。
另外AssetBundle 可以选择压缩后再进行网络传输,提升传输效率,减少包体大小等,不同的压缩方式,我们放后面对比一下优劣。
那么使用AssetBundle可以解决的问题是:
1.1 实现资源的热更。
1.2 减少初始包体大小,游戏可以做分包处理。
1.3 可以利用二进制文件做资源加密。
2. AssetBundle压缩
以下我仅作 lzma和lz4的压缩测试结果进行总结,未压缩和自定义在此暂不作比较。
2.1 文件大小和下载速度。lzma的压缩大小比lz4小,所以下载的速度lzma也比lz4的速度要快。
2.2 加载开销。运行时加载开销lz4要优于lzma不少。
2.3 加载时内存占用。在使用LoadFromFile(Async)加载lzma格式的AB包时,内存的占用会是资源文件内存的大约2倍,而使用lz4则是跟资源文件内存大小一致的。
总结是lz4无论是解压时间、加载开销和内存占用都优于lzma,但唯一的缺点就是包体会大不少(约25%)左右。所以如果想要包体小,且加载速度又快,我们可以使用GZip把资源在打包完之后再打个压缩包,这样的话包体也会小很多。
3. AssetBundle加载
我们从几种AssetBundle本地加载进行比较 new www,AssetBundle.LoadFromFile对其优劣进行分析比较。
3.1 new www 其优点是即可网络加载又可加载本地,而其缺点是这种方式加载资源,所占的内存要比LoadFromFiles高,相比LoadFromFile加载速度也会要慢一些。
3.2 AssetBundle.LoadFromFiles是目前版本最推荐的加载本地AssetBundle的方式,从性能上目前是最好的,内存占用相比new www也要小。(本地加载www没有可比性)
综上所述,推荐大家使用www来下载由多个AB包压缩生成的压缩包,下载然后解压到本地后再通过LoadFromFile来加载,这样的做法又快又高效,资源还小。
4. AssetBundle卸载
AssetBundle在使用过程中很容易产生卸载不干净,继而导致资源的冗余,强行卸载又可能导致资源丢失引用。如何卸载AssetBundle是值得深入研究的一件事。
4.1 AssetBundle.Unload(false)。 参数为false时,AssetBundle内的序列化数据会被释放,实例化的物体还都保持完好。简单的说就是断开了AssetBundle和实例之间的联系。值得注意的是这样做并不会卸载AssetBundle,意思是AssetBundle还会继续占用内存。然而如果再次实例化对象,也不会返回以前初例化过的AssetBundle,而是重新实例化一个新的AssetBundle,那么这样就出现了冗余,同样的资源,内存中会出现多份。所以适合这样来卸载的资源,一定是不需要多次创建实例的资源,例如:一次性使用的配置信息。而卸载的话要当创建的实例删除回收以后,手动调用Resources.UnloadUnusedAssets或切换场景来释放。
4.2 AssetBundle.Unload(true)。参数为true时,就简单多了,卸载AssetBundle,并且删除被引用的资源。注意!如果AssetBundle中有资源在场景中被引用,则会出现资源丢失的情况。这种卸载方式,最为彻底,完全从内存移除,缺点是你需要一套机制,来关注是不是还有资源引用,会不会引起异常。
4.3 关于卸载资源中,必须要跟大家说明的是,加载的AssetBundle,可以通过Resources.UnloadUnusedAssets来释放。但是前提是加载的AssetBundle一定要先释放干净,即没有任何引用。在调用AssetBundle.Unload(true)后会强行删除创建的实例和引用;而AssetBundle.Unload(false),必须先手动删除资源的实例和引用才能释放。另外Resources.UnloadUnusedAssets的开销其实并不小(因为不仅仅是释放AssetBundle还包括Resources.Load的其他资源),建议每隔一段时间进行调用比较合适。
那么给大家总结下使用卸载方案吧。
AssetBundle.Unload(false)适用于一次性使用的资源,获得资源引用后直接调用,当删除引用后,下次调用Resources.UnloadUnusedAssets后就删除了。AssetBundle.Unload(true)在使用中,最好的做法是给创建出来的实例都添加计数,当计数不为0时,表示场景或代码中仍有引用,而当计数为0时,表示没有引用了,这样就可以放心大胆的AssetBundle.Unload(true)了。
5. AssetBundle依赖打包注意事项
依赖打包最需要注意的也是唯一要注意的就是要避免依赖的处理不当,而导致资源冗余。
5.1 对于引用的资源,必须明确出现在其他AssetBundle的资源列表中,Unity才能识别为依赖关系,形成依赖打包。
5.2 合理规划AssetBundle包,公共的资源单独抽离打包。
5.3 注意资源与AssetBundle的关系,一个AssetBundle可以包含多个资源,但同一份资源不允许存在于多个AssetBundle中。
6. AssetBundle打包策略和建议
在打包之前首先要按照资源类型和逻辑进行分组,接下来我们讨论下分组,当然这里只是讨论如果要分组的情况,我们也可根据项目实际情况。
因为每个项目在AssetBundle分组情况有多种选择,有些项目甚至选择的是全颗粒化增量更新的模式。因此该小节暂不作建议和说明。
7. AssetBundles浏览与检测
要检测资源是否冗余,推荐使用AssetBundles-Browser、UnityStudio、disunity、UWA GOT等工具进行检测。
8. AssetBundles 好用的开源库
嗯,以下排名不分先后,都是很好用的开源库,不过一些特殊功能,例如:加密、AssetBundle差异合并、打包策略等,还需要根据具体的项目加以实现。
以上开源库均可在github上搜到。