UWA问答 | 博客 | 游戏及VR应用性能优化记录分享 | 侑虎科技 - qa
type: channel
link: https://blog.uwa4d.com/tag/qa
items: 如何让你的游戏性能转危为安？
items: 如何获取Unity导航系统中某点的高度
items: 技术分享连载（六十五）
items: 技术分享连载（六十四）
items: 技术分享连载（六十三）
items: 技术分享连载（六十二）
items: 技术分享连载（六十一）
items: 技术分享连载（六十）
items: 技术分享连载（五十九）
items: 技术分享连载（五十七）

如何让你的游戏性能转危为安？
type: item
date: 2018-11-05T05:18:00+02:00
description: 不知不觉，UWA在游戏性能优化领域耕耘了三年。我们相继推出了三项性能优化产品和服务：UWA驻场深度优化、UWA线上深度性能测评和本地性能测试工具UWA GOT （及其Online服务）。截止到目前，我们已经深度优化了近90款游戏，线上性能测评服务了2000多款游戏、VR和AR项目。 随着优化的项目和拜访的团队越来越多，我们逐渐意识到性能瓶颈不仅是技术层面所致，也来源于开发流程。在游戏项目的精品化和重度化趋势中，任何一个中间环节产生了资源浪费，对最终结果的影响都可能难以预估和弥补。所以，我们推出了UWA性能保障体系，我们认为性能优化不该局限于以前的“头疼医头、脚疼医脚”的救火行为，而应该将其扩展为质量保障体系的一环来保证研发团队更快地发现问题、解决问题，从而加快项目的研发效率。 在介绍UWA性能保障体系之前，我们先来看看项目的性能瓶颈是如何形成的，为什么它会成为多数研发团队的困扰。 项目性能在开发阶段的表现 项目的性能问题并非一开始就产生，也不是某一天突然出现的，而是在研发过程中日积月累的产物。在绝大多数的项目开发流程中，其性能耗时的走势大致如下图所示。 蓝色区域为研发期，一般为游戏立项后的功能开发和堆量阶段。在这一阶段中，功能开发是研发团队的主要目的，很少有团队在这一阶段会关注性能问题，即便是有QA的团队，这一阶段多数也是在测试功能的合理性和正确性，于是性能问题会在这里大量堆积，从0到1，再从1到100。 红色区域为上线测试和优化阶段，现在的项目研发团队多数都集中在这个阶段来进行优化，这时候主要功能已基本开发完成，后续是功能的调整、完善和调优；同时伴随大量玩家的涌入，性能问题被极速放大。卡顿、发热和崩溃问题是这一阶段开发团队和运营团队最为头疼的问题，熬夜、通宵也往往是“家常便饭”。 绿色区域是经过几轮测试和优化后，项目整体性能趋于稳定的状态。一般只要研发团队熬过了红色区域的优化阶段就可以顺利上线公测了。当然，后续也会有新版本的更新、新功能和内容的加入，所以不少项目的性能问题依然会再次涌现，即再经历前面的蓝色和红色区域，依次往复。 以上就是目前绝大多数游戏研发团队正在经历的过程，之所以会出现这种情况，其实是源于目前研发团队多数采用“瀑布流”式的开发习惯，具体如下图所示。 从预案设计，到程序开发、美术开发和玩家测试，根据反馈优化性能和调整方案，直至游戏上线。在项目内容和功能开发的过程中，虽然QA团队会对项目不断测试，但基本上都是围绕功能和数值，针对性能相关的测试寥寥无几。而随着项目的精品化程度要求越来越高、同品类竞争项目的数量越来越多，这种开发方式在性能方面的弊端暴露无疑： 问题一：性能优化时间紧、难度大 在开发流程图中的红色区域，是性能问题暴露的重灾区。这一阶段中，项目团队的制作人、策划和运营团队都在不断催促程序对性能进行优化，以期快速达到流畅的表现效果。原因很直接也很现实：玩家用户不等人。但是，短时间内修复长时间积累的大量性能问题，这其实是非常困难的，甚至可以说是一个悖论：如果研发团队拥有在短时间内就可以力挽狂澜的技术实力，那么在之前的开发过程中，也不会留下大量的性能缺口。 问题二：研发时间更长 研发团队在蓝色区域开发时，会习惯性地将问题（特别是性能问题）的解决后置，希望在后续的测试和优化阶段中集中处理，这是因为除了保证功能的研发效率之外，不少研发团队潜意识中对问题的解决持有这样的态度：同样一个问题，现在还是以后修复其实工作量都一样。这里我们想借用Scrum之父Jeff Sutherland在《SCRUM：用一半的时间做两倍的事》这本书的原话，他非常强调“问题一旦发现就立刻解决”的重要性： “几年前我在加州曾和Palm公司的开发人员聊过天。他们开发出最早一批被大家认为“个人数字助理”的产品，即现在的通讯设备。当时他们自动把工作中做过的每件事都一一加以记录。他们记录的其中一件事是，解决程序Bug要花费的时间，也就是软件开发人员在开发自己的程序导致系统出问题时，得花多久的时间解决。每一次电脑都会自动追踪这件事。 现在假设某天测试人员在试图把Matt（一位研发人员）的程序整合到系统时，却侦测到了缺陷。Matt和大多数程序开发人员一样，都不想马上回头把程序改好，而是答应事后会修改，接着就继续写新的程序去了。 在大多数企业里，这样的测试动作甚至不会在写出程序的同一天进行，可能是等到程序写出来几周、几个月后才进行测试，问题都是到了那个时候才被发现。但是，Palm公司每天都针对所有程序都进行自动测试，因此一有问题就会立刻察觉。 测试人员检测全公司每一位“Matt”们，亦即数百名开发人员，并分析马上修改程序所花费的时间，和数周后才修复程序所花费的时间有何不同。不要忘了，软件是一种颇为复杂、牵连甚广的产物，你觉得上述两种改正的方式所花费的时间会…

