UWA问答 | 博客 | 游戏及VR应用性能优化记录分享 | 侑虎科技 - 优化
type: channel
link: https://blog.uwa4d.com/tag/优化/
items: UWA DAY 2025 正式启动｜十年筑基，驱动游戏未来引擎
items: Unity实时反射相关优化
items: 特效优化2：效果与性能的博弈
items: 粒子系统优化：Mesh模式下的优化策略
items: 3分钟就能掌握的视频/音频优化技巧！
items: Unity引擎UI模块知识Tree
items: 帧同步优化难点及解决方案
items: New | 分“犀”有道，方为优化“利”器！
items: Unity手游开发札记——使用Shader进行UGUI的优化
items: 使用Fast Shadow Receiver优化渲染效率

UWA DAY 2025 正式启动｜十年筑基，驱动游戏未来引擎
type: item
date: 2025-04-24T06:43:00+03:00
description: 由侑虎科技UWA主办的年度游戏开发者大会 - UWA DAY 2025，正式拉开帷幕！作为第八届盛会，也是侑虎科技成立十周年的里程碑之作，本届大会将以“十年筑基 · 驱动游戏未来引擎”为主题，于2025年8月在上海盛大举办。 从开发优化到质量保障，从前沿技术到实践落地，UWA DAY始终紧贴行业脉动，持续为开发者提供深度技术交流的平台。今年，我们将以更加开放的姿态，与广大开发者共同探讨下一阶段游戏工业的进化之路。 十年耕耘，与开发者共筑技术星河 自2015年创立以来，UWA始终坚持“快｜精准｜有温度｜值得信赖”的初心，持续深耕游戏研发工具和服务。十年来，从性能评测工具到全生命周期解决方案，从小型沙龙到全国技术峰会，我们始终与开发者并肩前行，见证并参与了中国游戏产业的不断跃迁。 UWA DAY，作为技术交流的风向标，也在这三千多个日夜中与千万开发者一起成长。站在十周年的新起点，我们愿以更加坚实的技术底座，为下一代引擎注入持续动力。 技术破界，重构未来研发生态 本届UWA DAY将围绕「引擎革新」、「工业化工具链」与「研发生态建设」三大核心主题，来自UWA及行业顶尖团队的资深技术专家，预计带来30+场高质量分享内容——从理论探索到实战落地，绘制一幅面向未来的技术图谱。 议题议程 2025年8月1日 周五 2025年8月2日 周六 议题总览 ➨十年筑基，驱动游戏未来引擎 主题演讲 张鑫 侑虎科技 CEO 随着移动游戏研发成本持续攀升与轻量化游戏生态的成熟，中国游戏行业正呈现 "品质深耕" 与 "敏捷开发" 并行的双轨发展格局。一端是3A化手游对工业化流程的极致打磨，另一端是轻量化团队对快速迭代效率的不懈追求。这种行业分化既考验着UWA技术服务的专业性与适配性，也为工具矩阵的场景化创新开辟了全新赛道。 过去一年，UWA围绕研发效率与玩家体验两大核心场景展开深度探索，在技术落地与产品迭代中实现关键突破。本次报告将重点分享三个方向的成果： GPM 2.0：玩家端体验的精准洞察。通过精准捕捉玩家的性能异常波动，为高品质游戏的长线运营提供质量保障。 GOT Online & Pipeline：工业化流程的 AI 赋能。通过与AI大模型的结合，进一步降低游戏团队对于研发工业化流程的准入门槛。 Gears：降低研发端性能检测的使用门槛。通过这一年来不断完善的检测功能，满足广大游戏团队的日常常规检测需求。 行业变革之下，UWA 始终相信，唯有扎根研发痛点的持续创新，才能在品质与效率的双螺旋中找到平衡点。本次报告将深度拆解技术落地的实战经验，期待与行业同仁共探 "工业化流程+智能化工具" 的破局路径，以十年积淀为基，为游戏未来研发注入新的动能。 ➨ UWA Gears深度体验 - 运行时性能测试与GPU深度分析新突破 张强 侑虎科技 CTO UWA Gears作为一款集成运行时性能分析与截帧渲染分析的专业测评工具，是游戏GPU性能优化的重要支撑，能够为开发者提供精准的性能数据与问题定位依据。 当前，随着游戏引擎对Vulkan API的支持日益成熟，以及移动设备的快速更新换代，Vulkan的兼容性持续改善，其在渲染效率提升、图形资源内存优化等方面的优势逐渐凸显，越来越多的研发团队开始使用Vulkan。在此背景下，UWA Gears近期实现了对Vulkan API截帧分析的支持，为开发者在Vulkan环境下的GPU性能优化提供了更强助力。 本次报告将围绕UWA Gears展开分享：一方面，详细演示如何借助该工具进行GPU性能分析；另一方面，重点展示在Vulkan环境下的重要参数解读与分析方法。通过这些内容，助力开发者更透彻地理解Vulkan特性，精准发现并高效解决GPU性能问题。 ➨ 虚幻引擎5构建开放世界下的优化、挑战以及开发方向 陈宝康 Epic Games 开发者关系工程师 现担任Epic Games开发者关系工程师，协助解决游戏公司在使用虚幻引擎中遇到的相关问题，曾先后就职于字节、腾讯，从事游戏开发多年。 随着引擎技术的不断发展，游戏引擎未来会更加关注开放世界的构建能力来带给玩家以沉浸式的体验，而Epic作为以赋能创作者和构建数字生态为核心的商业引擎，长远目标同样也是为了创建出一个元宇宙的虚拟世界，并带给玩家以身临其境的体验。每个人都可以自由的进行创作和表达，构建虚拟世界的未来，同时并将其技术赋能于游戏、影视、工业等不同领域。而在这长远目标下如何去构建开放世界成为了其中的重要一环，因为构建开放世界下的场景对游戏引擎提出了更高的要求，更大的开放世界意味着制作过程中需要面临一系列问题，本次报告与大家分享的就是虚幻引擎5针对开放世界在不同版本中主要做了哪些工作、目前的进度、面临了哪些挑战，以及未来的开发方向。主要包括：UE5构建开放世界的技术点、不同版…

