UWA 是提供游戏专业的游戏研发/性能优化的咨询服务商,我们每周为行业开发者分享的知识型栏目《厚积薄发 | 技术分享》,现在已经伴随大家走过了 150 个工作周。
新年伊始,随着大家紧锣密鼓地开始了工作,我们优选了 12 个开放性的精彩回答,分享给大家。

UWA 问答社区:answer.uwa4d.com
UWA QQ 群 2:793972859(欢迎大家的加入)


1、如何设计渲染等级?

现思考如何设计项目中的渲染等级,求有经验的大佬指个方向,例如 ShaderLOD,还有其他什么思路吗?谢谢!

精彩回答:正好我也整理过项目的 LOD 规则,可能不限于题主描述的渲染等级。一套好的 LOD 系统需要美术在资源上做大量的资源分级工作,需要程序搭建完善的 LOD 框架和辅助工具,每增加一个功能模块都需要把 LOD 考虑进来。

一、如何确定项目的 LOD 分档和性能标准

LOD(Level of Detail),这里的 D 代表 Detail 而不是 Distance,也就是说,所有游戏画面和游戏功能的细节都可以进行分级,不必受限于距离。

1、如何制定性能标准?

在分级之前,需要确定目标(省电、正常的游戏体验、噱头),我们要在哪一种机型上面运行哪些表现效果和功能,达到怎样的性能目标(帧数 FPS、内存占用、Drawcall、同屏三角面数等)。

◆经验总结:

1)画质的表现力与性能开销是相互矛盾的
2)需要持续维护,相关功能系统的设计都需要考虑到不同画质
3)极简画质牺牲了可玩性,同时会增加 LOD 框架的复杂性,使用最简单暴力的方式去处理

◆兼容性问题:

1)低配画质需要考虑的适配问题
• 可能不支持 Shader 定义 8 张以上的贴图纹理
• 可能不支持 ETC2 格式的贴图纹理
• 可能不支持 OpenGL ES3.0

2)高配画质需要考虑的问题
• 支持 PBR、线性空间
• 奇怪的 Bugs

3)需要解决各个画质下的兼容性问题,建立黑名单和白名单。

2、如何找出可以进行分级的细节?

◆优先找出开销大的点
• 后处理效果 Bloom、HDR、ToneMapping、MotionBlur、DOF 等
• 实时光照和阴影、水面实时反射
• PBR 物理光照
• Ragdoll、DydamicBones 等物理系统
• 日夜循环、特效天气系统

◆细节模块的 LOD 功能考虑
• 场景/角色/特效/摄像机相关/其他系统模块

(完整答案请前往问答社区进行查看。)

感谢文雅@UWA问答社区提供了回答,欢迎大家转至社区进行交流


2、PBR 在手游开发中的适用性

我们是 MMO 游戏,现在想给主角(包括其它玩家)使用 PBR 材质(Untiy 自带的 Standard),其它角色和场景不使用。不知这种情况下性能是否能抗住,同时我也想了解下目前手游中对于 PBR 材质的使用情况。

精彩回答:目前手游中使用 PBR 材质的项目明显增多,特别是 MMO、RPG 等项目中,主要角色、NPC 和大 Boss 等都开始往 PBR 方面走了。

如果 PBR 仅是在以上情况下使用时,Unity 引擎自带的 PBR 是没有太大性能问题的,因为一般角色占据的屏幕面积都会比较小,而过剧情时,虽然屏幕面积占据较大,但时间一般都很短,所以给总体性能造成的压力不大。

但 PBR 暂时还不建议在中低端设备上用在地形等常常在屏幕中占据较大面积区域的物体上,因为它确实会造成较高的 GPU 压力,我们在 UWA Day 2018 上也对此进行了定量分析(类似下图),这点需要研发团队在使用时注意。但是,一切应该以自身项目测试为主,具体查看项目在自己设定的低配机器上到底性能如何。

最后,在使用 Standard Shader 时,除了关注性能外,还要关注下它对于内存的影响,可以参考这篇文章:【求知探新】Unity 中 ShaderLab 内存优化

该回答由 UWA 提供,欢迎大家转至社区交流


3、使用 FairyGUI 开发经验分享

我们新项目中想使用下 FairyGUI,主要还是为了减轻程序工作量,请问大家有在项目中使用过的吗?主要是关于图集、DrawCall 优化、一些滚动条的优化和 Richtext 解析等等,想问下在这些方面 FairyGUI 有没有很好的支持呢。另外可以支持自定义 UI 控件么?有经验的朋友都可以分享下吗?