如何获取Unity导航系统中某点的高度
type: item
date: 2018-06-13T14:07:00+03:00
description: 本期聚集话题：如何在Unity自带导航系统中获取某一点的高度、GPU Instancing 采样不同纹理、优化Text.get_cachedTextGenerator的堆内存... 这是第115篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） 导航网格 Q：我们现在正在做基于Unity自带导航系统的寻路逻辑，但是不知道如何获得Unity自带导航模型在某一点的高度，就没法正确设置角色y值。目前想到的办法是自己生成一套Heightmap和一套导航模型，先对Heightmap进行高度查询，Heightmap查询失败再进行导航模型的射线检测。但是这么做会增加一些内存，不知道Unity有没有内置的方法去获得导航某点高度？ 1）之前都是用的NavMesh.SamplePosition，从较高的点投射线，在射线上取点作为球心调用SamplePosition。 2）还一种方法是，让美术做Mesh Collider，专门用来接收测试高度的射线。 https://docs.unity3d.com/540/Documentation/ScriptReference/NavMesh.SamplePosition.html 感谢史云柯@UWA问答社区提供了回答 之前做过类似的东西，但是没有用NavMesh，所以不敢肯定效率是否比NavMesh.SamplePosition好。 方案：在Editor中做预处理，将场景中带碰撞的对象合并成一个Mesh（因为有些不是MeshCollider，而是Box、Sphere、Capsule，这种Mesh要自己生成），然后将问题转化为射线与三角面求交的问题（对于MeshCollider的射线检测，本质上就是这个问题）。 当然这是最基本的，更进一步： 1）相邻的面如果法线相近，就合并成一个多边形，于是又转化为射线与多边形求交的问题。 2）为多边形计算AABB，先用AABB进行检测。 3）对多边形进行排序，面积大的优先进行检测。 4）未实现的：将多边形投影到XZ平面上，y值只作存储，不参与求交运算。 5）未实现的：可以尝试使用四叉树进行空间分割。 感谢凯奥斯@UWA问答社区提供了回答 以前也遇到过这个问题，我尝试过各种方式，Unity并没有给出很好的解决方案，直接调用Navmesh.SamplePosition是很容易取到错误的位置，因为它是按照球体碰撞来获取最近的点，而且这个函数随着半径增加，性能急剧下降，测试过在1.5米半径下消耗是差不多的。所以获取高度，最好还是以物理碰撞未准，那么生成这个Mesh碰撞才是关键，体质可以使用Navmesh.CalculateTriangulation这个函数去获取已经刷好的Navmesh区域，生成Mesh。可以参考： https://blog.csdn.net/huutu/article/details/52672505 感谢简简单单@UWA问答社区提供了回答 欢迎大家转至社区进行进一步交流： https://answer.uwa4d.com/question/5b0e1fec09749726e4188d4f UI Q：在UWA Mono堆内存分析报告中发现我们逻辑代码中我们发现Text.get_cachedTextGenerator 这个函数居然占用了将近6MB的堆内存，请问这里可以怎么优化？ 每个Text组件都会执行一次构造函数，即TextGenerator…ctor。图片中该堆内存较高，说明题主项目中的Text组件太多了，建议对UI界面中类似的信息进行详细排查。 此问答由UWA提供，欢迎大家转至社区进行进一步交流： https://answer.uwa4d.com/question/5b178c0d54646026e9ddd737 渲染 Q：GPU Instancing能够支持每个实例采样不同的纹理吗？我试了下类似这样的做法，但是编译不过： UNITY_DEFINE_INSTANCED_PROP(sampler2d, _MainTex) 这是不被支持的，纹理不能被用来作为instanced的属性…建议题主了解下Texture2DArray，可以通过脚本创建一个Texture2DArray，然后把Array的Index作为instanced的属性传到Shader中，这样就可以通过不同的index来采样不同的纹理了。 感谢Bunnyisrunning@UWA问答社区提供了此回答，欢迎大家转至社区进行进一步交流： https://answer.uwa4d.com/question/5b1e22f454646026…