Unity实时反射相关优化
type: item
date: 2022-02-09T04:35:00+02:00
description: 一．优化最终效果及消耗 8人同屏，最高画质，耗时1.1ms（高通 骁龙710）。因为项目未上线原因，只能用测试图进行相关说明。 二、原反射脚本源码 1. 源码 using UnityEngine; using System.Collections; [ExecuteInEditMode] public class MirrorReflection : MonoBehaviour { public Material m_matCopyDepth; public bool m_DisablePixelLights = true; public int m_TextureSize = 256; public float m_ClipPlaneOffset = 0.07f; public LayerMask m_ReflectLayers = -1; private Hashtable m_ReflectionCameras = new Hashtable(); // Camera -> Camera table public RenderTexture m_ReflectionTexture = null; public RenderTexture m_ReflectionDepthTexture = null; private int m_OldReflectionTextureSize = 0; private static bool s_InsideRendering = false; public void OnWillRenderObject() { if (!enabled || !GetComponent<Renderer>() || !GetComponent<Renderer>().sharedMaterial || !GetComponent<Renderer>().enabled) return; Camera cam = Camera.current; if (!cam) return; // Safeguard from recursive reflections. if (s_InsideRendering) return; s_InsideRendering = true; Camera reflectionCamera; CreateMirrorObjects(cam, out reflectionCamera); // find out the reflection plane: position and normal in world space Vector3 pos = transform.position; Vector3 normal = transform.up; // Optionally disable pixel lights for reflection int oldPixelLightCount = QualitySettings.pixelLightCount; if (m_DisablePixelLights) QualitySettings.pixelLightCount = 0; UpdateCameraModes(cam, reflectionCamera); // Render reflection // Reflect camera around reflection plane float d = -Vector3.Dot(normal, pos) - m_ClipPlaneOffset; Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d); Matrix4x4 reflection = Matrix4x4.zero; CalculateReflectionMatrix(ref reflection, reflectionPlane); Vector3 oldpos = cam.transform.position; Vector3 newpos = reflection.MultiplyPoint(oldpos); reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection; // Setup oblique projection matrix so that near plane is our reflection // plane. This way we clip everything below/above it for free. Vector4 clipPlane =…

特效优化2：效果与性能的博弈
type: item
date: 2020-12-30T04:50:00+02:00
description: 在上一期《特效优化：发现绚丽背后的质朴》中，我们主要针对UWA本地资源检测中和特效相关的知识点做了讲解。这些看似细小的知识点，很容易在大家的开发和学习过程中被疏忽，而长期的问题积累最终都会反映到项目的性能表现上。为此，我们将这些规则列出，并且以一个个知识点的形式向大家逐一解读。 本期，我们将主要面向粒子特效相关的检测规则：“特效运行时包含的ParticleSystem组件数量过多”、“特效播放时最大粒子数量过大”，“特效总贴图数量过大”，以及新增加的规则“开启各向异性过滤的纹理”。我们将力图以浅显易懂的表达，让职场萌新或优化萌新能够深入理解。 1、特效运行时包含的ParticleSystem组件数量过多 本条规则面向的核心其实是项目开发中最常见的一对矛盾：效果与性能。为了实现更为复杂、更为绚丽的展示效果，研发团队会添加各种粒子系统。在带来效果提升的同时，也会产生非常明显的性能消耗和渲染压力。所以我们需要科学地做减法，减少ParticleSystem组件数量，以降低部分特效效果的代价来达到和项目性能之间的平衡。 这里需要说明的是，规则给出的默认阈值是一个比较严苛的标准，研发团队可以根据自己项目的实际需求去设定符合现阶段性能承受范围的阈值。 2、播放特效时最大粒子数量过多 本条规则和我们之前在文章《【性能黑榜】掌握了这些规则，你已经战胜了80%的对手！》中提到的“粒子数上限超过30的粒子系统”的这条规则很相近。 合理的粒子数会使得特效的表现效果恰到好处，但过多的粒子数会给CPU和GPU的计算带来更大压力。在复杂的粒子特效下，由于大量各种粒子的存在，处理特效所带来的性能开销也会大幅度地增加。 天平的两端是特效总体效果和性能，所以我们的视角要提高到总粒子数上。在实际操作中，开发团队要尽可能精确地基于特效总体展示效果，对每一个粒子系统的最大粒子数都进行合理的规划与调整，这样才有可能在保证降低粒子特效更新开销的同时，依然能够接近或维持原有的特效效果。 3、特效总贴图数量过大 本条规则从另一个维度为开发团队提供特效相关的检测规范。贴图涉及的方面很多，在之前的文章《材质优化：如何正确处理纹理和材质的关系》和《粒子系统优化：Mesh模式下的优化策略》内我们曾简单介绍过，Shader对纹理的采样、材质对纹理的引用以及粒子系统对纹理的应用等，如果在使用时不细致，就会导致特效内的贴图资源上涨，从而带来内存和计算上的额外消耗。 所以本条规则从“贴图总数”的角度，为大家统计出这些包含贴图数过多的特效资源，以供开发团队对其进行进一步的优化，来为特效做瘦身。 4、开启各向异性过滤的纹理 在之前的文章《纹理优化：不仅仅是一张图片那么简单》中，我们对纹理的过滤模式有过介绍，简单来讲是为了降低纹理到模型表面的这个过程中发生的“失真”现象。 相比于之前提到的各种过滤模式，纹理“贴”到物体表面时，各向异性过滤多考虑了纹理贴合与视野观察角度的关系。在这个前提下，如果在U、V方向上，纹理像素对应到物体表面的关系不是1:1时，它会按比例在各方向上进行采样不同数量的点，并进行计算以得到最终的对应关系。 在纹理的过滤模式中选择Bilinear（双线性过滤）或者Trilinear（三线性过滤）时，我们就可以对Aniso Level进行调整，范围是从0到16。 当我们把摄像机调成与纹理表面成一定角度时，Aniso Level的作用就会显示出来。如下图是nisoLevel设置为0或1时的纹理的表现（即不进行各向异性过滤）： 下图是AnisoLevel设置为9时（这时候进行了各向异性过滤）的贴图情况。对比两张图就能很明显地发现视野远端的清晰程度是完全不一样的。 开启各向异性过滤，在这种要考虑视野角度的情况下，可以很显著地提升纹理的表现效果，但代价是更高的GPU性能开销。而且各向异性过滤主要适用于大面积的地面与地形设置，对场景内的物体来讲，开启相关纹理的各向异性过滤就有“杀鸡用牛刀”的浪费之嫌了，表现效果上的提升并不足以抵消或平衡由此带来的性能消耗和渲染压力。 所以本条规则筛选出这些纹理后，开发团队要根据实际情况，在充分考量使用场景、表现效果和性能消耗的多方平衡后，再去决定是否需要保留开启各向异性过滤。 希望以上这些知识点能在实际的开发过程中为大家带来帮助。需要说明的是，每一项检测规则的阈值都可以由开发团队依据自身项目的实际需求去设置合适的阈值范围，这也是本地资源检测的一大特点。同时，也欢迎大家来使用UWA推出的本地资源检测服务，可帮助大家尽早对项目建立科学的美术规范。 万行代码屹立不倒，全靠基础掌握得好！ 相关推荐 《UWA学堂训练营｜游戏自动化测试》 性能黑榜相关阅读 《那些年给性能埋过的坑，你跳了吗？》 《那些年给性能埋过的坑，你跳了吗？（第二弹）》 《掌握了这些规则，你已经战胜了…

粒子系统优化：Mesh模式下的优化策略
type: item
date: 2020-11-25T04:17:00+02:00
description: 在上一期《动画优化：关于AnimationClip的三两事》中，我们针对动画优化中的“AnimationClip的压缩方式”以及“AnimatorController挂载的AnimationState”做了解释。这些看似细小的知识点，很容易在大家的开发和学习过程中被疏忽，而长期的问题积累最终都会反映到项目的性能表现上。为此，我们将这些规则列出，并且以一个个知识点的形式向大家逐一解读。 本期，我们将面向粒子系统，讲解UWA本地资源检测中的规则：“网格发射数超过5的粒子系统”、“检测粒子系统使用的网格Read/Write选项”、“引用网格面片数超过500的粒子系统”和“引用纹理数超过5的粒子系统”。我们将力图以浅显易懂的表达，让职场萌新或优化萌新深入理解。 1、网格发射数超过5的粒子系统 关于粒子系统的定义，我们在此不再赘述。我们重点来看一下粒子系统的渲染模式。 粒子的渲染模式可分为两大类：2D的Billboard（公告牌）图形模式和Mesh模式。Billboard是3D游戏中用的非常多的一种技术，使用该模式，粒子会以一个平面的形式存在，并始终以一定的角度对着我们的镜头。举个例子来讲，场景中有一棵树，是实体存在的；但在各种Billboard模式下，你只需要看到这棵树的一个“面”。在展示效果上表现其实一样，但渲染一个“面”和渲染一整个实体，前者在消耗的内存和运算量上会更有优势。 Billboard模式，粒子会始终面向摄像机； Stretched Billboard模式下，粒子在面向摄像机的同时，会在速度的方向上进行拉伸，可以设置粒子的各种缩放和拉伸效果； Horizontal Billboard模式下，粒子平面会和XZ面平行； Vertical Billboard模式下，粒子平面在面向摄像机的同时，会在Y轴方向上直立（垂直于XZ平面）； 而“Mesh”模式就是我们今天的主题。这个模式允许粒子系统发射3D网格而非2D的Billboard，以此来实现更为复杂和贴合需求的粒子效果。 3D网格性能开销显然是高于2D的Billboard的。如果粒子系统的发射的网格数量过大，会带来较高的CPU计算开销、GPU渲染压力甚至较高的堆内存分配。所以我们对使用Mesh进行渲染的粒子系统的“最大粒子数”要求更为严苛。一般的粒子系统中，粒子发射数上限建议不超过30（我们在之前的文章中有进行讨论，详见《【性能黑榜】掌握了这些规则，你已经战胜了80%的对手！》）。经由UWA的测试结果表明：渲染模式为Mesh、网格发射数在不超过5的情况下，能够维持粒子效果和性能消耗上的一个相对平衡。 所以开发团队在本条规则执行后，需要对筛选出来的粒子系统进行显示效果和性能消耗上的平衡考量，并对相应的粒子系统进行修改。 2、所使用的网格未开启Read/Write选项的粒子系统 由于粒子系统的渲染对象从传统的粒子变成了3D网格，所以引用的网格对象本身的属性也会对整个粒子系统产生影响。在这种情况下，很多针对网格的优化建议也同样适用于Mesh模式下的粒子系统，只不过针对特定应用环境，部分优化建议需要“反着看”。 在之前的文章《网格优化：溃堤之穴，一个也不能放过》中，我们对于网格的Read/Write选项进行过一定的说明。仅仅针对网格而言，我们是不建议开启相关的读写权限。 但是在粒子系统应用中，往往需要去实现各种各样的动态效果。所以当渲染模式为Mesh时，网格就需要开启Read/Write选项以实现对3D网格的动态修改，来更好地实现相应的渲染展示效果。 Unity官方文档告诉我们，网格必须设置为Read/Write Enabled才能在粒子系统上工作。若不开启，在某些设备上运行时，可能会出现模型丢失的情况。可见问答：https://answer.uwa4d.com/question/5cf732fed27511377098284e。因此我们将该类异常的资源检测出来，希望研发团队进行规范，防止出现意外的显示结果。 3、引用网格面片数超过500的粒子系统 和上一条规则不同，这条便是网格优化在粒子系统中的正向应用。在以前文章《那些年给性能埋过的坑，你跳了吗？》中，我们对于网格的面片数有过相关的简单讲解。对3D网格而言，过多的面片数会带来内存，加载和渲染上的额外消耗。在粒子系统中也是同样如此。 在Mesh模式下，发射的3D网格对面片数的要求也是同样严苛的。所以开发团队需要在本条规则下，去检查相关粒子系统引用的网格是否存在面片数过多的情况，并视实际的表现效果和性能消耗去决定是否需要做出相关的修改。 4、引用纹理数超过5的粒子系统 在之前的文章《材质优化：如何正确处理纹理和材质的关系》中，我们简单讲了下材质的Shader中引用了过多纹理会在内存和计算上带来的影响。 同样的，在粒子系统中，如果在材质内…

3分钟就能掌握的视频/音频优化技巧！
type: item
date: 2020-10-15T07:45:00+03:00
description: 节前，我们已经将【性能黑榜】上的Top10规则均做了详细的解读（可戳文末相关链接回顾）。无论是大家在开发时的疏忽，还是相关知识点的缺失，这些问题的积累最终都会反映到项目的性能表现上。为此，我们将这些规则曝光出来，并且以一个个知识点的形式逐一解读。 今天，我们来继续剖析【UWA本地资源检测】中和音频视频相关的规则。我们将力图以浅显易懂的表达，让职场萌新或优化萌新深入理解。 1、未使用Streaming加载的长音频 这里我们先了解一下Unity里音频加载的三种方式： Decompress On Load 音频文件以压缩形式存储在磁盘上，加载时直接解压放到内存中。这种方式在内存占用上没有优势，但在后续播放时对CPU的计算需求是最小的。一般来讲这种方式更适用于短小的音频。 Compressed In Memory 音频文件以压缩形式存放在内存里，使用时再解压。这意味着会有更多的CPU开销，但是在加载速度和内存占用上具有优势。这种情况适用于大型音频文件。 Streaming 音频文件存放在磁盘中，加载时循环以下操作：“从磁盘读取一部分，解压到内存中，播放，卸载”。这种方式在内存占用上相较而言是最小的，但在CPU的消耗上是最不具优势的。 而在音频的实际运用中，背景音乐的使用是普遍存在的。几乎所有游戏都会使用BGM以增强对游戏氛围和环境的渲染。结合以上关于音频加载方式的描述可以看出，采用Streaming方式去加载背景音乐，可以有效减少内存占用和加载时间，从而降低Audio资源对项目总体内存和加载时间的影响。 因此，UWA使用这条规则，找出非Streaming加载的音频，再由开发团队依据这些音频的具体使用场合，去决定最适合的加载方式。 2、未使用PCM格式的音频 这条规则面向的是音频压缩时的编码方式。Unity支持的音频压缩方式有如下三种： PCM 全称是Pulse-Code Modulation。属于脉冲调制编码，它将模拟信号转换为数字信号，实质上没有经过编码，没有进行压缩，所以在音质上是属于完全无损的原始音频。而且相较于原生的模拟信号，它的抗干扰能力更强，保真效果更好。 Vorbis 应该叫做OGG Vorbis。类似mp3格式，但这是一种免费开发的非商业压缩格式。属于有损压缩。 ADPCM Adaptive Differential Pulse Code Modulation，自适应差分脉冲编码调制。是一种基于PCM的优化压缩方式，但也属于有损压缩。 未使用 PCM 格式的音频可能存在音质问题，所以我们将这些音频筛选出来，以供开发团队进行进一步的检查，去考虑音频格式和原音频的质量是否符合预期的使用需求。 3、双声道的音频 在导入音频文件时，我们可以发现，有些音频文件是多声道的（我们常见的是左右双声道），如下图： 在Android设备上，双声道音频对声音的效果提升不大，但是会占用双倍内存。所以建议使用单声道的音频。 而对于这些原本就是双声道的音频，可以考虑开启Force To Mono： 勾选Force To Mono的情况下，Unity会将音频合并成单声道，从而节约内存。 所以在本条规则筛选出这些双声道音频后，开发团队可以结合音频的实际表现效果去决定是否要对音频进行设置上的修改。 4、尺寸过大的视频资源 大尺寸的视频资源在表现效果上会更好，但是如果超过了目标设备群体的性能范围，那不仅提升效果有限，而且还会导致内存上的额外占用。 根据UWA对大数据的分析，在当前设备的性能范围内，512的视频尺寸能够保证在大部分的设备上达到所需的表现效果，同时不会对设备性能造成额外的压力。 需要说明的是，每一项检测规则的阈值都可以由开发团队依据自身项目的实际需求去设置合适的阈值范围，这也是本地资源检测的一大特点。同时，也欢迎大家来使用UWA推出的本地资源检测服务，可帮助大家尽早对项目建立科学的美术规范。 万行代码屹立不倒，全靠基础掌握得好！ 性能黑榜相关阅读 《那些年给性能埋过的坑，你跳了吗？》 《那些年给性能埋过的坑，你跳了吗？（第二弹）》 《掌握了这些规则，你已经战胜了80%的对手！》

Unity引擎UI模块知识Tree
type: item
date: 2020-06-11T10:53:00+03:00
description: 本文整理的内容会以UGUI为主，当然也会涉及到NGUI和FairyGUI的内容，并将UI框架和优化内容单列出来帮助大家分类学习，最后是UWA的工具篇，开发者们可以根据报告查看问题，优化项目。模块知识Tree内容会持续更新推送，本期推送在“优化”分类中新增了两篇课程《让UI性能飞起来-基于GPU实现的UI动画系统》和《UITookit下一代UI系统》。 在UWA学堂界面中的“分类“选择UI即可查看相关课程。 无论对于从业者还是正在读大学的技术萌新，学习的有效时间和个人时间都是有限的。高效学习者最在意的是时间的价值，一个需要体系化的知识地图就显得尤为重要。 UGUI 《UGUI开发基础与实践》 该课程通过对Unity引擎UGUI的基础介绍结合具体的实例制作，系统、详细地讲解了UGUI开发的各项实用知识点，主要包括：UI界面布局、自定义Shader效果、UI组件扩展、UI事件系统搭建，以及常用的性能优化方法等。 讲师会手把手带领学员掌握如何搭建高效和酷炫的3D UI系统，并在实践中理解其中涉及到的基础知识、运行机制，以及进阶技巧。 《UGUI深度研究之源码鉴赏》 从UI的代码底层讲起，通过理解底层代码的原理来认知上层的有效优化方法。这里会从源码中深入分析UI网格重建的实现原理，并完成了提取触发UI重建的UI元素的代码实现，可以更方便、更准确地定位引起UI性能问题的UI元素，从而有针对性地做优化。 而在分析Image、Text、Layout等部分源码的同时，也提出使用的注意事项。使开发者在UGUI制作及性能优化方面，更好地达到“知其然，知其所以然”的效果。 “UI开销如何优化？”、“如何定位引起重建的UI元素？”、“UGUI Text如何进行优化？”、“如何合理使用Layout？”、“在设置UI点击事件时有哪些注意事项？”等等这些常见的UI开发问题，都可以在这个专栏中得到解答。不管是行业新人，还是Unity从业者，都不妨来一起领略一番UGUI源码，相信都会有所收获。 《UGUI深度研究之优化技巧》 UI界面常常面临的性能问题是界面打开慢，在一些低端机上打开复杂UI时，可能会需要5秒以上。本篇专栏通过讲解纹理格式、纹理通道分离、纹理尺寸设置等方面，找到移动端UI纹理资源兼具效果和性能的合理格式，以此减少纹理资源加载耗时，从而减少UI的打开耗时。此外，还包括了UI制作上的常见UI制作优化思路，帮助开发者找到UI的“正确打开方式”。为了更好地在研发后期找到瓶颈UI，这里还提供了一个UI界面的静态分析工具，可以帮助开发者更直观地分析定位“问题UI”。 随着UI迭代和研发流程的变化，在UI资源上也会遇到一些不可避免的麻烦问题，本篇专栏也对这些问题进行了举例并给出了可参考的解决方案。 通过本篇专栏的学习，开发者可以更好地对移动端UI制作进行有条理的性能把控，提升UI界面的运行效率。 《Unity UGUI 渲染3D对象详解》 该课程是一个针对项目研发过程中一个场景问题的解决方案。在UI界面中渲染3D物体是一个非常普遍的需求，该课程讲解的并不是如何去渲染，而是讲解在UI界面中渲染3D物体通常会遇到哪些棘手的问题，它的原理是什么以及如何解决。通过该课程，你可以对UI界面中渲染3D物体的原理进行掌握，同时对于大多数研发团队中会碰到的问题和解决方案都会有一个更为全面的了解。 《Unity运行时动态图集的实现》 本课程讲解了Unity运行时通过动态图集来降低Draw Call的方法，从基本思路和实现原理切入，随后对动态图集的功能做了优化和扩展，附带Demo源码。不仅包含可复用的图集，还有空间利用效率更高的不可复用图集的实现。 《UGUI输入组件支持Emoji》 基本上每个游戏都会有聊天功能，策划通常也会要求我们支持Emoji的输入和展示。然而UGUI的InputField组件是不支持Emoji输入的，旧版本甚至输入Emoji还会触发exception。 本文会先说明为什么InputField不支持Emoji，这部分主要先解释一下Unicode的编码规则、计算机中Unicode的实现方式，再介绍Emoji的编码规则，以此推出InputField不支持Emoji输入的原因。 之后详细介绍一套低成本的解决方案，会逐个的说明InputField需要改写的关键点和原因，最后介绍Text组件对Emoji的渲染支持。 《详解UGUI Draw Call计算和Rebuild操作优化》 这是目前对UGUI Draw Call分析得最为透彻、全面的一篇文章。通过大量举例让大家直接明白为何UI界面中的Draw Call总是居高不下，同时让大家掌握对Draw Call、UI重建、Overdraw等高频实际问题的高效优化知识。 《Unity UI模块优化案例精讲》 对UI模块常见的…

帧同步优化难点及解决方案
type: item
date: 2018-07-19T08:44:00+03:00
description: 帧同步这部分比较复杂，细枝末节有很多优化点，也有一些不同的优化方向，根据不同项目类型、对操作手感的要求、联机玩家的个数等，会有不同的难点和痛点。不同的优化方向，优化手法的差异，可能导致一些争论。并且，帧同步，本身也有很多变种，以应对不同的需求。所以，本文一切都是基于作者的项目类型（ACT）来做的方案和优化，并不一定适合其它也需要帧同步的游戏，故在此提前说一下，以免引起一些不必要的误解。 帧同步的几个难点 帧同步的基础原理，以及和状态同步的区别，已经有很多文章介绍，我就不再赘述，大家可以自行google。以下只说几个难点。 保证客户端独自计算的正确，即一致性 帧同步的基础，是不同的客户端，基于相同的操作指令顺序，各自执行逻辑，能得到相同的效果。就如大家所知道的，在Unity引擎中，不同的调用顺序，时序，浮点数计算的偏差，容器的排序不确定性，Coroutine内写逻辑带来的不确定性，物理浮点数，随机数值带来的不确定性等等。 有些比较好解决，比如随机数值，只需要做随机种子即可。 有些需要注意代码规范，比如在帧同步的战斗中，逻辑部分不使用Coroutine，不依赖类似Dictionary等不确定顺序的容器的循环等。 还有最基础的，要通过一个统一的逻辑Tick入口，来更新整个战斗逻辑，而不是每个逻辑自己去Update。保证每次Tick都从上到下，每次执行的顺序一致。 物理方面，因为我们战斗逻辑不需要物理，碰撞都是自己做的碰撞逻辑，所以，跳过不说，这块可以参考别的文章。 最后，浮点数计算无法保证一致性，我们需要转换为定点数。关于定点数的实现，比较简单的方式是，在原来浮点数的基础上乘1000或10000，对应地方除以1000或10000，这种做法最为简单，再辅以三角函数查表，能解决一些问题，减少计算不一致的概率。但是，这种做法是治标不治本的方式，存在一些隐患（举个例子，例如一个int和一个float做乘法，如果原数值就要*1000，那最后算出来的数值，可能会非常大，有越界的风险。） 最佳的解决办法是：使用实现更加精确和严谨，并经过验证的定点数数学库，在C#上，有一个定点数的实现，Photon网络的早期版本，Truesync有一个很不错的定点数实现。 定点数的实现 其中FP，就可以完全代替Float，我们只需要将我们自己的逻辑部分，Float等改造为FP，就可以轻松解决。并且，能够很好的和我们Protobuf的序列化方式集成（注意代码中的Attribute，如下图），保证我们的配置文件，也是定点数的。 TSVector对应Vector3，只要基于FP，可以自己扩展自己的数据结构。（当然，如果用到了复杂的插件，并且不开源，那么对于定点数的改造，就会困难很多） 三角函数通过查表方式实现，保证了定点数的准确 我个人认为，这一套的实现，是优于简单的乘10000，除10000的方式。带来的坏处，可能就是计算性能略差一点点，但是我们大量测试下来，对计算性能的影响很小，应该是能胜任绝大部分项目的需求。 对于计算的不确定性，我们也有一些小的隐患，就是我们用到了Physics.Raycast来检测地面和围墙，让人物可以上下坡，走楼梯等高低不平的路，也可以有形状不规则的墙。这里会获得一个浮点数的位置，可能会导致不确定性，这里，我们用了数值截断等方式，尽量规避，经过反复测试，没有出现过不一致。但是这种方式，毕竟在逻辑上，存在隐患，更好的方式，是实现一套基于定点数的raycast机制，我们人力有限，就没时间精力去做了。这块有篇文章讲得更细致一些，大家可以参看： 帧同步：浮点精度测试 https://zhuanlan.zhihu.com/p/30422277 帧同步网络协议的实现 在处理好了基础的计算一致性问题后，我们就要考虑网络如何通信。这里，我不谈p2p方式了，我们以下谈的，都是多client，一个server的模式，server负责统一tick，并转发client的指令，通知其他client。 首先，是网络协议的选择。TCP和UDP的选择，我就不多说了，帧同步肯定要基于UDP才能保证更低的延迟。在UDP的选择上，我看网上有些文章，容易导入一个误区，即，我们是要用可靠传输的UDP，还是冗余信息的UDP。 基于可靠传输的UDP，是指在UDP上加一层封装，自己去实现丢包处理，消息序列，重传等类似TCP的消息处理方式，保证上层逻辑在处理数据包的时候，不需要考虑包的顺序，丢包等。类似的实现有Enet，KCP等。 冗余信息的UDP，是指需要上层逻辑自己处理丢包，乱序，重传等问题，底层直接用原始的UDP，或者用类似Enet的Unsequenced模式。常见的处理方式，就是两端的消息里面，带有确认帧信息，比如客户端(C)通知服务器(S)第100帧的数据，S收到后通知C，已收到C…

New | 分“犀”有道，方为优化“利”器！
type: item
date: 2018-03-02T03:17:00+02:00
description: 2018的号角已经吹响，UWA携新功能，为优化再添助力！作为UWA性能测评报告中的精髓，此次“分析与建议“模块加强了问题的分析力度，它能将数据所反映的表象一层层抽丝剥茧，逐步定位到更里层阶段。 目前我们已将其部署到所有测评过的项目中，用户登录官网，在项目测评报告的“分析与建议”中即可查看。 我们以渲染模块为例，上图为最新版本报告中的渲染模块界面。通过半透明渲染的耗时，UWA定位到问题瓶颈在于UI、粒子或者其他半透明物体的耗时，并分别对于这些瓶颈提供了不同的优化建议，大大缩减了研发团队进一步排查和试错的时间。 上图为某个项目的性能评测报告的内存模块页面，在“分析与建议”一栏中，我们详细解释了Unity项目的运行内存的构成，如资源内存、Mono以及其他内存等，并就当前测试项目的以上各内存分别给出优化建议，让大家在得到解决方案的同时，对引擎本身有个更系统深入的了解，让每一次优化，都能成为技术知识温故更新的过程。 不同的项目所展现的性能问题各不相同，需要的优化方案也大相径庭。UWA凭借多年丰富的实战经验，将沉淀过后的技术积累通过不断的测试验证，转换成UWA性能测评中可量化的上千种参考标准，为研发团队的项目提供定制化的解决方案，并力求检测速度更快捷、分析内容更全面、 解决问题更精准。 目前，UWA支持过的项目类型已经覆盖到市面上大部分的主流类型如MOBA、FPS、MMO、沙盒、ACT、ARPG、吃鸡、音乐、电竞、TPS、SLG等，更有不少精品游戏在近两年的游戏市场中取得了傲人的成绩，如当下备受追捧的《恋与制作人》，日益渐兴的沙盒类手游《创造与魔法》，挑战极致性能的重度MOBA手游《小米超神》等。UWA非常荣幸能见证它们荣耀背后，极具匠心地将产品反复打磨的历程。在我们看来，这些成就更多是必然。 UWA的合作案例 种一棵树最好的时间是十年前，其次是现在。优化亦是如此，因为时间不等你。 不是所有的分析都能一针见血， 不是所有的优化都能对症下药。 UWA，优化有“道”。 UWA会员功能一览

Unity手游开发札记——使用Shader进行UGUI的优化
type: item
date: 2018-02-07T14:26:00+02:00
description: UI优化是个极其繁琐且考验功底的部分。笔者整理了之前工作中基于Shader来进行优化的思路和方法，以及自己构建的、代替UGUI提供的通用控件的组件，包括小地图、Mask的使用、进度条、基于DoTween的动画效果优化等，具有极高的参考意义。 一、前言 近期断断续续地做了一些优化的工作，包括资源加载、UI优化、效果分级等各个方面。优化本身是一件琐碎且耗神的事情，需要经历问题定位、原因探查、优化方案设计和实现、效果验证、资源修改多个步骤，也会涉及到各个职位之间的配合和协调。在这其中，可能带来较大工作量的是对于之前普遍使用的一些方法/控件的优化，如果无法兼容之前的使用接口，可能会给美术和程序带来较大的迭代工作量。 UI是这其中可能越早发现问题收益越高的一块内容，所以我整理了这段时间做的一些基于Shader来进行优化的思路和方法，以及会分享自己构建的、代替UGUI提供的通用控件的那些Component，希望在项目中前期的同学可以提前发现类似的问题，并尽早进行改善。 二、优化目标 UGUI已经提供了非常丰富的控件为我们使用，但是出于通用性的考虑，其中很多控件的性能和效果都可能存在一些问题，又或者在频繁更改UI数值的需求下会引发持续的Mesh重建导致CPU的消耗。我们期望通过一些简单的Shader逻辑来提升效果或者提高效率，主要会集中在如下几个方面： 降低Draw Call 减少Overdraw 减少UI Mesh重建次数和范围 接下来的内容，我们就来分享如何使用简单的Shader进行UGUI优化的过程。 三、小地图 在我们游戏中，玩家移动的时候右上角会始终有小地图的显示，这个功能在最初的实现方案中是使用UGUI的Mask组件来做的，给了一个方形的Mask组件，然后根据玩家位置计算出地图左下角的位置进行移动。这种实现方式虽然简单，但是会有两个问题： Overdraw特别大，几乎很多时候会有整个屏幕的Overdraw； 玩家在移动过程中，因为一直在持续移动图片的位置（做了适当的降频处理），所以会一直有UI的Mesh重建过程。 当时的Prefab已经被修改了，我简单模拟了使用Mask的方法带来的Overdraw的效果，如下图所示： 使用Mask组件带来的Overdraw的问题 在上图中可以看到，左侧是小地图在屏幕中的效果，右侧是选择Overdraw视图之后的效果，整张图片都会有一个绘制的过程，占据几乎整个屏幕（白框），而且Mask也是需要一次绘制过程，这样就是两个Draw Call。其实这里UI同学为了表现品质感，在小地图上又蒙了一层半透的外框效果，消耗更大一些。 针对这一问题，首先对于矩形的地图，可以使用运行效率更高一些的RectMask2D组件，但这并不能有本质的提升，解决OverDraw最根本的方法还是不要绘制那么大的贴图，然后通过蒙版或者Clip的方式去掉，这是很浪费的方法。有基本Shader概念的朋友应该可以想到修改UV的方法，这也是我们采用的方法：思路很简单，就做一个和需要显示大小一样的RawImage控件，然后赋给它一个特殊的材质，在vs里面修改要显示的区域的UV，就可以做到想要的效果。 直接贴出来Shader代码如下： sampler2D _MainTex; fixed4 _UVScaleOffset; sampler2D _BlendTexture; v2f vert(appdata_t IN) { v2f OUT; OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex); OUT.texcoord = IN.texcoord; //计算uv偏移 OUT.offsetcoord.xy = OUT.texcoord.xy * _UVScaleOffset.zw + _UVScaleOffset.xy; #ifdef UNITY_HALF_TEXEL_OFFSET OUT.vertex.xy -= (_ScreenParams.zw-1.0); #endif return OUT; } fixed4 frag(v2f IN) : SV_Target { half4 color = tex2D(_MainTex, IN.offsetcoord); half4 blendColor = tex2D(_BlendTexture, IN.texcoord); color.rgb = blendColor.rgb + color.rgb * (1 - blendColor.a); return color; 核心的代码就只有加粗的那一句，给UV一个整体的缩放之后再加上左下角的偏移。之后C#逻辑就只需要根据地图的大小和玩家所在的位置，计算出想要显示的UV缩放和偏移值就可以了。玩家移动的时候只需要修改材质的…

使用Fast Shadow Receiver优化渲染效率
type: item
date: 2017-12-27T03:43:00+02:00
description: 笔者利用Fast Shadow Receiver插件，通过CPU的实时计算来换取GPU的渲染性能的方案，解决了场景静态合批被动态阴影打断的问题，大大提升了游戏在中配下的帧率。本文详细记录了笔者从熟悉、应用测试到反复修改的过程，极具参考意义。 一、起因 关于Unity中的动态阴影，已经有挺多帖子聊过这个话题了，比如这篇《Unity移动端动态阴影总结》，还有钱康来博客里的这篇《利用Projector实现动态阴影》和《Planar Shadow》等等。 无论是最简单的基于Planar投影的方案还是稍微“老式”一些的Projector的方案，乃至目前比较主流的ShadowMap的方案其实都各有优劣和对应的应用场景，它们之间的原理和差异不是本文的重点，有兴趣的同学也可以很容易地找到相关的论文或者博客来看。 我们项目本着不要重复造轮子的想法，一直坚持使用Unity原生的ShadowMap的方案来做动态阴影。而且UWA也做过一些动态阴影方案效率的对比，自己的轮子能做得比有源码的官方好的并不多，更何况我们这种地表有起伏，高配需要支持多角色动态阴影的“大型”MMORPG游戏，ShadowMap已经是最适合的方案了。 然而！人生总会有然而！大约1个多月前，我发现了这个问题—《Unity中静态合批与ShadowMap的宏设置冲突问题》，简单来说，静态合批首先对场景物体进行了排序，保证结果正确，但是当引入了动态阴影之后，会去修改物体接受阴影的宏（这也是一种优化，因为有采样和阴影计算的消耗，所以关闭掉宏着色器的效率更高），导致原本排序好的物体无法正常进行合批，因为着色器的宏不一样了，从而导致之前静态合批之后理论上可以做到很低的Batch数值增加了很多，使得场景渲染的效率大幅下降。 在想清楚这个问题的原因之后，在依然想要使用Unity的ShadowMap的前提下感觉是没有什么特别简单的优化方案的，于是就暂时搁置下来，直到上周的时候对游戏各个效果对于帧率的影响在真机上做了一个定量的测试之后，才发现问题远比想象中的严重…… 各个效果对于帧率影响的定量测试结果 上面的测试是在中配机型小米Max2上进行的，可以看出阴影的开关与否导致一帧的时间消耗有9.5ms左右的差异，是所有效果中影响最大的！而ShadowMap自身渲染消耗不应该有这么大的差异才对，观察了下Batch数量的差异，单纯场景的Batch数量大约会从25增加到150左右，这有点超出我们之前制定的美术规范了。 在中配效果下，我们只有主角自己开启了动态阴影，因此最初的一个想法就是引入另外一套阴影绘制方案，比如Dynamic Shadow Projector，来专门针对主角进行阴影的绘制。虽然我个人很不喜欢同时使用两套技术方案，但目前看起来这似乎是在不降低效果的前提下唯一的选择了。 二、Dynamic Shadow Projector插件 This simple Unity asset provides a few components to render a shadow onto a render texture so that the render texture can be used with Blob Shadow Projector. Blob Shadow Projector is usually used for dropping a round blurry shadow which is not suitable for a skinned mesh object. This asset enables a projector to drop a dynamic shadow which is perfect for skinned mesh objects. Dynamic Shadow Projector插件的原理比较简单，将角色的阴影绘制到一张rt上，然后使用Unity的Projector组件将这张rt作为绘制输入，再绘制一遍接受阴影的物体。阴影的rt是每帧更新的，也就做到了可以让带有动画的角色阴影是实时变化的。 试用了一下，还是比较简单易上手的，几个组件正确设置之后就可以看到效果了，由于是针对单个角色的，因此使用比较小的rt就可以做到比ShadowMap更加精细的效果，但是如果想让一个Projector处理多个角色，一旦扩大Projector的范围，阴影效果质量的下降就比ShadowMap的方法还要厉害。 128×128的rt只投影一个Cube的情况下rt的使用率和阴影质量 512×512的rt投影三个Cube的情况下rt的使用率和阴影质量 上面两张图分别给出了模拟使用一个Projector针对单个角色进行投影和多个角色进行投影的效果对比图，在下面的那张图中，三个Cube的距离…