精彩回答 1:两个上线产品,一个自家的一个朋友的:
http://www.9game.cn/xxyg
http://www.9game.cn/mengxiangqiyuan

主要几个点:

1)对美术十分友好,各种习惯跟 Adobe 系列的一致,编辑器本身就是 AS3 开发的;
2)包装了一些概念,十分方便拼界面工作;如关联=>屏幕适配 、控制器=>状态控制等等;
3)之前的版本解析使用 xml,GC 较多,最新版本已经更新为二进制,界面创建的 CPU 和内存消耗都降低了很多,但是还在内部使用,并未应用到线上;这个功能 FairyGUI 也刚刚发布不到两个月;
4)自带图集支持 Alpha 通道分离,在贴图压缩和机型兼容上可以直接应用到;PS:建议稍微修改源码,让 Alpha 的贴图格式为 Alpha8,实测这样的在保证视觉效果的前提下可以把贴图压很比较小;
5)各种基本的东西:DrawCall、图集、各种类型组件等等这些都属于还不错的状态,一般不会遇到什么支持不了的;
6)讲一个调试缺点,FairyGUI 的组件并不是 MonoBehaviour 类型的,所以在 Unity 里面看到的 GameObject 并不能和组件一一> 对应,遇到一些奇怪问题的时候需要一定想象力,不能像 UGUI 或者 NGUI 简单直接看到 UI 的参数;
7)FairyGUI 是跨平台的,会遇到 FairyGUI 编辑器中预览的和 UnityEditor 里面看到的不一样的情况,如果对 Flash 的渲染有一定了解,应该不算什么障碍,如果没有相关经验还是需要踩一点坑的。
感谢袁首京@UWA问答社区提供了回答

精彩回答 2:作者也来凑凑热闹。最大的优势当然是开发效率,用了 FairyGUI 的基本都中毒,这种开发模式的效率是其他 UI 远远不能比的,越大的项目优势越明显。UI 开发有一个很大的特点是重构次数比较多,一旦发生这种情况,通常都会有让一线程序员辞职的念头,但用 FairyGUI 的就可以做到云淡风轻。

大家很关注的性能,我觉得 NGUI/UGUI 为了降低 DrawCall 采用的合并 Mesh 技术对 UI 制作要求太高,一旦动静分离不合理会引发灾难。FairyGUI 提供的优化技术是动态后期优化,在制作时对 UI 人员基本没有要求。简单的说,就是你随便拼,FairyGUI 负责自动优化。两者的运行性能,你很难感受到差别。但如果 UGUI 你不优化,就会和 FairyGUI 有很大差别。所以有一个说法,FairyGUI 可以轻松应付低端机,但 UGUI 却要花大力气。

GC 问题是 C# 库不可回避的话题,FairyGUI 也在不停迭代中改进。最近推出的二进制格式,更是解决了大家一直有意见的加载问题。FairyGUI 运行时,如果不发生文字和图片的更换,是没有 GC 的,即使各种动效(位移、透明、旋转等等)在不停运行。这一点是很难得的,因为一般情况下,你如果要用其他 UI 实现这些效果,那么势必要引入其他插件或者自己写大量代码,那么这些代码的优化是要你自己完成的。

最后也来说说缺点,首先 FairyGUI 需要你学习多一个编辑器,了解一种不同的开发模式,在 FairyGUI 目前知名度远远不及 UGUI 的情况下,选择 FairyGUI 需要决策者有前瞻性;其次 FairyGUI 结合 UI 实际运用的痛点,自带了很多缓存机制,例如游戏中 List 总是避免不了不停刷新,所以 FairyGUI 的 List 是自带 Cache 的等等,你需要了解一下这些特点,否则会造成 FairyGUI 有内存泄漏的错觉。
感谢谷主@UWA问答社区提供了回答。

欢迎大家转至社区交流


4、Lightmap 平台色差问题解决方案

问题描述
PC 上烘培,转到安卓平台即可复现,但非所有项目都会遇到这个问题。我使用的 Unity 版本是 2017.4.6f1,经过测试 2017.4.14f1 依然存在这个问题。色差具体表现有亮度降低和色相变化。

经过了一个通宵,排除了贴图格式、项目设置、烘培设置、灯光参数等,最终确定改了问题原因:

问题原因
Android 不支持完整的 exr(HDR)格式,在 Swith To Android 的时候,Unity 会把 Lightmap 自动转换成一种低精度格式(并且不给任何选项和提示)。重点是这个格式转换并不是完整映射,而是类似拷贝过去的形式,转换过程会有数据溢出的问题。

变暗很好理解,而色相问题可以这么理解:假设光照贴图是一个 (2000,255,0) 的近乎大红的橙色,经过一个有数据溢出的格式转换后,会变成 (255,255,0) 的纯橙色,这就是我们看到色差的原因。

