技术分享连载(七十四)

技术分享连载(七十四)

本期聚集话题:网格顶点属性丢失、优化数据表的加载、Screen Depth Buffer RenderTexture.Color Buffer、图集格式设置...


我们将从日常技术交流中精选若干个开发相关的问题,建议阅读时间15分钟,认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。
UWA QQ群:793972859
UWA 问答社区:answer.uwa4d.com


资源管理

Q1:在移动平台上打AssetBundle时,为了控制包体大小,会开启PlayerSettings中的Optimize Mesh Data,对网格文件使用范围比较多的资源有较好的优化效果。 然而这带来了一个问题,就是在部分使用网格的特效中,美术会对模型上一些顶点做特殊处理,以达到一种网格遮罩的效果,而这个设置需要用到网格的顶点色。然而在开启Optimize Mesh Data这一选项后,打出来包中Mesh上的顶点色会丢失。 请问有什么比较好的解决方法,既能使用此方法优化网格,又能保留网格的顶点色效果?

一般来说,开启“Optimize Mesh Data”选项,引擎会在发布时遍历所有的网格数据,去除其多余数据,从而降低其数据量大小。需要注意的是,这里的“多余”数据是指Mesh数据中包含了渲染时Shader中所不需要的数据,即如果Mesh数据中含有Position、UV、Normal、Color、Tangent等顶点数据,但其渲染所用的Shader中仅需要Position、UV和Normal,则Mesh数据中的Color和Tangent则为“多余”数据,引擎在发布游戏时,会将这些数据自动去除。

需要注意的是,开启这个选项是一把双刃剑。对于在Runtime情况下有更换Shader需求的Mesh,建议研发团队对其特别注意。如果Runtime时需要为某一个GameObject更换更为复杂、需要访问更多顶点属性的Shader,则建议先将这些Shader挂载在相应的Prefab上再进行发布,以免引擎去除Runtime时会使用到的网格数据。

此问答来自于UWA 问答社区,如您对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/5985785a46dafb521451bd85


资源管理

Q2:安卓客户端存在大量的模板数据需要配置,其中一些模板表甚至可能达到万级的数据条目,那么怎么对这些数据模板表进行打包和加载,可以兼顾加载速度和热更新表结构?一开始我们采用了ScriptableObject,把全部模板数据加载到内存并序列化为Asset的方式进行Assetbundle打包,该方案加载速度较为理想。但当我们通过Dll替换热更新安卓客户端时,发现这种方式不支持热更新,一旦Dll中修改了模板表结构,热更新替换后,ScriptableObject的AssetBundle就无法读取了,提示损坏的AssetBundle,目前的方案是采用Protobuf代替ScriptableObject进行序列化,可以实现热更新模板表结构,但是加载速度相对ScriptableObject有较大的差距,目前数据模板加载较慢便导致了玩家进入世界的时间比较久。因此想了解大家有什么好的建议呢?

本地读取数据表是个老话题了。我不能完全解答这个问题,只能提供一下我过去项目的浅显的经验。用ScriptableObject或者BinaryFormatter二进制存储然后反序列化成保存数据结构的对象,这两种方法应该是加载速度最快的。

我们实际没有采取这个方案,也使用的是Protobuf,是出于以下考虑:

  • 一份二进制数据,客户端和服务器可以通用。从服务器推数据很方便;
  • 策划习惯使用Excel编辑,有脚本可以把表格内容导出成Protobuf的二进制数据,另外,还有.cs/.go表结构描述文件需要重点考虑。也就是说,策划修改表结构、增减表,服务器和客户端的结构描述文件可以自动生成好;

实际用下来,确实会有一些差距。如何选择还是要看表格的体量了。

感谢 UWA 问答社区的 WangLiang 提供了以上回答。

我先把问题拆分一下,题主遇到的问题如下:

第一个问题:配置表存储格式
现在主流的数据存储基本分为三大类,各有优劣,需要根据实际情况选择:
1、ProtoBuf或类似序列化库,这种方式兼容性高,但是加载速度一般;
2、自己实现二进制数据存储,兼容性差,需要精心设计达到较高的数据表达能力;
3、采用Lua热更新方案的游戏,普遍直接把数据存储为Lua表。

