Crunch压缩图片的AssetBundle打包

Crunch压缩图片的AssetBundle打包

本期我们聚集了这些话题:Crunch压缩图片的AssetBundle打包、MeshBaker合并Mesh之后面数翻倍、图片的多语言实现、WWW加载AssetBundle内存...


这是第133篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群:793972859(原群已满员)


资源管理

Q1:我有一个关于Crunch压缩图片的AssetBundle打包的问题,Unity官网的AssetBundle用法介绍里有一条,就是说因为使用Crunch压缩之后的图片,打成AssetBundle包再压缩,大小也基本不会有变化,还会导致打包慢+使用时候需要解压,所以建议不要压缩。

4.6.1. Crunch Compression
Bundles which consist primarily of DXT-compressed textures which use the Crunch compression algorithm should be built uncompressed.

但是我们打AssetBundle的时候,并没有针对每个AssetBundle选择压缩不压缩的选项,要么所有都压缩,要么所有都不压缩。所以按照“使用Crunch压缩的图片打包时建议不要压缩的思路”的话,我想到2种做法:
1)所有AssetBundle包都不压缩,不知道这会不会导致Texture以外的资源的AssetBundle包变大很多,如果有用过的人希望介绍下经验。
2)先用LZ4打所有包,然后备份AssetBundleManifest文件,再用不压缩方式打Crunch压缩的图片包,打完把之前备份的AssetBundleManifest覆盖回来,感觉这种做法有点怪,还会导致图片的AssetBundle包要打2次,延长打包时间。
想问一下有没有更好的做法?还是说只要不压缩就可以了?

有时候有些选择的确比较纠结。让我选择的话可能直接都用LZ4的方式,毕竟LZ4自身牺牲了压缩率换取了“does not require the entire bundle to be decompressed before use”这种优势。Crunch压缩之后的贴图没有什么压缩空间,但是在LZ4中解压的时间也不是很长(具体需要题主自己测试下)。
AssetBundle不压缩我感觉是不太能接受的,之间简单看过整个包体会大挺多的,这个题主用你们工程试一下就知道了。两遍打包的过程,个人是觉得性价比比较低,而且可能隐含一些坑需要花点时间踩。对于打包来说,越简单的流程肯定越好。
因为用的项目还在Unity 5.6的版本,所以没有做过Crunch的相关测试,建议题主除了问大家的建议之外,也可以自己做测试真正验证下,比如确定下完全不压缩对于包体的影响,使用LZ4压缩和不压缩贴图对于加载的时长影响。相信有最真实的测试数据会让题主做决策时更加放心。
感谢贾伟昊@UWA问答社区提供了回答

两种策略:
1)LZ4压缩,打包时间在可接受范围之内;
2)AssetBundle不压缩,将打包好的AssetBundle进行zip压缩,游戏第一次初始化时,解压缩到可读写目录,这样Unity直接读取未压缩的AssetBundle文件,也方便以后做统一的热更新管理。
感谢郑骁@UWA问答社区提供了回答

楼上说的两种方式是主流方式,LZ4在包体和加载速度上有个好的平衡。自己压缩然后解压到可读写目录,用CreateFromFile方式加载,在内存和效率上都有优势,代价就是前面会有很长时间的解压过程,这个时候要给出界面响应,听闻如果时间过长没有游戏内容画面苹果爸爸可能会通不过。

感谢赵林@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5bc94e4b8af62a50acb8e0fd


渲染

Q2:我使用MeshBaker,把全场景的同Shader物件,在合并Mesh总面数64K范围内,尽量合在一起。合并完Mesh后发现场景的DrawCall降低了, 但是发现Stats面板中面数翻了倍。貌似相机的视锥体剔除功能对MeshBaker合批完之后的无效。求教这个问题怎么破解?