解决方案探索
在解决问题的过程中搜索到了这两个链接:

1、Unity 填坑笔记(二):移动设备上烘焙变暗问题

解决方案:测试出 Lightmap 数据 Unity_Lightmap_HDR,然后使用这个参数直接调用源码中的 HDR 解码算法,并将贴图转成 ETC(2)_RGB4。

这个方案我持否认态度,首先 Alpha 通道是用于保存亮度倍数的,抛弃了必定是有损的;其次,unity_Lightmap_HDR 是根据不同烘培结果定的一个值,不是每个场景都固定的。就是说,即使要这个方案也不是这么做的,Alpha 通道不能丢弃,unity_Lightmap_HDR 还是得读原来的值,否则换个场景就又有色差了。

2、Lightmap 在 PC 上与 iOS 和 Android 上表现不同的问题

解决方案:拿原图重新编码,游戏中使用重新编码的贴图进行解析。

这个方案比较靠谱,但是我这边测试做不出来。

问题的进一步分析
我这边已经基本上完全排除其他因素了,用新建的项目和同样的贴图、灯光对象,全部使用默认设置来烘培,这东西确实是引擎 Bug,但有些项目却不会遇到,这让我感到困惑,这一定是制作流程问题。

通过对比我发现,我们场景的贴图画得非常暗,而烘培使用了高亮度的烘培,而这个做法是不合理的,贴图那么暗(基本压到 0.4 以下),是一种严重的精度浪费,而光照强度也很不符合正常环境的亮度。

所以如果没特殊原因,应该所有出问题的项目都是因为低亮度贴图强行烘亮的操作引起的。

我个人认为最科学的解决方案
以正常亮度为标准来绘制贴图,以符合现实世界强度的灯光参数来烘培场景,就不会出现这个问题了。不需要重新编码,也不需要实时解码,就可以避开这个引擎问题。但是如果有高亮度烘培的硬性需求的话,就必须自行编码重新保存了。

精彩回答:
解释两句,题主说的那个 Unity 填坑笔记后来整理了一份更加详细的文档:
https://zhuanlan.zhihu.com/p/28728151

解释几点:
1)这个文章要 fix 的问题是 Unity 5.x 版本中对于线性空间烘焙处理的一个 Bug,这个 Bug 在 2017 版本中已经修复了,所以和题主遇到的问题并没有直接关系。所以文章的解决方法并不能解决题主的问题。

2)赞同 Alpha 通道抛弃掉必定有损,但是这是我们自己项目在观察美术烘焙好的 lm 贴图的 alpha 通道之后得出的结论——我们项目中 lm 贴图占用的内存尺寸和 Alpha 通道带来的效果提升相比,我们更倾向于损失后者,所以这是个取舍问题,建议各个项目根据自己的具体情况来定,所以并不赞同题主认为的不能丢弃的观点。

3)使用 unity_Lightmap_HDR 的确不正确,在我们项目中效果 “正确” 也只是看上去一样而已。我们也已经更改为 2017 fix 的正确答案:

inline half3 DecodeLightmapDoubleLDR( fixed4 color )
{
float multiplier = IsGammaSpace() ? 2.0f : GammaToLinearSpace(2.0f).x;
return multiplier * color.rgb;
}
inline half3 GammaToLinearSpace (half3 sRGB)
{
// Approximate version from http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
return sRGB * (sRGB * (sRGB * 0.305306011h + 0.682171111h) + 0.012522878h);
// Precise version, useful for debugging.
//return half3(GammaToLinearSpaceExact(sRGB.r), GammaToLinearSpaceExact(sRGB.g), GammaToLinearSpaceExact(sRGB.b));
}

另外,美术把贴图做得亮度偏低,然后通过打光来提高整体亮度,貌似很多美术喜欢这种做法...我们美术在制定了线性空间下标准贴图亮度为 187 左右的情况下,还有部分场景故意使用较暗的贴图亮度。也可能是我们没给场景开 HDR 的原因。

感谢贾伟昊@UWA问答社区提供了回答。

欢迎大家转至社区交流


5、如何深入掌握研发项目的物理性能

如何提高 Unity 物理引擎 Physx 的性能?我在场景中加入 100 个具有物理刚体的小人,并充分碰撞,发现低端机的帧率只能跑到 10 帧以下。

精彩回答 1:如果需要充分进行物理计算,这些拥挤在一起的小人很可能会造成大量的物理碰撞,进而造成很高的物理耗时。