第二个问题:配置表数据与代码兼容
一般不建议大量修改数据结构,比如增删字段,如果实在无法避免,需要代码连同数据一起发布进行热更,做好版本管理即可。

第三个问题:配置读取速度优化
1、先从数据量上约减,减小数据冗余重复,数据存储设计优化,多次引用的字段多引用等等;
2、采用多线程加载,避免使用Unity提供的API,在游戏启动时,并行加载配置表,充分利用多核优势;
3、就我们自己项目而言,没有使用Lua的更新方案,但是我们依然采用Lua作为了数据存储,经过优化后加载速度也不错,可以参考 LuaTableOptimizer。

感谢UWA问答社区的Lujian提供了以上回答。

如您对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/5981c5c5c08748f866b5ebe2


渲染性能

Q3:MainCamera 渲染完场景后,在 Screen Depth Buffer 上记录了整个场景的深度信息。Camera2 在 MainCamera 后,将模型绘制到一个 RenderTexture 上,我想利用 Screen Depth Buffer + Camera2.RenderTexture.ColorBuffer 对 Camera2 绘制的内容进行深度剔除,这时 Unity 会提示 Screen Depth Buffer 和 RenderTexture.ColorBuffer 不能混用。这样的需求该怎么做呢?

我搜索到一个方案,就是使用 _CameraDepthTexture ,在Camera2绘制模型时,在 Shader 中通过深度进行clip剔除。由于_CameraDepthTexture 会开启相机深度渲染,场景的 DrawCall 翻倍了,所以不考虑这个方案。

还有一个方案,就是将 MainCamera 也渲染到一张 RenderTexture上,这样 MainCamera.RenderTexture.DepthBuffer + Camera2.RenderTexture.Color 组合起来就能满足需求,最后将结果 Blit 到屏幕Buffer。但不知这样对性能有哪些负面的影响。

Depth textures are available for sampling in shaders as global shader properties. By declaring a sampler called _CameraDepthTexture you will be able to sample the main depth texture for the camera.

_CameraDepthTexture always refers to the camera’s primary depth texture. By contrast, you can use _LastCameraDepthTexture to refer to the last depth texture rendered by any camera. This could be useful for example if you render a half-resolution depth texture in script using a secondary camera and want to make it available to a post-process shader.

这个是官方文档的一个说明,按照题主的需求是否可以尝试一下_LastCameraDepthTexture变量。_CameraDepthTexture只是记录本相机的Detph,_LastCameraDepthTexture记录的是之前任何一个相机的Detph。

第二个方案,将MainCamera渲染到RenderTexture上,最后将结果Blit到屏幕。多一次Blit肯定会有额外性能开销,但也与Blit的Shader复杂度有关。

此问答来自于UWA 问答社区,如您对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/59803134a3de32780242667c


资源管理

Q4:请问在iOS上,图集如何设置成PVRTC的格式呢? 下图中的设置是否正确?
请输入图片描述

图集的设置需要考虑到该图集的用法,如果是作为普通的Texture给3D物体用的,那么TextureType选择Texture,然后Format选择Compressed就可以,因为在iOS上,Unity会自动处理NPOT(把纹理拉伸为边长为2的幂次的正方形),并默认采用PVRTC。设置之后可以看一下下方的预览面板,会直接显示PVRTC的;如果是给UGUI用,那不同情况下,设置又不一样了;如果是给NGUI用,可以按照问题中截图里的设置即可,最终再确认下预览图上是不是PVRTC。

如您对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/59832e4f81507d903785754e


其他

Q5:请问怎么在inspector中显示Dictionary呢? 例如: Public Dictionary<string,string> dict;

Unity 无法序列化Dictionary类型,无法将其显示在inspector中。一个Workaround是将Dictionary的Key和Value拆到两个list中,list可以在Inspector中显示。利用API:ISerializationCallbackReceiver (https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html 有示例代码)可以在序列化/反序列化时将Dictoionary中的内容与list内容同步。

此问答来自于UWA 问答社区,感谢 Saber提供了回答,如对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/5981a6c46004cc7c562cd159


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