CPU送给GPU渲染之前的最小的单元是一个Mesh(或者一个Submesh?),所以如果你手动合并了Mesh,就意味着面数很大可能上会变多,这个是面数和DrawCall之间的取舍,所以建议根据项目瓶颈来选择一个合适的均衡点。
我们用的是Unity的Static Batch,可以降低Batches,面数不会增长,原理是合并成Submesh,并且进行排序,然后根据镜头依据Submesh进行裁剪。当然也有代价,包体和内存大小可能会多一些,然后因为只是做排序,会因为动态阴影等宏的设定导致被Batch被打断。
简答做个测试验证:2个Cube,合并之后:
请输入图片描述
旋转相机,视角内只有一个的情况下:
请输入图片描述
Demo比较简单,只做之前观点的验证,题主可以自己再做一个复杂的Demo来验证下。
天下没有免费的午餐。大部分优化都是有代价的,都是用另外的东西来换取,只是代价的性价比不同罢了。
感谢贾伟昊@UWA问答社区提供了回答

如楼主所言,这时一个客观存在的事实。所以我会选择如下方案:
放弃静态批处理,进行手动合并,合并的方案根据视野距离分块,将小块中的Mesh在场景Load的时候进行合并,这样DrawCall数会增加,但不是很多,三角形会大量减少。
感谢郑骁@UWA问答社区提供了回答

补充楼上,手工合并是会存在这个问题,但手工合并的时候可以自己控制粒度,但还是会带来不灵活的问题。Static Batch存在内存问题和包体增大的问题。
Unity提供了一种可以运行时合并的API StaticBatchingUtility.Combine,有两个重载
public static void Combine(GameObject staticBatchRoot);
public static void Combine(GameObject[] gos, GameObject staticBatchRoot);
可以使用这个API控制合并粒度,相当于静态合批,这个可以不增大包体,代价是内存和CPU开销。正如楼上说的,天下没有免费的午餐,优化大多时候是要付出另外的代价。

感谢赵林@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5bc951798af62a50acb8e0fe


内存

Q3:我有个2018.2.13f1版本上用WWW加载AssetBundle的内存疑问。WWW加载内存记录如下:

初始:
请输入图片描述

www下载中:
请输入图片描述

www.assetbundle调用之后:
请输入图片描述

www.dispose之后
请输入图片描述

ab.unload之后
请输入图片描述

www.assetbundle调用之后,Mono增长36MB,Unity增加26.1MB, 这个26.1MB怎么理解?下载的AssetBundle文件是UnCompressed,大小36MB;
请输入图片描述

通过在群里跟题主要了LZ4和LZMA的内存测试数据,我们发现如下表格的内存统计在2018(也许2017.4)上已经不太准确了。
请输入图片描述
以题主的New WWW加载为例,在之前的加载36MB的UnCompressed AB时,其内存是36+36=72MB,而现在则是36+36+26=98MB。
之前的72MB是全部统计在Unity中,现在是62MB在Unity中,而36MB在Mono中体现。
之所以出现这种情况,是因为Unity引擎在新版本中(暂时还无法考证具体是从2017哪个版本开始),New WWW加载AB会走www.bytes这个操作,然后通过LoadFromMemory来进行加载。这也是为什么36MB的Uncompressed在加载后,Mono会增加36MB,而后LZ4和LZMA的AB加载,Mono也都会增加与其Size一样的大小。
同时,多出来的26MB是由LoadFromMemory加载导致的,这里有个遗漏点需要补充下,即无论是36MB的UnCompressed AssetBundle还是10MB的LZMA AssetBundle,其www.assetbundle调用后,其Unity内存一项的增幅均为26MB,而如果是20MB的LZ4 AB的内存增幅则为20MB。这里面有两种推测:

1)LZ4 的AssetBundle bytes通过LoadFromMemory加载时,则直接转换成LZ4的AssetBundle来进行使用;而UnCompressed和LZMA则会被压缩成类似于LZ4的其他参数格式,所以内存会有所偏差;
2)无论是哪种压缩格式的bytes,都是转成LZ4格式,但UnCompressed和LZMA格式的数据是需要进一步执行数据转换的,因此,需要开辟一个Buffer来进行转换,所以多出来的6~7MB是Buffer所致。
当然,以上两种是在没有源码查看的情况下来进行推测的,个人比较倾向于第一种,但不排除有其他更多原因存在。后续,关于其他AssetBundle API的加载方式我们也都会进行大量测试,看来有很多内容需要更新了,如果社区其他朋友有做过类似测试或看过其他文章,也非常欢迎来进行指正和分享。