技术分享连载（六十五）
type: item
date: 2017-05-22T16:56:00+03:00
description: 本期聚集话题：优化PutGeometryJobFence耗时、CanvasRenderer.OnTransformChanged、UGUI渲染耗时定位、Clear 开销优化... 我们将从日常技术交流中精选若干个开发相关的问题，建议阅读时间15分钟，认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们，一起探讨。 UWA QQ群：793972859 UWA 问答社区：answer.uwa4d.com 性能优化 Q1：在UWA性能报告中，看到PutGeometryJobFence开销较高，请问这个该如何进行优化？ 就目前我们所优化过的项目而言，推测PutGeometryJobFence是Unity项目的主线程在等子线程的一些网格计算操作完成，其在不同的模块中均有出现（UGUI模块、渲染模块、Mesh.Skin操作、Animator动画模块等等），出现的地方不同，其本身含义也并不相同，不能一概而论。 而就上图中的耗时来看，如果项目中使用了UGUI，那么很大几率（>80%）是由于UI模块的不合理操作所致。我们在Unity5.3~5.5版本中深度优化了多款手游，均不同程度地遇到了该值耗时较高的问题，并且该函数与WaitingForJob经常成对出现，特别是在Android平台上。 下图为一款我们深度优化的ARPG项目中，PutGeometryJobFence在游戏运行时的耗时开销。 通过现场进行定位，我们发现PutGeometryJobFence的开销，实际上是主线程在等子线程中的耗时完成的等待时间，且几乎全部是等待Canvas.GeometryJob，如下图所示。 因此，我们直觉猜测该UI模块的重建开销很高，以至于子线程的计算压力过大，而PutGeometryJobFence较高，也很可能是主线程的等待时间。为了验证这一猜测，我们开始大力定位和优化该项目的UI模块的重建开销，待调整和优化项目的UI模块后，其WaitingForJob的运行时开销大幅降低（接近于0ms），而PutGeometryJobFence的CPU耗时则基本上可以忽略不计了。 同时，需要说明的是，以上仅是我们目前在多款Unity5.3~5.5项目中的具体优化经验，但并不能说明PutGeometryJobFence耗时高就一定是由UGUI模块导致，随着Unity引擎的不断升级，其背后也会隐藏着更多的含义。因此，希望大家在优化时就具体问题进行具体分析、具体解决。 性能优化 Q2：下图中，函数"Transform.set_position"的开销很大，请问有办法优化吗？ 从图中看，是通过Deep Profiler进行的函数耗时统计，首先Deep Profiler本身对于耗时的统计影响很大，通常在Deep Profiler下调用次数越多的函数，其耗时的统计就会越明显地偏高，因此，我们首先建议关闭Deep Profiler，同时用Profiler.BeginSample/EndSample将修改Position的代码包进来，再查看其耗时，相对会更加准确。 其次，可以观察到有180次的CanvasRenderer.OnTransformChanged的回调，移动UI元素相比于移动其他的元素确实会有额外的开销，建议减少不必要的UI元素的移动，比如通过增加移动阈值，降低移动频率等等。 性能优化 Q3：Unity Profiler中的Canvas.RenderSubBatch是否属于UI上渲染的开销？和重建有关系吗？ 这个确实是UI模块（UGUI）的渲染开销，但是跟UI的重建并无太大关系，主要还是跟UI界面的渲染Draw Call相关。从上图中可以看出，UI Draw Call达到45个，该值较高，研发团队渲染对UI模块的Draw Call进行进一步优化。 性能优化 Q4：这个Clear的耗时为什么这么大，有什么可以优化的方法吗？ 图中的Clear耗时确实很高，但需要研发团队关注该高值是小概率出现，还是经常出现。如果是前者，则无需过分担心，但如果是后者，则需要研发团队关注相机图像后处理操作的使用。Clear操作一般是用来清除Camera的各种Render Buffer，当图像后处理使用的越多，则同一帧中开辟更多的Buffer的概率也越大，从而造成Clear的开销也较高。对此，如果你的项目中Clear耗时较高，且高耗时较为频繁，则建议先关闭图像后处理操作，来查看该情况是否有所好转。然后再逐步开启各个图像后处理操作，逐步定位造成Clear较高的耗时根源。 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站（answer.uwa4d.com）上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，…

技术分享连载（六十四）
type: item
date: 2017-05-16T04:33:00+03:00
description: 本期聚集话题：Shader Variant Collection、如何优化Material.SetPassFast、粒子资源的AssetBundle打包方式... 我们将从日常技术交流中精选若干个开发相关的问题，建议阅读时间15分钟，认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们，一起探讨。 UWA QQ群：465082844 UWA 问答社区：answer.uwa4d.com 资源管理 Q1：Unity 5 的 Shader Variant Collection 功能是否推荐使用？我有以下3个疑点： 1）使用 Shader 变体之后，Shader 是否还能走资源更新？抑或 Shader 不推荐走资源更新？ 2）Shader 变体和 Shader Always Include 的主要区别是什么？二者对内存和帧率影响如何？ 3）在 Unity 5 较早的版本中，Shader 变体功能似乎有一些Bug，现在是否可靠？ 经过测试，在较新的版本中（如Unity 5.5.3），将ShaderVariantCollection与Shader打包在相同的AssetBundle中后，其中会包含该Shader在ShaderVariantCollection中指定的Variant。因此： 1）使用 ShaderVariantCollection后依然可以进行资源更新（通过更新AssetBundle，来更新Shader的实现或者ShaderVariantCollection中包含的Variant）。 2）ShaderVariantCollection与Always Included Shaders的区别主要在于打包时所包含的Variant。Always Included Shaders中的Shader，其所有的Variant都会被包含，好处是，理论上不会出现Variant丢失的情况；坏处是，会导致更大的发布包以及额外的内存占用，而影响最大的是手动进行Warmup时的耗时以及ShaderLab的内存占用。因此一般来说，对于Variant特别多的Shader（如Standard Shader），并不推荐放入Always Included Shaders中。 3）目前即使是较新的版本，其可靠性我们也并不能确保，依然建议在使用前进行一些测试来验证。 此问答来自于UWA 问答社区： https://answer.uwa4d.com/question/591418087640b5a822204e73 如您对该问题仍有疑问，可以转至社区进行进一步交流。 性能优化 Q2：请问为什么我项目渲染中Material.SetPassFast的开销这么高？这个该如何优化？ Material.SetPassFast是Unity引擎在渲染过程中Material的轮循切换开销，一般在Unity5.0~Unity5.3版本中出现。它的开销主要分为两种： 1） Shader.CreateGPUProgram峰值开销 这种情况主要出现在Shader第一次渲染时。在Unity5.0以后，引擎为了避免Shader加载时过高的CPU峰值出现，已经将Shader.Parse和Shader.CreateGPUProgram两种操作分开执行，前者在Shader加载时，后者在Shader第一次渲染时。下图则为一款项目运行时渲染时的Shader.CreateGPUProgram开销。 2） 渲染状态切换开销 这种情况是几乎每一帧都发生的，有渲染的地方就会有Material的切换。从问题图中可以看出，在运行的16000帧中，Material.SetPassFast一共被调用137万次。这里可以认为几乎全部是渲染时Material的切换操作。因此，该项较高的主要原因还是材质切换操作过多所致。所以，建议研发团队在报告中的详细材质页面查看是否有过多“冗余”的材质出现，如有则尽可能降低材质的使用冗余度。 动画 Q3：Animator会把所有状态的AnimationClip加载到内存，有什么好的办法可以动态加载？ 1）Animator Controller结构不需要改变，但动画需要变化。比如随着人物等级或技能升级，同种的攻击动作随着变化等。该种需求可以通过AnimatorOverrideController来进行完成，即按需加载新的AnimationClip，然后替换AnimatorOverrideController中相应的AnimationClip即可。目前，Unity的AnimatorOverriderController不仅可以进行单个替换，同时也可以ApplyOverrides成组替换； 2）Animator Controller结构需要改变，类似于Animation老版本动画的AddC…