在优化之前,题主需要先了解一些基础物理数值推荐,并不是所有设备都能流畅跑起 100 个自由制作的蒙皮模型的。所以,建议先看这篇文章:如何读懂 UWA 性能报告?—物理篇

另外,题主还需要看一下真机上的具体耗时瓶颈是否真正耗时在物理上,100 个角色,如果 Mesh 量很大,渲染耗时也是不可忽视的,所以建议通过 Unity Profiler 来先确定一下真机上性能瓶颈。

最后,是否每个小人都需要精确的物理计算,是否可以通过一些射线求交、范围判断等性价比高的判断方法来代替纯物理碰撞?这需要题主从设计的角度上来进行权衡。
该回答由 UWA 提供

精彩回答 2:你并没有什么办法直接调用 PhysX 的 API,所以所谓优化物理引擎的性能,倒不如说优化相关组件的性能。首先优先使用 CharacterController 而非 RigidBody。然后尽量避免使用 MeshCollider,用 BoxCollider、SphereCollider 和 CapsuleCollider 代替,如果不可避免,那么钩上 Convex。当然,最好的优化就是不要用物理碰撞。射线能解决很多问题,但是射线也不是免费的,它的消耗跟场景里的面数正相关,当然设置射线的 maxDistance 和 LayerMask 能节省掉不必要的消耗。最好就是用简单的物理模型来描述问题,自己写代码实现。

感谢凯奥斯@UWA提供了回答。

欢迎大家转至社区交流


6、大世界场景优化和加载策略

关于大世界 (SECTR+Terrain)我有几个问题:
1)市面上是否有手游(比如吃鸡)使用了 SECTR 和 Terrain 来解决大世界的问题,并且实现效果还不错的呢?另外是否有其他现成插件也适用大世界的??
2)Terrain 的 Draw Call 有没有什么优化的 Guideline?我注意到 Terrain 自带的树和草似乎都比较费。另外,水平视角的时候能看到较多场景中的内容,有什么一般性的策略来减轻渲染负担?
3)在 SECTR 的框架下,可以很容易的给一个地图分块,但应该如何解决地表分层的问题(比如地面、建筑、装饰等)?这里的层似乎已经不符合 SECTR 中 Sector 的概念,而是另一个维度。
4)大地图中用何种手段解决 LOD 的问题比较好?SECTR 提供了一个简单的 LOD 方案,只是对组件(尤其是 Renderer)根据其 Bound 来进行显隐。SECTR 是否在 GC 方面有坑?
5)性能上,通过 AssetBundle 频繁加载和卸载子场景(这需要修改 SECTR_Chunk),如何能减少卡顿?

精彩回答:就目前我们优化过的超大地形(8kx8k)的移动游戏来看,基本上都没有使用 SECTR+Terrain 这一解决方案的,而是全部转换成 Mesh 来进行无缝拼接。我们没有使用过 SECTR 这一插件,所以对其分拆机理和组织方式并不了解。但是对于 Terrain 来说,一般研发团队更倾向于使用 Mesh 来进行替换。

Terrain 的优势在于编辑十分方便,通过一些插件(比如 Terrain Composer)可以快速生成基础地形等等,但是它的 Draw Call 并不容易控制,至少很不直观,内存同样较之同等复杂程度的 Mesh 要大,且 TerrainData 的加载效率也并不高。所以,对于前期通过 Terrain 来进行地形编辑的团队,后续往往会将其导出成 Mesh 来进行动态分块加载。这是目前我们较为推荐的方案。

对于场景的加载,只要 AssetBundle 中的内容不多,Size 不大,现在通过 LoadFromFile+LZ4 的方式来进行加载已经相当快了,现在的顿卡一般不出现在 AB 加载上,而是出现在 AB.Load 和实例化上。对此,有效地办法是通过预加载、缓存和自己制作流式加载来缓解卡顿问题。前两者伟昊在他的回答中已经很详细了。流式加载则是指控制每帧中加载和实例化的资源数量,这需要根据具体的内容、情况来进行具体分析了,并没有统一的准则可遵循,唯一要做的就是多测试、多试验。

除上述之外,还需要以下几点了解一下(以下为题外话):
(1)地形切得不要过于细碎,否则会加大 Culling 以及后续引擎场景规划(CreateSharedRenderScene)的开销;
(2)超大地形的游戏(吃鸡、沙盒等)中,UI 模块的开销较之其他游戏(MMO、ARPG、卡牌等)要明显降低,这其实可以给其他模块贡献出更多的计算空间;
(3)物理模块的耗时则大幅提升,如果在加上一些车体载具,那么其往往会在不注意之间加入大量的物理开销,这是研发团队在面对大地形开发时会遇到的新话题。