该回答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5bcd3f4d01a33650b1f88981


UI

Q4:目前我们在做台湾版本的多语言适配,也就是将简体版的改为繁体版的,目前不考虑图片尺寸的问题。目前需要适配的范围有:策划文字、UI文字、UI的单图、策划配置的单图、UI图集、策划配置的图集、UI Prefab、角色以及角色技能和特效。

关于策划文字和UI文字,我们都通过导入导出表格解决了,目前还剩下各种各样的图片,然后关于单图部分,在切换语言的时候,我遍历所有用到这些单图的Prefab、Asset,将引用修改为多语言版本的图片,也算是解决了。目前还剩下图集部分,我现在有两个想法:

一个是修改打图工具,在打图的时候筛选出不同语言的图片,选择当前语言的打包成一个图集,好处就是维护简单,不需要维护多份不同语言的图集,坏处就是打图的过程相对较慢,每次切换语言时间很长,然后不直观,不能对比两张图集之间的区别。
另一个方法就是,打两份图集,通过后缀来区分不同的语言,然后效仿单图的多语言解决方案,修改引用,好处就是打好了图片,如果没有修改不需要在切换的时候重新打包图片,速度快,方便对比,坏处就是需要维护两份图集,修改无多语言的图片需要同时修改两份图集,比较麻烦。

我们没有其他语言的图集,做法是把图集中带文字的图片拆出来用Texture来根据语言加载,以后的新项目是从设计上规范不把带文字的图片放入图集。多份图集的问题是,如果要动态切换语言,还需要切换对应图集,资源和内存的冗余都需要考虑。
感谢林健@UWA问答社区提供了回答

我们使用AssetBundle变种机制做的多语言,预设、图集、纹理都可以做变种,基本可以应对各种需求。好处是拖上去的引用不用换,Unity变种机制帮你搞定,坏处是有一个大硬伤编辑器下无法直接预览,必须先构建AssetBundle。
感谢littlesome@UWA问答社区提供了回答

多语言版本不仅会涉及到图片及文字替换,也会出现排版问题,例如阿拉伯语。所以在设计上尽量居中显示并排版,这样问题是向两边扩散的,减少文字过长被遮挡的问题。多语言有些游戏是在游戏内切换,所以我会在游戏中加入对应的type,在资源加载时进行区分。
Prefab的引用可以如题主所述修改。图集规划可以解决大部分问题,但是部分细碎图片,我会选择打包成大图集,做好映射关系表,在动态加载时进行区分加载,避免对原资源造成过大改动。
感谢郑骁@UWA问答社区提供了回答

以前我做的时候是文本,图集,Prefab,Audio等各种分开考虑,需要注意的问题就是各种语言的长度不同导致的排版,图集下各种语言放到一起然后打AsseBundle的时候做语言筛选,多语言图大小可能也存在不同,要注意图集的规划,不要出现分成两个图集的情况等。
现在我倾向的方案是用AssetBundle变种机制,这个要解决编辑器下预览问题,我们在编辑器模式和机器上走的加载流程不同,编辑器模式下走的加载Prefab显示,所以预览这个问题对我们项目来说并不存在。
感谢赵林@UWA问答社区提供回答

该回答来自UWA问答社区,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5bc5ae0d5c3fd06b3442ddc3


UI

Q5:UGUI字体加粗后Android真机并没有加粗而且加宽导致文字重叠,网上没有找到解决方案。不知道该如何解决。

假如你用msyh,那你需要在资源里面同时放上msyh以及msyhbd,这样才能在UGUI中使用msyh的粗体,不然你得到的只有Unity自己加粗了的msyh。

该回答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5bc59e7d5707da4d53bc3633

今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
官方技术QQ群:793972859(原群已满员)