技术分享连载（六十三）
type: item
date: 2017-05-09T10:17:00+03:00
description: 本期我们聚集了这些话题：UI 血条制作方法比较、Unity5.6 Lightmap中渲染高光、Mono堆内存机制、动画精度优化... 我们将从日常技术交流中精选若干个开发相关的问题，建议阅读时间15分钟，认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们，一起探讨。 UWA QQ群：793972859 UWA 问答社区：answer.uwa4d.com UI Q1：我在改血条，我原来是一个Canvas里放了所有血条，后来改成每个血条一个Canvas，再改成每个血条完全不用UGUI，直接用SpriteRenderer绘制，感觉性能越来越差了，怎么办呢？ SpriteRender的使用确实非常少，目前我们对SpriteRender的合并性能也还没有进行过测试，无法分析其底层的合并机制和开销细节。这里仅对于“所有血条放一个Canvas”和“一个血条放一个Canvas”做一个比较： 1）前者的开销主要在于网格的更新，在Unity5.2之前，可以从Canvas.BuildBatch中看到，而之后则主要是在子线程中通过Timeline来查看。因此只看主线程的话，这种方法肯定是更高效的； 2）后者的开销主要在于DrawCall的数量（前者理论上能做到只用1个DrawCall，后者一个Canvas即一个DrawCall），开销被包含在了Camera.Render或者Canvas.RenderOverlays中，在中低端机型上，100个DrawCall的开销也是很明显的； 因此，在选择时，需要考虑的就是“网格更新”和“DrawCall”的权衡，比如：如果一个血条的顶点数只有8个，100个血条也才800个顶点，而800个顶点的网格更新开销是明显低于100个DrawCall的；但如果血条的顶点数很高，那么就需要考虑拆分Canvas，尽可能做到某些Canvas是间歇性静止的，从而降低一部分网格更新的开销，但如果所有的血条每帧都会移动，那么拆分Canvas的效果也不会太明显。 总之，还是建议尽可能降低血条的顶点数，然后选择前者。 图形渲染 Q2：请问下在Unity 5.6版本中如何解决预渲染缺少高光的问题？该版本中光照预渲染Directional Mode选项中少了Directional Specular选项，渲染出来的效果场景缺少高光。 现在Unity 5.6的Lighting面板如图： Unity 5.6的确是将Lightmap中的Directional Specular选项去掉了。如果需要在使用Lightmap时渲染高光，替代方案是采用Mix Lights模式下的Shadow Mask以及Distance Shadow Mask选项。即将场景中的Directional Light改成Mix Lighting类型，并且在Lighting Mode选Shadow Mask或者Distance Shadow Mask。其原理是：LIghtmap中仅仅存储indirect的光照，而direct光照是实时计算的，所以包括高光、阴影等都可以是实时的（阴影也可以是预计算好的）。这样做的好处是给Lightmap光照一定的灵活度，原来的Lightmap是完全静态的，现在是部分静态（direct的实时，indirect静态）。 此问答来自于UWA 问答社区： https://answer.uwa4d.com/question/5902db1da7fbeb4a58bf64a3 如您对该问题仍有疑问，可以转至社区进行进一步交流。 骨骼动画 Q3：我把骨骼文件的Optimize Game Objects”开启了，然后骨骼信息就没有了，那Avatar换装时候需要处理的骨骼信息怎么办？ 在开启“Optimize GameObject”选项后，因为Avatar信息消失，所以并不能通过原始的合并骨骼、合并Mesh的方法再来实现换装功能。对于开启“Optimize GameObject”选项的模型，Unity本身有另外一套更为方便的换装方式，即只要所换装模型的骨骼结点信息与Avatar自身骨骼信息可以匹配，那么直接将换装模型挂在Avatar模型下做为子节点即可，而不必再通过骨骼合并的方式来进行换装。 内存管理 Q4：Unity的Mono在内存不够的时候GC操作是即时触发的吗？比如说我当前Mono总内存是20MB，已用15MB（其中5MB可回收），然后我同步加载大量资源，需要9MB内存，Mono上限可能被撑大？ 按照问题的描述，Mono内存并不会撑大。一方面，如果加载的9MB内存是来自于资源，那么资源内存是不属于Mono内存，Mono内存是逻辑脚本所产生的堆内存。另一方面，如果这9MB是来自于堆内存，那么也未必会引起Mono上线撑大，因为Mono在堆内存不够用时，会先触发…