该回答由 UWA 提供。

欢迎大家转至社区交流


7、如何把握卡通渲染制作流程?

我最近在 Unity 中制作角色的卡通渲染效果,目前的工作流程是这样的:
1)程序在 Unity 中写 Shader 调效果;
2)美术在 3dmax 修改模型顶点色和法线等资源,修改完导出给程序;
3)程序将资源导入 Unity 中验证效果。目前需要频繁地修改资源、导出导入资源,效率比较低下。
所以我有以下两个问题:
1、如何在 3ds max 中写着色器,美术修改模型后能及时看到效果?
2、成熟的制作流程是什么样的?

精彩回答 1:
1、这个不难的,让渲染程序员简单看一下 Maya 或者 Max 的文档,几个小时基本都是可以搞定的,Max 或者 Maya 都有例子和文档,比如 Max 的话就是用 DirectX Shader Material,不用把这个想得很难,好好看文档。

2、我觉得本质上主要是想要所见即所得,所有的工作也都是为了这个目的。卡通渲染对美术资源的修改大多需要定制化,例如靠刷顶点色控制描边,靠改顶点法线去改善光照,所以如果把 shader 做到 max 里可以大大提高美术的制作流程,而不是总是导入导出。通常的流水线是,维护一个和引擎预览效果一直的 max(或 maya)shader 材质,再加一个自定义的法线编辑工具(如果美术觉得内置的不够用的话,比如 max 的法线编辑比较困难),这样在修改美术资源保证效果一致性就好了。
感谢乐乐@UWA问答社区提供了回答

精彩回答 2:
对于第一个问题,读书哥提供了一个方法:

max 里可以用 HLSL 写,有 DX9/11 的 Sample,但是问题挺多的,Driver 建议用 Nitrous Direct 3D 9,记得关掉 Gamma/LUT 和 SSAO。

附上参考文档链接:
http://docs.autodesk.com/3DSMAX/16/ENU/3ds-Max-SDK-Programmer-Guide/index.html?url=files/GUID-0C7A600E-7954-42B0-86EE-586A379A2CAD.htm,topicNumber=d30e34269

感谢李嘉乐@UWA问答社区提供了回答。

欢迎大家转至社区交流


8、吃鸡游戏的物理碰撞

由于吃鸡类游戏的强同步,很多时候可能使用帧同步,客户端无法使用直接使用物理引擎,或者状态同步情况下服务器需要计算碰撞等。此时怎么处理这一块碰撞呢?数据结构又是怎样呢?

精彩回答 1:如果让我选择技术方案的话,绝地求生这种 3D 自由视角的吃鸡游戏绝对不会选择帧同步,原因有:

1)射击游戏在玩家移动、开枪等操作上会有较强的手感体验上的诉求,帧同步很难支持即时的操作反馈;
2)自由视角的吃鸡虽然没有战争迷雾,但是会有视距的问题,使用帧同步把所有信息广播给玩家,外挂做起来简单太容易,而吃鸡手游中标定其他玩家位置这样的外挂又有很大的优势,所以本质上不适合。

不知道题主说的帧同步和我理解的帧同步是否一致。然后,基于状态同步,服务器可以跑物理,但是真实的物理完全在服务器跑,对于服务器的压力太大,需要付出的成本过高,一台物理机也可能承载不了多少同时在线的玩家消耗,运维的成本你要评估下是否可以接受。

常见的做法:
1)简化 3D 物理,根据上下文状态只做部分关键判定,比如类似命中这样的。不过因为物理中的数据计算比较敏感,浮点误差都可能导致结果不一致,这里的工作量和坑应该都不少。

2)使用 2D 物理系统代替 3D 物理系统来做,在物理计算中去掉高度,只做 2D 的碰撞,结合专门的检测进程,是一种可行的检测方案,性价比比较高;

3)客户端判定,服务器验证,先相信客户端的判定,然后进行客户端表现,然后服务器基于数据做验证,这里可能会使用 2D 物理引擎,也可能在核心判定中再添加一些比如高度的信息等来做;

4)客户端判定,加严格的反外挂方式,封号之类的。这种当然你也要可以判断出来玩家作弊,判断方式可以未必实时去做,类似帧同步放后面通过回放模拟来做也可以,这时候有可能可以使用 3D 物理的来做(经验上依然不推荐,只是从实时变成离线,对于性能的要求没有那么高了,3D 成为一个可能性)。

5)先不做,等游戏火了有钱了再加,没火的话,也没什么专业的外挂团队来使坏。
至于数据结构,太细节了,根据需求自己设计吧。2D 的直接集成开源的 2D 引擎就可以了。
感谢贾伟昊@UWA问答社区提供了回答

