上一章:【Unity3D技术文档翻译】第1.7篇 AssetBundles 补丁更新
本章原文所在章节:【Unity Manual】→【Working in Unity】→【Advanced Development】→【AssetBundles】→【Troubleshooting】
AssetBundles 问题及解决方法
本章节涉及一些使用 AssetBundles 的项目的常见问题。
资源重复(Asset Duplication)
从 Unity5 开始的 AssetBundle 系统会在 Object 被打包进 AssetBundle 的时候,查找所有它的依赖。这是基于资源数据库(Asset Database)实现的。依赖信息用于确定将要被包含进 AssetBundle 的 Objects 集合。
被明确指派了 AssetBundle 的 Objects 将只被打包进该 AssetBundle。一个 Object “被明确指派”的意思是:Object 的 assetBundleName 不为空。
如果 Object 没有被明确指派给一个 AssetBundle,那么该 Object 将会被包含进所有引用了该 Object 的 AssetBundles 中,无论 AssetBundle 中有一个还是多个 Objects 引用了它。
如果有两个不同的 Objects 被分别指派给两个不同的 AssetBundles,并且这两个 Objects 都对另一个 Object 有引用,那么该 Object 将被拷贝进两个 AssetBundles。重复的依赖同样会被实例化,这意味着被依赖的 Object 的两份拷贝会被认为是两个不同的 Objects,且拥有各自的 id。这将增加应用 AssetBundles 的大小。如果应用加载了这两个 AssetBundles,那么就会导致内存中加载了两份 Object 的拷贝。
这里有些方法可以解决这样的问题:
- 确保被打包进不同 AssetBundles 的 Objects 没有共同的依赖。把任何有共同依赖的 Objects 打包进同一个 AssetBundle ,避免重复依赖。
- 这个方法通常不适用于那些有很多共同依赖的项目。并且这个方法会导致作为整体的 AssetBundles 被频繁地重新构建和重新下载,造成不方便和不高效。
- 分割 AssetBundles,使得拥有共同依赖的 AssetBundles 不会同时被加载。
- 这个方法对于特定类型的项目可能有效,比如以关卡为基础的游戏。然而,这会增加项目的 AssetBundles 大小,并且增加构建的次数和加载的次数。
- 确保所有被依赖的资源被打包进它们自己的 AssetBundles。这就完全排除了重复资源的风险,但是同样的这会使情况变的复杂。应用必须追踪 AssetBundles 间的依赖关系,并且确保在调用任何 AssetBundle.LoadAsset 方法之前,正确的 AssetBundle 已经被加载好。
在 Unity5 中是通过 UnityEditor 命名空间下的 AssetDatabase 方法来追踪 Object 的依赖。正如命名空间表明的,这个方法只能在 Unity 编辑器中使用,而不是在运行时。AssetDatabase.GetDependencies 方法可被用于定位一个 Object 或者资源的所有直接依赖。注意,这些依赖可能各自也有依赖。此外,AssetImporter 方法可用于查询某个 AssetBundle 中包含的 Object。
组合使用 AssetDatabase 和 AssetImporter 的方法,就可以编写编辑器脚本,确保一个 AssetBundle 所有的直接或者间接依赖都被打包进一个或者各自的 AssetBundle 中;或者任意两个有共同依赖的 AssetBundles,被打包进同一个 AssetBundle。考虑到重复资源造成的内存开销,建议所有项目都有一个这样的脚本。
Sprite 图集重复(Sprite Atlas Duplication)
当 Unity5 中资源依赖的计算代码,和自动生成 Sprite Atlas 结合使用的时候,会出一点奇异的问题,下面的部分将描述这一点。
任何自动生成的 Sprite 图集,都会和图集生成的 Sprite Objects 一起打包到 AssetBundle 中。如果 Sprite Objects 被分配给多个 AssetBundles,那么 Sprite Atlas 就不会被打包为一个 AssetBundle,并且出现资源重复;如果 Sprite Objects 没有指明 AssetBundle,Sprite Atlas 同样不会被打包为一个 AssetBundle。
想要确保 Sprite Atlas 不重复,需要检查划分到同一个 Sprite Atlas 的 sprites (Tag 相同)要被指明为相同的 AssetBundle。
(Unity 5.2.2p3 会有额外的图集问题,在 Unity 5.2.2p4 已修复,建议用尽可能新的版本,这里不再赘述)
Android 纹理相关问题
由于 Android 生态系统严重的设备碎片化,经常需要将纹理压缩为不同的格式。所有 Android 设备都支持 ETC1 格式图片,然而 ETC1 格式不支持透明通道。如果应用不需要 OpenGL ES 2 的支持,解决问题最简洁的方法就是使用 ETC2 格式,该格式需要 OpenGL ES 3 的支持。
然而大多数应用都需要适配老旧设备,因此 ETC2 格式可能无法使用。解决这个问题的一个办法是使用 Unity5 的 AssetBundle 版本变量。(你还可以查看 Unity 安卓优化指南,来了解更多其他解决方案的细节)
使用 AssetBundle 版本变量,所有不使用 ETC1 格式压缩的纹理必须分配到只包含纹理(texture-only)的 AssetBundles 中。接着,为不支持 ETC2 格式的安卓生态系统创建合适的版本变量,使用特定的纹理压缩格式,比如:DXT5、PVRTC、ATITC。针对每一个版本变量,改变纹理的 TextureImporter 设置,使其与版本变量一致。
在运行时,使用 SystemInfo.SupportsTextureFormat 方法可以检测支持的纹理压缩格式。你可以使用这个信息,根据所支持的格式纹理,选择和加载相应的 AssetBundle 版本变量。
更多关于 Android 纹理压缩格式的信息,可以点击这里查看。
iOS 文件句柄溢出
这个问题在 Unity 5.3.2p2 就已经解决了。
(这个问题简言之就是:iOS 在加载一个 AssetBundle 时会产生一个文件句柄,iOS 的句柄数上限是255个,超过这个上限就会加载失败并报错。这里不再赘述。)
如果本文对你有帮助的话,点个赞或者评论一下吧!