技术分享连载（六十二）
type: item
date: 2017-05-05T10:45:00+03:00
description: 本期聚集的话题：Static Colider.Move性能开销、耗电发热该如何优化、Resources加载和AssetBundle加载的区别、渲染模块中的Mipmap设置 我们将从日常技术交流中精选若干个开发相关的问题，建议阅读时间15分钟，认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们，一起探讨。 UWA QQ群：793972859 UWA 问答社区：answer.uwa4d.com 资源管理 Q1：在Unity 的 Profiler里，有些记录右边会有Warning的个数信息，请问这个是否影响性能，或者是否有必要修改呢？ 这种字符的出现很可能会导致后续的物理更新出现较大的性能开销，包含在Physics.Simulate/Processing中。出现这种情况主要是受限于Unity版本中的PhysX在移动静态碰撞体是开销较高的问题（虽然文档中说5.x下已经解决，但我们确实发现在5.x的项目中该项仍然存在）。因此，我们建议给需要移动的Collider加上RigidBody并勾选Is Kinematic复选框，从而将其变为动态碰撞体，如下图所示。对于不移动的物体，则直接将模型的Apply Root Motion选项进行关闭，从而直接省去Static Collider.Move的性能开销。 资源管理 Q2：我们设置了TargetFrameRate为30，想避免过高的耗电和发热，请问这样做是合理的么？ 这种设置仅在帧率本身很好的情况下，才会起到减少耗电和发热的作用。但如果本身游戏已经较为卡顿，那么该设置方法意义不大。对于耗电和发热，研发团队需从CPU、GPU和IO入手，尽可能降低这三方面的负载压力，比如： （1）CPU：针对UWA性能测评报告中的Top10函数进行优化，根据性能堆栈定位性能根源并进行优化； （2）GPU：针对报告中的OverDraw对GPU负载较大的根源进行完善和优化； （3）IO：尽可能控制资源加载的粒度和网络传输的频率。对于资源粒度的划分，我们曾有过较为深度的分析：可以参考《技术分享连载五十九》。 图形渲染 Q3：我们美术在制作场景地面的时候，会有如下图所示的非常明显的一条模糊和清晰的分割线。我清楚这是由于Mipmap的使用导致的，我们不想让这条线这么明显，初步的想法是设置Texture的mipMapbias这个参数，这个参数没有在Texture的配置界面上提供。 Q3.1：想问下Unity有没有办法全局设置这个值？或者只能像我们现在这样在贴图导入的时候由脚本来设置？官方给的这个值的建议是“Note that using large negative bias can reduce performance, so it’s not recommended to use more than -0.5 negative bias.”，按照我之前的理解，这应该对应到Mipmap偏移层数的设置，应该是个整数。 目前来说我们没有发现能全局设置的办法。 Q3.2：不清楚这个建议不超过-0.5的值具体含义是对应几层Mipmap偏移？ Mipmap的层级数的确是一个整数。mipMapbias是由Unity引擎定义的一个参数，按照Unity官网解释来看整数代表了比当前层级更低（级数更大，更模糊）的Mipmap，负数代表了比当前层级更高的Mipmap。“-0.5”具体是偏移多少层级目前也不是很清楚。 Q3.3：我们也尝试了Trilinear的Filter Mode设置，并没有明显的效果，按理说这个设置应该是会让Mipmap采样两层中的8个点进行混合，这点也有点奇怪。 Trilinear应该是会比Bilinear效果好一些，但是也差强人意。原因是这样：传统的Mipmap（不采用Anisotropic Filtering）都是每层将u，v两个方向缩减一半，即：512x512的下一层是256x256。这就导致在两层交界处在不同层采样出来的纹理在u，v两个方向都被拉伸（或者叫变模糊）。Anisotropic Filtering的方式可以简单理解为在交界处只在某一个方向上被拉伸，另一个方向保持原有采样率（或者叫纹理分辨率），这样才能明显降低突变的模糊感。 Q3.4：目前我们给美术的建议是使用Aniso Level，调整一个比较高的值，比如大于6，会有比较理想的效果，但是查了一下这个的性能消耗貌似很大，不知道UWA有没有对这个参数做过性能的分析，具体的性能消耗有多大？ 使用Anisotropic Filtering不仅纹理内存占用会增高，而且采样率也增高（因为有一个方向的保持不变），因此它比传统的mipmap更耗时，耗时在于GPU端对纹理进行采样时增加了访存，在CPU端没有影响。虽然Anisotropic Filtering 耗时…