精彩回答 2:吃鸡玩家数量一般在 100 个以上,这种情况下用帧同步需要同步的数据量会很大,延迟也会比较严重。因为帧同步一般需要收集所有同场景玩家的输入,然后分发给各个客户端,让各个客户端用相同的逻辑自己去计算每个玩家的位置、状态,游戏逻辑是跑在客户端的。MOBA 适合使用帧同步是因为一场比赛只有 10 个玩家。

我觉得吃鸡还是适合状态同步。状态同步怕的是角色太多,导致需要同步状态的角色过多,造成网络同步数据量大。吃鸡没有什么野怪,全部是玩家,所以场景里角色就是玩家。可拾取的装备需要同步的数据比较少,基本上需要一个位置坐标就行了,玩家身上的状态数据量要多很多。客户端看不见的玩家、装备,完全可以不用同步,因为逻辑完全跑在服务器端,客户端只需按照服务器的逻辑做绘制就行了。

关于游戏手感的问题,FPS 游戏对延迟的要求是所有游戏类型中最高的。如果玩家网络不是很好,也很难优化到比较好的情况。客户端先行可以先做显示的预判例如:击中后的血迹。但是数值结果还是依靠服务器端的计算,否则很容易被外挂利用。即使服务器结果判定没有打中,问题也不是很大,最多就是玩家看到有几枪打中有血迹特效,但是对方没扣血。这种情况大概率出现在网络环境不好的情况。

感谢 ZFK@UWA 问答社区提供了回答。

欢迎大家转至社区交流


9、帧同步的浮点数精度的不一致问题

我们游戏采用帧同步框架,计算逻辑的时候不可避免地产生浮点数运算,如移动和跳跃等。为了保证在不同的平台和不同的机器上得到一致的结果,有什么好的方案能解决或避免浮点数结果不一致的问题呢?

精彩回答 1:一般情况下,浮点计算涉及到的位置同步无需保留过高的精度,小数点后保留 2 位到 4 位足矣。因此计算前将浮点数乘以 1000,然后取整再进行计算,可以有效地解决多端的精度不同步问题。但是需要注意一个问题,就是别超出了你数值类型的最大值范围。
感谢 odiecc@UWA 问答社区提供了回答

精彩回答 2:根据我们自己正在摸索的帧同步经验,如果不是王者荣耀这种级别的游戏,帧同步的浮点数精度影响不是系统的瓶颈。如果战斗时长不长,露馅的概率很低,全改整数的工作量是非常巨大。如果是小公司,要好好评估是否能挺住。

逻辑与表现的分离才是最大的问题,意味着 Unity 的大量组件不能使用。大量的重新实现的东西需要从头去写,当然全用整数也一样得重头去写。游戏小公司需慎重评估生存问题和完美方案的性价比。
感谢李先生@UWA问答社区提供了回答

精彩回答 3:帧同步还是定点数比较靠谱,浮点数带来的误差有可能累计到一个无法接受的地步。另外回答其他朋友的一些问题。

1)网络传输,使用可靠的 UDP(TCP)传输;

2)另外看到其他朋友提到的逻辑与表现的分离,这个是帧同步方案必须的,至于说到的 Unity 中大量组件组件无法使用,主要是> > 帧同步方案逻辑的处理不应该按照本地时钟时序,而是严格按照服务器逻辑帧处理,说不可用其实主要针对逻辑层而言,表现层其实不见得 Unity 组件都不可以用,表现层上 Unity 的组件其实必不可少的。

感谢 Chale@UWA 问答社区提供了回答。

欢迎大家转至社区交流


10、Unity 中纹理格式的探究

关于 Unity 中使用的纹理格式问题,自己查了一些资源,有了一些大概的认知,但是希望能有比较完整、有理有据的依据。如果已有整理好的文章求科普!目前从各方面渠道得到的大致结论有:

产生了以下疑问:

精彩回答 1:关于 ASTC 的支持率,参考贾伟昊的回答。
https://answer.uwa4d.com/question/5a6700517daacf4c7ff04918

可以考虑在 iOS 上用起来,但是要放弃一些机型。安卓上建议使用 ETC2,搭配 Unity 的 Crunched 算法,效果更佳。RGB24/RGBA32 确实不建议使用,主要是会占用显存带宽,但是效果确实好。Dither 是可以用,但是对 Scale9 和渐变不友好。

UI 的 Mipmap 不要开,UI 不会发生 Mipmapping 的情况,除非你设计的纹理尺寸比实际用到的要大很多。

