Unity5的Bundle开发和优化
https://gameinstitute.qq.com/community/detail/128107
一、关于Bundle加载速度图示:
二、设计方案:
有两种方案可供选择:
第一种是用完全bundle的管理方式,以每个文件夹作为独立的bundle,项目管理过程中需要注意分包处理、在加载Object和场景之前先加载其相关依赖的bundle,场景切换前卸载上个场景不需要的bundle。然后在加载和卸载的过程自己去给每个文件单独计算引用计数。最后提供当某些资源不用之后,可以卸载其缓存资源和bundle。特点是原生的bundle加载方式便于理解和管理,但是引用计数(不一定和Unity的计数完全一致)存在隐患。
第二种方式,是用自定义文件格式去处理,打包bundle的时候,把每一个资源单独打包成bundle,然后再根据自定义把所有bundle打成一个或多个Page包,然后通过id length offset去读取每个bundle文件的内容。在加载的时候可以用多线程去读取资源,然后在主线程创建bundle和资源。特点:复杂度稍微高一点,bundle分的很散便于引用计数的计算和内存管理,但同时过散的资源也可能导致速度问题。另外后台多线程会提高文件的读取速度,但同时增加缓存读取和内存创建bundle 可能带来额外的内存开销。
三、其他:
LZMA:Unity打包成AssetBundle时的默认格式。优点是打包后体积小,缺点是解包时间长导致加载时间长。
LZ4:相较LZMA会生成更大的压缩文件,但优点是使用时不需要整体解压
BuildAssetBundleOptions.None|默认LZMA方式压缩
BuildAssetBundleOptions.UncompressedAssetBundle |不压缩资源,最后可以使用自己的压缩方式
BuildAssetBundleOptions.CollectDependencies | Unity5.3不用了
BuildAssetBundleOptions.CompleteAssets |Unity5.3不用了
BuildAssetBundleOptions.DisableWriteTypeTree | 不能无视TypeTree的改变,如果TypeTree发生了改变
BuildAssetBundleOptions.DeterministicAssetBundle |编译资源包使用一个哈希表储存对象ID在资源包中。
BuildAssetBundleOptions.ForceRebuildAssetBundle | 强制重现生成bundle,即使bundle没有改变。
BuildAssetBundleOptions.IgnoreTypeTreeChanges | 可以无视TypeTree的改变,即使TypeTree发生了改变
BuildAssetBundleOptions.AppendHashToAssetBundleName |把Hash导在bundle的名字中
BuildAssetBundleOptions.ChunkBasedCompression |用Lz4的方式进行压缩
BuildAssetBundleOptions.StrictMode |严格模式,有任何错误产生 马上终止打包。
BuildAssetBundles的AssetBundleBuild[]参数和面板上的AssetImporter参数差不多 , 都是提前设好bundle名字和Variant名字。
内存来说loadbundle比loadAsset远小
设置相同assetBundleName的打包到同一个bundle资源里边。设置相同的assetBundleName和assetBundleVariant打包到同一个assetBundleName.assetBundleVariant资源里边。
设置相同assetBundleName和不同assetBundleVariant的 会被认为是同一个bundle 不能重复加载(只能先卸载) 他们之间不能有依赖关系,但是bundle文件分开是多个。常用来做不同精度的模型或者贴图分开显示。
依赖关系只跟assetBundleName有关。可以只使用assetBundleName进行bundle分包,只有把prefab material texture shader等全部分开打成bundle才能不会资源重复,否则一个bundle会将所有依赖的资源打在一个包里。
注意IOS文件限制和每个bundle包的大小,不能太大。
加载策略:
采用多种策略加载:慢速加载:纯异步:load bundle和loadAsset都异步,Resource异步加载
快速加载:半异步:load bundle同步和loadAsset异步,Resource同步步加载
正常情况,快速加载比较快,但是大资源Resource内文件异步比同步速度快,小资源相反。(说明异步加载确实是多线程了)
缓存策略:
bundle模式有三种文件缓存,分别是:
bundle文件:可以理解为文件系统的原始二进制数据,加载速度很快内存消耗比较小。缓存在bundleManger
Asset文件:经过二进制文件创建出来的资源文件,包括声音图形mesh等缓存文件,加载速度根据创建对应资源的类型不同会慢一点。缓存在ResourceMgr
场景文件:实例化的场景文件及其引用,缓存在游戏中
卸载特点:
1.bundle的加载几乎没什么消耗只是部分依赖数据的加载。所以内存优化重点是ResourceDB
2.bundle.unload(false)只卸载bundle会导致缓存资源冗余,bundle.unload(true)会完全卸载 但是如果场景中还有引用,资源会丢失。
3.GetAllDependencies 会把该bundle内全部资源的全部依赖的bundle加载进来,如果bundle划分不是很细的话会加载冗余数据,依赖关系最好自己维护。
4.bunlde中缓存资源一旦加载,若其依赖的资源被卸载后再加载 该资源依赖丢失不可用,若只是本身bundle卸载再创建,可用重复加载资源,因为每个资源的依赖资源是存在本身身上的。
5.Unity正常切换场景会把下个场景没有引用的资源卸载掉,引用包括bundle引用,ResourceDB引用,场景不卸载的OBJ引用。
6.我们的逻辑切换场景的时候会把下个场景不用的所有资源和bundle卸载掉,但是对于下个场景要用的bundle但是其中的某个资源已经不用了,无法卸载(就算场景和ResourceDB引用置空也无法卸载),这种时候建议分成不同bundle。
卸载策略:
1.场景文件加载后其和依赖的bundle不能卸载,否则容易内存冗余和出错。直到再次切换场景把上一个场景中的所有存在bundle根据条件给卸载掉。
2.对于全局存在不被依赖的Prefab,可以加载后存在场景中设置DontDestroy,然后把ResourceDB置空,把该prefab的bundle.unload(true)卸载该prefab。这样内存最优,但是不能卸载其依赖的bundle,然后要保证其他地方不会再load该资源。
3.主要优化是卸载ResourceMgr的数据 ?而不是bundle,bundle的内存占用很小。采样unload(true)策略。
4.场景加载后的场景bundle和其依赖bundle一定不能unload(false),单纯卸载bundle的意义不大,最好是直接操作unload(true),但是要管理好资源,容易出错。
5.相同多的Object,放在场景里加载比单独加载Prefab快。
6. Resources.UnloadAsset(mat);这个只能卸载Texture等资源,prefab等实例资源 只能Destroy(obj),缓存Obj资源无法手动卸载,只能把引用置空切换场景和bundle.unload(true)卸载掉,然后Unity就会自动释放。
7.建立一张单个资源的依赖和被依赖资源
8.要么unload(true),要么所有bundle都不要卸载。想卸载资源就要解决依赖问题。unload(false)比不卸载更可怕。
优化策略:
1.对于特定资源比如主角职业相关的技能技能特效装备等,在整个游戏过程中会反复加载的,那么在高内存机器上设置不卸载,bundle和ResourceDB都不卸载。预加载并且不卸载。
2.对于全局UI和全局主角,加载后,把其Object的bundle和ResourceDB给卸载掉,然后GameObjec设置DontDestroy永远存在,其依赖的资源不能处理。
3.设置bundle的不卸载会导致该bundle所有资源和所有依赖的资源都不卸载,如果把其依赖给卸载了,就算再加载 该bundle的依赖也是丢失的,只能把该bundle再卸载了重新加载。?
4.不卸载的优化只能从美术规范上去规避,只能单独的少量资源设置不卸载。要保证bundle内全部资源都是可以不卸载的。
5.设计通用UI管理器,跟EffectPool方式一样去管理。
依赖测试:
1.prefab依赖测试:
2.场景和prefab的依赖
……So……bundle的增量打包一定要定期清理bundle,全部删除重新打。
3.场景和普通资源的依赖
结论就是:不能采用prefab依赖的方式打包,一定要采用细分原则,把所有用到的资源都标记独立打包。
参考:我们以前游戏是使用美元符号$来标记,该资源或者该文件夹内资源独立打包成一个bundle。另外要定期清理所有bundle。
来自:https://blog.csdn.net/gy373499700/article/details/79508842