技术分享连载（六十一）
type: item
date: 2017-04-25T06:31:00+03:00
description: 本期我们聚集了这些话题：优化Shader预加载、 MMO游戏资源规范、GC分配定位、Unity的普适性的资源管理问题... 我们将从日常技术交流中精选若干个开发相关的问题，建议阅读时间15分钟，认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们，一起探讨。 UWA QQ群：793972859 UWA 问答社区：answer.uwa4d.com 资源管理 Q1：Unity5.4.1中，我将需要的Shader打到一个AssetBundle包中（包含一个关联了所有Shader的Shader Variants），分别用Shader.WarmupAllShaders和ShaderVariantCollection.WarmUp两种方式进行预加载，后者耗时更少。 这里用了Standard Shader做的测试，可以看到真机上两种方式耗时差距是非常大的。 请问，确实是ShaderVariantCollection的效率更高，还是我测试方法有问题？ 测试代码： Shader的AB包里的内容: 根据官方的文档的描述，确实是ShaderVariantCollection的效率更高，详见： https://docs.unity3d.com/Manual/OptimizingShaderLoadTime.html 因为在ShaderVariantCollection中，是可以给每个Shader添加指定的Keyword的，ShaderVariantCollection.WarmUp的调用只会对ShaderVariantCollection中指定的Keyword进行Warmup操作；而Shader.WarmupAllShaders则是对所有的Keyword全部进行Warmup操作（其中大多数很可能都不会用到）。 因此在Shader.WarmupAllShaders的文档中也提到，建议使用ShaderVariantCollection.Warmup来进行细粒度的Warmup操作，避免大量多余的Keyword被Warmup，造成严重的卡顿，大家可以参考下文： https://docs.unity3d.com/ScriptReference/Shader.WarmupAllShaders.html 此问答来自于UWA 问答社区：https://answer.uwa4d.com/question/58fabe919db5d16c66166132 如您对该问题仍有疑问，可以转至社区进行进一步交流。 资源管理 Q2：我们在做MMO国战类游戏，因此想咨询下资源标准。我们同屏会有70~80个玩家，这种情况下主角一般控制在多少面，多少根骨骼呢？一个Boss有80多根骨骼会有问题吗？ 这个需要看研发团队希望在什么样的机型上运行。假设是低端机器类似红米Note2，那么建议玩家都控制在30根骨骼，1500面以下。如果只有一个怪物80根骨骼，问题并不大，但还需要考虑场景中其他物体的使用量。 建议研发团队看一下这篇文章，这是我们半年前统计的关于MMO游戏的性能数据，相信能对MMO游戏的开发团队具有借鉴意义：《MMO移动游戏性能分析报告：渲染、UI、逻辑代码和内存需重点关注》 此问答来自于UWA 问答社区： https://answer.uwa4d.com/question/58f614999db5d16c66166121 如您对该问题仍有疑问，可以转至社区进行进一步交流。 资源管理 Q3：我们想请教一个Unity的普适性的资源管理问题。举个例子，我们现在的一个特效Prefab包含的贴图打成AssetBundle时没有单独拆分出来，就会存在一个问题，这个特效AssetBundle会存在重复加载的问题进而导致重复的特效贴图在内存中。（针对“重复加载”说明下：我们首先通过AssetBundle出来一个Object，这个Object会缓存一段时间，在这段缓存时间过后这个Object会被Destroy掉，而需要释放的特效是通过Instantiate这个Object出来的。当这个Object过了缓存时间被destroy掉后，下次需要释放相同的特效还是通过load同样的AssetBundle进行再实例化出来，这样内存中存在两份贴图了。） Unity中是没有这种API来提供的，一般是团队中自行做一些资源的引用计数来进行管理。频繁调用UnloadUnusedAssets是不可取的（该函数的主要作用是查找并卸载不再使用的资源。游戏场景越复杂、资源越多，该函数的开销越大，一般在300~2000 ms范围内），但可以调用UnloadAsset来释放资源。 内存管理 Q4：我们游戏现在测试出来的GC是否合理？这个分配数值可以接受吗？是否存在泄露？如果存在泄露，有没有具体到每个函数的分配详情？ 研…

技术分享连载（六十）
type: item
date: 2017-04-17T13:11:00+03:00
description: 本期我们聚集了这些话题：LoadFromCacheOrDownload使用规范、编辑器下字体引用、网格的顶点色渲染... 我们将从日常技术交流中精选若干个开发相关的问题，建议阅读时间15分钟，认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们，一起探讨。 UWA QQ群：793972859 UWA 问答社区：answer.uwa4d.com 资源管理 Q1：对于WWW.LoadFromCacheOrDownload(System.String url, Int32 version)这个接口的第二个参数，我知道是做缓存资源的更新用的。我们之前在Editor模式下作了一个测试：用该函数加载一个资源，Version参数设置为1，会发现在缓存目录下生成了一个对应该资源的缓存目录，这很正常。然后我们再次用这个接口加载这个资源，Version参数设置为2，现会发在缓存目录下生成一个新的对应该资源的缓存目录，并没有覆盖旧的目录。这个情况在移动平台上是否也是这样的？ 是的，在移动平台上，通过LoadFromCacheOrDownload加载的AssetBundle，其Version版本号变化时，新的解压Data是不会覆盖旧的解压Data的。清除旧的解压Data主要有三种方式： 设置缓存的过期日期，默认情况下是150天； 调用Caching.CleanCache来全部清空缓存； 当本地Cache已满时，Unity会从最早的AssetBundle来进行自动清理。 此问答来自于UWA 问答社区： https://answer.uwa4d.com/question/58ec4adfda71c21b3b1ac91f 如您对该问题仍有疑问，可以转至社区进行进一步交流。 资源管理 Q2：我听说WWW.LoadFromCacheOrDownload(System.String url, Int32 version)这个接口加载资源，如果是Unity 4.x的版本，会有500个资源的数量限制，如果超过这个限制，Unity会删除之前缓存的每个资源，是这样吗？那在Unity 5.x 版本上是否还存在这样的问题呢？ 在Unity 4.x的版本中，如果通过LoadFromCacheOrDownload来加载AssetBundle，那么有两种情况需要考虑： 内存中加载的AssetBundle数量。在iOS平台上，通过该接口加载、同时存在于内存中的AssetBundle数量确实是有限的，接近300个，这是由于iOS上文件句柄数的限制导致。而该限制在Unity 5.0以后则被完善了，因为Unity 5引入了虚拟文件系统，所以不再有这个限制；在Android平台上，则没有这个限制，或者说数量限制值非常大，基本可以忽略。 本地Cache中缓存的AsseBundle数量。无论是iOS、Android还是PC版本，都没有500的数量限制，而是有一个硬盘占用大小限制。具体来说，在WebPlayer平台上，有50MB的缓存限制，而在其他平台上，则是4GB的缓存限制。所以只要硬盘占用大小不超过限定值即可。 此问答来自于UWA 问答社区： https://answer.uwa4d.com/question/58ec4807da71c21b3b1ac91c 如您对该问题仍有疑问，可以转至社区进行进一步交流。 资源管理 Q3：我阅读了UWA的你应该知道的AssetBundle管理机制一文，想对其中“每次加载都涉及到解压操作”的理解进行确认：对于new WWW实际是解压到WebStream。而如果是用的LoadFromCacheOrDownload，那么资源是在磁盘，所以在调用www.assetbundle时才做解压。那么有个问题，如果使用直接获取AssetBundle的那些API，解压过程是怎样的呢？CreateFromFile是在调用assetbundle.load的时候从磁盘解压，CreateFromMemory是直接解压到WebStream？ 另外，对于WebStream来说，同一个WWW对象多次调用www.assetbundle方法时，得到了栈上的多个变量，但是这些变量都指向同一个WebStream中的对象吗？ 对于第一个问题，AssetBundle的解压均发生在加载API的调用处，而不发生在www.assetbundle时，即无论是New WWW、LoadFromCacheOrDownload、LoadFromFile还是LoadFromMemory，其AssetBundle的解压均在上述API调用处。在Unity 5.3版本之前，Create/LoadFromMemory是直接解压到WebStream中，而在5.3后，则已经不存在WebStream这一说法了。具体可以查…