模型的纹理资源的 Mipmap 一般是要开的。RTR 第三版 18.3.1 提到 Mipmap 可以提高纹理 Cache 连续性,即提升 Cache 的命中率,也提升了纹理采样的速度。另外开了 Mipmap,可以在 Mipmap level 之间进行三线性滤波(当然这个是不建议开的),可以使纹理放大缩小的时候,看起来质量更好。因为在生成 Mipmap 的时候,Unity 可以选择滤波算法,所以即使不开三线性滤波,在对应的 Mipmap level 上采样,也比在原来的大尺寸的纹理上采样效果要好一些。
感谢凯奥斯@UWA问答社区提供了回答

精彩回答 2:凯奥斯已经回复得很全面了,想到的内容就补充一下好了。

1)了解一下各种压缩格式对应的设备 OpenGL ES 版本限制,这样方便你面对问题的时候做取舍,常规的建议终究只是立足于经验和项目的建议,个人根据自己项目需求的判断更加适用。

2)类似 PVRTC 的限制,和压缩方式有关,也和 GPU 采样效率有关。所以即便选择没有限制的压缩格式,甚至不压缩,PVRTC 的限制都最好保持长宽为 2 的幂。

3)UI 用 PVRTC 的确难以接受,主要还是 Alpha 通道的问题。大型项目可以考虑 ASTC,另外的思路就是拆分 Alpha 通道、白名单内的贴图采用不压缩或者 Alpha 通道拆分的方式。

4)我们 Loading 图一般是对应的设备上的压缩格式,比如安卓 ETC2、iOS 上 ASTC,其实 Loading 图一般不透明,PVRTC 也够用了。尺寸按照 UI 需求一般是和设计分辨率一致。这个也完全看缩小之后的效果 UI 是否满意,Loading 图注意及时卸载,只增加峰值,不常驻占用,内存影响不大,包体方面一般没这么敏感。

5)除了 UI 不用开 Mipmap 之外,比如一些 2.5D 的固定视角游戏有时候也不开,目的是追求清晰度;另外有些特殊的贴图也可以不开,比如染色用的 Mask 通道图等等,我们一些面部的贴图美术也要求关闭掉,这些从效果出发都可以满足,不一定完全按照固定的规则执行。
感谢贾伟昊@UWA问答社区提供了回答

精彩回答 3:之前整理过项目里面几种纹理格式的特点,其实题主大部分也都提到了,就作为一个补充贴出来吧。其中画质是我个人的肉眼感觉,没有绝对的数据比较,仅供参考。

感谢喵小逗@UWA问答社区提供了回答

精彩回答 4:能不能压缩,也需要考虑美术的感受。

最近手上的游戏 2D 序列帧用得比较多, 此时使用 ETC2 和 PVRTC 因为压缩的原因,会产生闪的问题。使用拆 Alpha 通道的方式可以一定程度上避免,特别是对于带 Alpha 通道的图,ETC2 质量低的原因大半在于对 Alpha 的压缩。

ASTC 的质量是明显好于 PVRTC 的,iPhone 上唯一的问题是 iPhone 5s 的支持。我们的方案是,如果机型不支持 ASTC,会额外准备一份低质量的 PVRTC 的资源,尺寸是原图的一半,而设计的主要目的是解决内存问题,顺带解决 iPhone 5s 的压缩问题。

感谢史亚征@UWA问答社区提供了回答。

欢迎大家转至社区交流


11、项目中策划数据如何储存和读取

因为项目中策划数据是生成二进制文件,最近发现在获取 String 值的时候消耗了大量的 Mono 堆内存(byte[] to string)。我尝试把所有的 Excel 数据转成 ScriptableObject 对象再打到 AssetBundle 中发现 AssetBundle.LoadAsset() 相比于 byte[] to string 申请 Mono 堆内存更大。请问大家项目中策划数据是如何存储和读取的,对于大量文本内容如何能尽量减小 Mono 内存的消耗?

精彩回答 1:配置表二进制,ScriptableObject, CSV 都是常用的方式,如果是 Lua 重度的游戏可能会直接把配置表转为 Lua 文件进行加载。加载配置表的时候 Mono 堆内存消耗大,一个可能是本身配置表数据量过大,再一个可能是同时加载的配置表过多,导致申请的 Mono 堆内存过大,而这个峰值上来了就降不下来了。

如果是本身配置表过大,检查下是否可以缩减一些冗余数据,再一个就是拆表,分步加载。另外就是注意在解析数据的过程中分配的内存,List,String 等的复用来减少频繁分配内存。
感谢赵林@UWA问答社区提供了回答

精彩回答 2:我们游戏用的是 Lua 文件来存储数据。确实会出现光数据就有 60MB 的内存。感觉还是使用一些 db 作为数据比较合适。用的时候去对应数据并缓存下来即可。而且一个玩家在玩的过程应该不会用到所有的数据,如果直接用 Lua 或其他文本文件,那就意味着只要用到其中一条,就把整个表都加进来了。
感谢 halm@UWA 问答社区提供了回答

精彩回答 3:
1)不要将配置表打成 AssetBundle 包,这样不仅降低加载速度,也会产生大量的 GC,毕竟有一次转化过程,而且增加了 Mono 内存;
2)少用序列化文件;
3)重点是我们要将配置表进行适时的加载,不要一次性加载,Streaming 加载部分数据,降低读取整表需要的耗时和内存消耗。

感谢郑骁@UWA问答社区提供了回答。

欢迎大家转至社区交流


12、半透明物体实现阴影效果的方法

当"RenderType" = "Transparent"的时候貌似是没有办法接受阴影的。我们需要在场景中做一层半透的地表, 来掩盖模型和地表之间的接缝,所以需要显示阴影, 请教各位大佬有什么好的方法吗?另外我想了解下为什么半透明物件默认不能接受阴影呢?

精彩回答 1:抛开 Unity 引擎,从本质上说,半透物体肯定是可以接受阴影的。之前就实现过地表有一块玻璃材质,会在玻璃上以及下面的物体上都有投射的阴影效果。所以如果自己编写的 Shader,想要接受阴影,只需要采样 Shadowmap,走一下阴影计算的过程就可以了。

对于 Unity 引擎,我试了一下的确选择 Transparent 的 RenderType 也就没有了阴影,这个我不是非常清楚是否可以自己在 Shader 中去强制走一下 Shadowmap 的处理过程来解决,题主可以试下。

但是题主要注意,半透物体接受阴影的情况下,尤其是题主要解决的这种覆盖接缝的情况,会导致这部分最后产生的阴影变得比较重的效果,因为半透下面的物体肯定还会有阴影产生,半透的阴影虽然可以受 Alpha 的影响变淡,但是叠加在一起就会出现变得更暗的问题。当然 Transparent 的部分不接受阴影,意味着阴影效果经过 Alpha Blend 之后会变得淡。
感谢贾伟昊@UWA问答社区提供了回答

精彩回答 2:这个问题恰好之前做了些尝试,正如 @ 贾伟昊所说,内置的 Transparent 是不接受阴影的,但可以尝试在自定义 shader 中强制走 Shadowmap 的。参考代码如下:

Shader "Transparent/Diffuse With Shadows"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Alpha ("Alpha", float) = 1
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase" "RenderType"="Transparent"}
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask RGB
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
            #include "AutoLight.cginc"
            struct v2f
            {
                float2 uv : TEXCOORD0;
                SHADOW_COORDS(1)
                fixed3 diff : COLOR0;
                fixed3 ambient : COLOR1;
                float4 pos : SV_POSITION;
            };
            v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                half3 worldNormal = UnityObjectToWorldNormal(v.normal);
                half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
                o.diff = nl * _LightColor0.rgb;
                o.ambient = ShadeSH9(half4(worldNormal,1));
                TRANSFER_SHADOW(o)
                return o;
            }
            sampler2D _MainTex;
            float _Alpha;
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                fixed shadow = SHADOW_ATTENUATION(i);
                fixed3 lighting = i.diff * shadow + i.ambient;
                col.rgb *= lighting;
                col.a *= _Alpha;
                return col;
            }
            ENDCG
        }
    }
}

但直接用这个 Shader 也还是没有效果…
因为文档里有句话,重点是粗体:
Only opaque objects cast and receive shadows so objects using the built-in Transparent or Particle shaders will neither cast nor receive. Generally, you can use the Transparent Cutout shaders instead for objects with “gaps” such as fences, vegetation, etc. Custom Shaders must be pixel-lit and use the Geometry render queue.

所以需要把 Render Queue 调整到 2500:

请输入图片描述

然后效果就出来了:

请输入图片描述

该回答由 UWA 提供,欢迎大家转至社区交流


回顾三年多来,我们分享了近 500 个和游戏开发、优化相关的精彩问答。这些问答不仅有具象明确的提问,也有详细周全的解答;既能供研发团队自身项目中即学即用,还能通过参与者的回答分析,帮助我们在解决问题的过程中触类旁通、鉴往知来。如果你知道答案,欢迎你来分享;如果你有疑惑,欢迎你来提问。生有涯而学无涯,期待你的加入!


↙↙↙阅读原文可查看相关链接,并与作者交流