技术分享连载（五十九）
type: item
date: 2017-04-11T05:16:00+03:00
description: 本期我们聚集了这些话题：AssetBundle粒度规划、Android上的多线程渲染、Instantiate实例化堆内存占用、动画模块中函数的CPU占用... 我们将从日常技术交流中精选若干个开发相关的问题，建议阅读时间15分钟，认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们，一起探讨。 UWA QQ群：793972859 UWA 问答社区：answer.uwa4d.com 资源管理 Q1：关于AssetBundle（下称“AB”）拆分粒度问题。在我的项目中，我是基于逻辑以及最终的打包大小来分的，比如尽量不让压缩后的AB包超过1MB, 这是UWA之前的推荐，同时也是因为当前版本Android包的SerializedFile占用大小所考虑。经过这样的规划之后，我打包这块的代码基本是写死的， 包括手动把公共包拆成多个(公共包略大，不拆的话压缩后会有6、7MB)， 还有每个AB包的内容也是人为拆成多个。 这样虽然最后功能实现了，但个人依旧觉得不够自动化。每次新添加资源的时候， 都得走一遍这种手动流程。也研究了别人写的开源方案 ，全自动分析依赖关系，但问题是最后包打得实在太细了，不太符合科学(但还是推荐大家学习)。所以就想问下各位的公司项目是如何解决这个AB 粒度划分问题的？ 这是一个相当开放的问题，仁者见仁，智者见智。UWA目前无法总结出一个大一统的方式来建议如何进行打包，首先需要说明我们看到的两点情况： （1）没有最好的打包方式，只有最适合项目需求的打包方式； （2）无论是粗粒度打包还是细粒度打包，现在都有成功的项目在采用，这说明只要符合需求，两种打包方式都是很好的。 接下来，我们就粒度问题给出一些我们的看法： 1. AB粒度建议不宜过细，特别是一个资源一个AB。 粒度过细，一方面会导致加载IO次数过多，从而增大了硬件设备耗能和发热的压力；另一方面，在我们测试过的Unity 5.3 ~ 5.5 版本中，Android平台上在不Unload的情况下，每个AB的加载，其每个文件的SerializedFile内存占用均为512KB（远高于其他平台），所以当内存中贮存了大量AB时，其SerializedFile的内存占用将会非常巨大。同时，需要进一步说明的是，该问题已在Unity5.6中进行完善。 2. 在Unity 5.3版本之后，对于AB文件的文件大小其实不必再限定于1MB之内。 之前UWA有这一限定是出于两方面的考虑：一是New WWW在Unity 5.3之前是使用最为频繁的AB加载方式（虽然现在也是），在5.3版本之前，New WWW加载会形成一个比较大的WebStream，一般来说是压缩AB的4~5倍，会占用比较高的内存；另一方面，当AB较大（比如大于5MB）时，其加载开销会很大，由于是子线程中运行，所以真正反映到Profiler中时，大家会看到的一个“诡异”的CPU高开销——Graphics.PresentAndSync，具体原因可以看这里（ 扒一扒Profiler中这几个“占坑鬼” ）。所以，我们对此做了一些实验，发现将AB压在1MB以下是一个加载比较可以接受的情况。以上是我们之前为什么会提出1MB的主要原因。但Unity5.3之后，随着LZ4的引入，很多情况已经变化了，基于其Chunk的加载特点，AB加载很快，且内存占用要比之前小很多。所以LZ4的AB其实可以考虑更加粗粒度一些。 但是，这里仍然有以下三点注意： 1. 对于需要热更新的AB，也如问答中其他朋友的所言，要考虑实际情况控制AB的大小； 2. 即便是LZ4的AB，其加载方式不同，加载效率也可能完全不一致。以下是我们在UWA DAY 2017的分享，我们在两个不同的AB（LZ4格式）中都加载同一个资源，唯一不同的是一个AB包含10个Asset，而另一个AB包含30个资源，以下是三种不同方式从加载AB到AB.Load的耗时对比。可以看出，New WWW加载出现了明显的时间差异。因此，在Unity5.3之后，尽可能建议通过LoadFromFile(Async)来对AB进行加载。 3. 对于AB的打包，尽可能把逻辑上同时出现（一个Prefab中非Share的Asset）、小而细碎的资源（Shader、Material、粒子系统等）尽可能打包在一起，并通过LoadAll来进行加载，因为这样会带来更好的加载效率。下图为LoadAll和Load One By One的性能对比。在我们做过的实验中，LoadAll确实会带来更好的性能开销。 上述是我们建议研发团队在AB打包时的一些注意点，希望对大家的项目优化有所帮助。 此问答来自于UWA 问答社区： https://answer.uwa4d.com/question/58e5bd96e042a5c92c3484ec…

技术分享连载（五十七）
type: item
date: 2017-03-29T08:51:00+03:00
description: 本期聚集话题：CPU和GPU的性能消耗、AssetBundle 兼容性、Animator精度优化、如何卸载引用资源... 精选5个性能优化问题，建议阅读时间15分钟，认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们，一起探讨。 UWA QQ群：465082844 性能 Q1：在整体的性能消耗上，CPU和GPU各占一半合理吗？如果不是，各占多少为好？还是说需要根据机型来看？其次，我如何知道游戏在手机上的GPU消耗？Profiler是看不到的，有工具推荐吗？ 首先，CPU和GPU是并行的，也就是CPU在运算的时候GPU也在运算，一帧的结束时间是两者中比较晚结束的那个。因此，一般我们在考虑这个问题的时候，经常会说是CPU bound还是GPU bound，也就是GPU在等CPU还是CPU在等GPU。最理想的情况是两者都并行均衡，且都没有出现互相等待的情况。但在目前的大多数移动游戏中，都是CPU耗时为主要性能瓶颈。这也是为什么有多线程渲染的原因，多线程渲染就是利用CPU端的并行性，让CPU处理得更快，不拖后腿。 其次，对于GPU的压力分析，可以尝试用Intel GPA，Mali或者高通出的针对自家芯片的工具。 动画优化 Q2：UWA DAY 2017中提到“Animator优化可以把精度从8位减到3位”是什么意思？如何实现呢？ Unity的动画文件中每个关键帧数据的数值精度会达到小数点后6位以上（如下图一）。在Unity中可以通过将菜单“Edit->Project Settings->Editor”中的Asset Serialization选项设为"Force Text"获得动画文件的文本，然后通过文本处理将所有数据数值精度降低到小数点后3位（如下图二），这样可以使动画文件的大小降低。 图一 图二 以133s的动画文件为例，在不开启压缩时，其内存大小为31MB，使用Keyframe Reduction选项可以降为8MB，进一步经过降低精度后，可以降为3MB。 资源管理 Q3：我用DestroyImmediate 和 Resource.Unload 来进行卸载Prefab，在卸载前我获取Prefab下所有的材质，发现有些文件无法卸载，类似下图中的MangedStaticReferences()，怎么卸载这些资源呢？ 从图片上来看，这个是资源被逻辑代码缓存的问题。图中YangCheng纹理之所以无法卸载，是因为它被YangCheng材质所引用，而YangCheng材质又被UIAtlas脚本所引用，ManagedStaticReferences表示此资源最终是被逻辑脚本所引用，即可能某个脚本中的变量或者Container对其仍有索引。从该图来看，很可能是使用到该Texture的某个UI GameObeject没有被释放所致。建议研发团队可以检测一下具体使用YangCheng材质的索引情况，定位其被索引的原因。一般主要有两种可能，一是它自己本身被储存于脚本变量中，二是引用它的GameObject被储存于变量中。研发团队可先从以上两方面入手，对该资源进行检测和排查。 资源管理 Q4：我们项目做AssetBundle打包时，发现如果资源所依赖的C#Script的Public成员变量有变化时，用新代码加载旧的AssetBundle就会不兼容。有没有什么方法能判断这些Script在变化时是否需要重新打AssetBundle呢？目前我们使用.CS文件的MD5来判断是否需要重新打包，但其实这样应该有很多不必要的重复打包，对吗？ 该问题的本质原因是新代码的序列化信息变化所致。建议研发团队使用Unity 5的新的AssetBundle打包方式，默认情况下，Unity 5引擎会自动检测其脚本的序列化信息是否进行改变，从而自动进行增量打包。 资源管理 Q5：Unity的材质的宏是有材质用到时才会被编译吗？还是说不手动Skip都会编译？ 在Unity中shader variant也包含两种类型，一种是通过 shader_feature 定义，一种是通过 multi_compile 定义。而只有通过 shader_feature 定义的 variant 在发布时会根据其使用情况来进行剥离。具体可见Unity官方的文档：https://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html 需要注意的是，放在Always Included Shaders中的Shader，其包含的所有 variant 都不会被剥离，因此对于Standard Shader这类包含了大量 shader_feature的Shader，不推荐将其放入。 今天的分享就到这里。也欢迎热爱进步的你加入UWA的QQ群（4650828…