UWA问答 | 博客 | 游戏及VR应用性能优化记录分享 | 侑虎科技 - ugui
type: channel
link: https://blog.uwa4d.com/tag/ugui/
items: 如何定位游戏发热问题
items: 如何降低无效的物理开销
items: 如何动态获取URP设置里自定义的RenderFeatures
items: AssetBundle加载的TMP字体材质赋值失败
items: ToLua或XLua中的虚拟机是否独立于Unity的主线程
items: Shader Graph和Amplify Shader Editor有哪些区别
items: 2D项目大量物品图标Draw Call优化方案
items: Timeline技能编辑器如何提取关键帧信息
items: 高通芯片GPU是否有类似于HSR功能
items: 子线程GC导致主线程函数耗时较高的问题

如何定位游戏发热问题
type: item
date: 2022-06-08T10:10:00+03:00
description: 1）如何定位游戏发热问题 ​2）Unity获取指定脚本的引用对象 3）如何知道打包时的一个Shader有多少变体 4）如何优化Font.CacheFontForText频繁造成的耗时峰值 这是第300篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Performance Q：目前项目的发热问题很头疼，2D游戏，基于TileMap、SpriteRenderer和UGUI的渲染，关闭了垂直同步，TargetFrameRate设置为60。Android和iOS上发热都很严重，而且在比较好的机型（比如iPhone 12这种），发热现象甚至更明显。 跟大部分情况不同，发热并没有怎么影响帧率，在大部分机型上，帧率都不是问题，连iPhone 8机型，都能60帧跑满。 在Unity Profile和Xcode都进行过性能分析，CPU最明显的热点函数就是Spine的骨骼动画更新计算。然而到一个没有骨骼动画的场景，发热现象稍好点，但还是比预期的要烫不少（场景中除了地面和一些静态贴图，基本就没有多少东西）。 另一个比较普遍的发热点是网络测试，在某场景关闭网络后，发热依然严重。甚至启动游戏，停留在登录界面一会儿，发热现象都比别的游戏更明显。 猜测是否由于每帧的顶点数量过多造成的，在游戏中Unity的Status面板，顶点数量Verts达到了40KB，三角形Tris也有几乎20KB。看起来很多，但我不太清楚当前主流游戏这个数值的级别大概是多少。而且，顶点和三角形的数量很难解释登录界面依然容易发热，毕竟登录界面这些数值不可能很高。 自己也做了很多测试了，实在搞不清楚问题究竟在哪儿，Unity是还有什么特别需要优化的，针对发热的点吗？ A1：可以用Xcode抓帧看看带宽，Load Store Action是否合理。 感谢littlesome@UWA问答社区提供了回答 A2：以下是我的建议： iPhone 8上跑满60帧，证明CPU、GPU都没有到达瓶颈，消耗在较为合理的范围。推荐在Unity Profiler看一下CPU端的消耗，以及查看一下DrawCall数量。 推荐使用FrameDebugger，看一下是否有冗余的物体或者后处理在渲染，尤其是你说的启动界面有发热。 如果低级错误都排查过了，那么建议看一下是否用了原生的插件。 渲染的分辨率是否调整过了，RenderScale的值和FrameDebugger可以查出来分辨率。 感谢张振东@UWA问答社区提供了回答 A3：关于发热的问题，通常要从几个角度排查：CPU压力（耗时）、GPU压力（耗时和带宽，可以考虑降低分辨率看看发热问题是否会有改善）和IO等几个角度。从题主的问题上看，耗时应该是没问题，都能跑满帧（当然60帧本身就是对发热影响比较大的一点，可以看看限制30帧会不会发热有下降），所以要看看一些隐形的东西是否有问题。比如带宽，可以用Snapdragon在高通手机上跑一跑。如果带宽较高，看看纹理的一些设置是否合理，比如是否压缩、是否开启Mipmap，这两项通常都是需要设置成开启的。还可以查看是否有不必要的BlitCopy操作，在URP项目中比较容易出现Copy Color和Copy Depth浪费。对于IO，需要看看是否存在子线程里面有频繁IO的现象。 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62910e1ab87a457351669b0c Script Q：Unity获取指定脚本的引用对象：一个GameObject上挂载了一个Script，这个Script引用了很多资源。如何只获得这个Script引用的资源呢？AssetDatabase是会获取所有的引用，但是并没有做区分。 A1：有Guid可以使用以下代码加载： var assetPath = AssetDatabase.GUIDToAssetPath(guid); var texture2D = AssetDatabase.LoadAssetAtPath(assetPath); 感谢萧小俊@UWA问答社区提供了回答 A2：通过SerializedObject拿到了所有的ObjectReference，然后AssetDatabase.GetAssetPath获取对应路径： var assetObj = AssetDatabase.LoadAssetAtPath<Object>(path); if (assetObj != null) { GameObject gameObj = ass…

如何降低无效的物理开销
type: item
date: 2022-05-24T04:09:00+03:00
description: 1）如何降低无效的物理开销 ​2）EventSystem.Update如何优化比较合适 3）如何定位UWA报告中检测到的Standard Shader问题 4）如何定位在UWA AssetBundle检测中看到n/a的网格问题 这是第298篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Physics Q：如果Unity没有用到物理模块部分，如何将这部分关闭？以及是否可以动态开关？ A1：以下供参考： Physics.autoSimulation = false; Physics.autoSyncTransforms = false;//不用NGUI或者Raycast可关闭 感谢萧小俊@UWA问答社区提供了回答 A2：可以选择动态关闭，也可以直接关闭Physics设置里面的Auto Simulation，如果用到射线检测或者NGUI，需要把下面的Auto Sync Transforms勾选。 感谢小ben@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6285b2a7b87a4573515d6387 UGUI Q：请问EventSystem的优化怎么下手比较合适？对EventSystem的性能优化有何建议？ A1：EventSystem的优化可以参考这篇文章：https://networm.me/2019/10/06/unity-ugui-eventsystem-optimization/ 感谢小ben@UWA问答社区提供了回答 A2：规划一下Canvas，只有需要检测才需要挂上GraphicRaycaster组件。然后EventSystem中的大部分性能可能是点击之后触发的事件造成的，并不能算EventSystem的，需要单独看一下触发的代码。 感谢萧小俊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6285b420b87a4573515d64fb Shader Q：请问在UWA报告中看到Standard Shader，该如何定位问题？ A：Standard Shader出现的原因，一般是导入的FBX模型中或者Unity自身生成的一些3D对象使用了自带的Default Material，从而依赖了Standard Shader。可以结合UWA在线AssetBundle检测工具排查是哪个AssetBundle包中哪些资源引用了Standard Shader，如下图： 在找到了是哪个FBX资源带有Default Material后，可以用Unity提供的AssetPostprocesser类的回调函数对其进行剔除，如下图，是一个简单的做法。 感谢Faust@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/628b39e3b87a45735161a84a AssetBundle Q：在UWA 在线AssetBundle检测结果中看到n/a的网格，请问如何定位问题？ A：可以尝试在Asset Studio中查看相关AssetBundle的具体资源。 一般AssetBundle中的资源都是已命名的，检测到的未命名的资源通常是脚本生成的Asset中的一部分。 可以用Asset Studio查看这些未命名资源和哪些Asset相关，进而加以定位。 感谢宗卉轩@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/628b3807b87a45735161a5dc 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）

如何动态获取URP设置里自定义的RenderFeatures
type: item
date: 2022-04-26T05:37:00+03:00
description: 1）如何动态获取URP设置里自定义的RenderFeatures ​2）UGUI里，把UI丢回池里，把方案SetActive(false)方法的最佳 3）Prefab中m_Text会自动换行导致搜索不到 4）URP变体优化shader_feature，MAIN_LIGHT_SHADOWS关键字废弃 这是第295篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Script Q：我们在URP项目中自定义了多个RenderFeatures去实现游戏效果，现在想动态开启和关闭相关的RenderFeatures，请问怎么在代码里动态获取这些Features呢？ A：我们使用反射获取，供参考： public static class ScriptableRendererExtension { private static readonly Dictionary<ScriptableRenderer, Dictionary<string, ScriptableRendererFeature>> s_renderFeatures = new Dictionary<ScriptableRenderer, Dictionary<string, ScriptableRendererFeature>>(); public static ScriptableRendererFeature GetRendererFeature(this ScriptableRenderer renderer, string name) { if (!s_renderFeatures.TryGetValue(renderer, out var innerFeatures)) { var propertyInfo = renderer.GetType().GetProperty("rendererFeatures", BindingFlags.Instance | BindingFlags.NonPublic); List<ScriptableRendererFeature> rendererFeatures = (List<ScriptableRendererFeature>)propertyInfo?.GetValue(renderer); if (rendererFeatures == null) { s_renderFeatures[renderer] = null; } else { innerFeatures = new Dictionary<string, ScriptableRendererFeature>(); for (var i = 0; i < rendererFeatures.Count; i++) { var feature = rendererFeatures[i]; innerFeatures[feature.name] = feature; } s_renderFeatures[renderer] = innerFeatures; } } if (innerFeatures != null) { innerFeatures.TryGetValue(name, out var result); return result; } return null; } } 感谢yqsas@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/625d1d50a43e6117d06de3b8 UGUI Q：想请教下在UGUI里，把UI扔回池里，取代SetActive(false)这种低效方法的最佳方案是什么呢？具体应当怎样操作？ A1：可以尝试移到屏幕外的一个池子里，不SetActive(false)，SetScale=0，Disable一些逻辑脚本，移回去的时候做相反的操作。配合一些数值初始化的操作，可以减少UI重建的耗时。 感谢小ben@UWA问答社区提供了回答 A2：可以使用Canvas Group，改变Alpha来代替SetActive()，不会触发UI重建，我们有些项目已经使用这样的方式，效果还不错。 感谢廖武兴@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/625fd559a43e6117d071a58a Script Q：在Prefab中的m_Text会自动换行，导致完全匹配搜索的时候搜索不到，这应该怎么解决？ A：如果使用类似文本读…

AssetBundle加载的TMP字体材质赋值失败
type: item
date: 2022-04-13T04:55:00+03:00
description: 1）AssetBundle加载的TMP字体材质赋值失败 ​2）资源打包关系依赖树 3）Shader中设置ColorMask后，最终输出颜色的计算 4）Time.deltaTime和Time.unscaledDeltaTime值不一致 这是第293篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） UGUI Q：从AssetBundle加载的TMP字体材质，为什么在代码中设置为TMP组件的材质会失败？ 使用Resources.Load加载材质赋值是可以的，但是AssetBundle加载代码赋材质却行不通。这里是把材质和字体都打了AssetBundle，TMP相关其他的没有打AssetBundle，走的TMP本身的Resources.Load加载方式。 从AssetBundle中LoadAsset后用一变量存了材质，然后所有text的材质赋值都访问了这个变量。 A：TMP字体内部默认用的Resources.Load的方式加载资源，建议修改TMP源码，把所有Resources.Load加载资源的方式代理成自己的。 我们的方案是： 在非AssetBundle模式下用它自身的加载方式。 AssetBundle模式下，就会由框架的资源加载代理。 感谢1 9 7 3-311135@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/624d62aba43e6117d057f4b4 AssetBundle Q：想做包体资源分析，大家有什么好的树显示工具或者思路推荐吗？有比较好的开源方案也可以。最简单就像N叉树一样，比如root一个文件名，然后展开整个树结构。 A1：我自己做了一个，供参考。都是用Unity自己的IMGUI最基本的接口去实现。 EditorWindows GUI.Box GUI.BeginGroup GUI.Label Handles.DrawBezier Handles.DrawWireDisc TreeView 基本上，组织好各个AssetBundle的依赖关系其实是很好呈现的。 感谢黄程@UWA问答社区提供了回答 A2：推荐一款比较好用的插件，不止有依赖树，还有其他打包的资源数据可供分析： https://assetstore.unity.com/packages/tools/utilities/build-report-tool-8162 感谢郑骁@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/623c8322a43e6117d040ae9b Shader Q：场景中有一个相机，ClearFlags为SolidColor，颜色设置为Blue（0,0,1,1）。 另外创建了个Cube，使用 一个最普通的vf shader，设置的输出颜色是Red，即fixed4(1,0,0,1)；使用默认的ColorMask设置则会输出红色Cube，但是若设置了ColorMask R，最终Cube的颜色显示的是Blue和Red混合出来的，这一块有什么文档可以参考吗？ 附Shader： Shader "Custom/ColorMask" { Properties { } SubShader { Tags { "RenderType"="Opaque" } Pass { ColorMask R CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { return fixed4(1, 0, 0, 1); } ENDCG } } } A1：猜测ColorMask R应该是只写入当前R通道的颜色，但是GB仍然会从摄像机的Background值获取。 感谢Knight-132872@UWA问答社区提供了回答 A2：Background的颜色是每帧渲染其他对象之前要对ColorBuffer设置的颜色，所以在渲染Cube之前，Color Bu…

ToLua或XLua中的虚拟机是否独立于Unity的主线程
type: item
date: 2022-03-23T06:28:00+02:00
description: 1）ToLua或XLua中的虚拟机是否独立于Unity的主线程 ​2）Timeline技能编辑器提取关键帧信息 3）Canvas AdditionalShaderChannels设置无效 4）SDK返回的时候取Time.deltaTime比较大 这是第291篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Lua Q：ToLua或XLua中的虚拟机是否独立于Unity的主线程？ A：Lua的虚拟机有自己的堆、栈内存，虚拟机的更新需要有外部的线程通过函数调用来驱动，而Lua虚拟机目前只支持单个线程来驱动。在实际项目中，因为Lua中要访问Unity的API，所以基本上都是通过主线程来驱动。 但实际上，如果不需要访问Unity API，完全是可以放在子线程里去驱动的。 该回答由UWA提供，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61af4160d8413e18eb3d94d5 Timeline Q：最近在用Unity做一个技能编辑器，让策划可以在Timeline编辑器里编辑技能动画、施法特效、受击动画、受击特效和播放声音等等。编辑这些都没什么问题，我想根据编辑好的Timeline导出一系列行为节点，在Editor的环境下导出成配置（包含所有行为节点）。现在我不清楚怎么获取Timeline里第m帧开始某动画x，第n帧结束这些行为。同时怎么能给动画打上对应Tag？ A1：Timeline的组成如下图： 每个Track对应一个TrackAsset其实就是一个PlayableAsset，整个Timeline也是一个TimelineAsset，里面存了所有Track的数据。 对于每个Clip的开始和结束时间在TimelineClip中可以获取。 感谢羽飞@UWA问答社区提供了回答 A2：我把基于Timeline的编辑器小Demo共享上来。 技能编辑器Demo。 以上是本人摸索着写的一个简单技能编辑器Demo，基于Timeline的。 感谢题主右前锋@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61c00a17d8413e18eb546b3b UGUI Q：在UI中使用到的Shader中用到了UV1和UV2通道，在Canvas AdditionalShaderChannels中选择添加了对应通道后没有效果，相同代码资源在2019.4.26f1c1正确显示，请教大家是否是版本问题还是需要特殊设置处理？ Unity版本：2018.4.17f1 Unity版本：2019.4.26f1c1 A：搜索了一下Unity Release Note，在Unity 2019.2.0版本中发现Unity修复了这个问题。修复后，AddVert会把UV2的数据和UV3的数据也拷贝进去。没修复前，UV2的数据从RenderDoc里面看是0。 测试了一下，确实在2019.1.14版本还是有问题的，下一个版本2019.2.0就没问题了。 在2018.4.23f1版本上参考这个链接的Outline效果进行了测试，在小米9真机上没有发现问题，题主可以参考一下： https://www.1024sou.com/article/2307.html PS：看了2018.4.18到2018.4.23之间的所有的Release Note，没有发现相关问题的修复。这个文章是使用Shader来实现Outline的，感觉效果挺不错。 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6228644a80139e0c1f1f661a Script Q：nity调用某SDK进行登录，SDK返回的时候取Time.deltaTime比较大（就是从调用SDK开始到SDK返回的时间），这种情况可能导致我在Update里的逻辑经历了很长的一帧。 A1：直接点就是判断Time.deltaTime大于某个阈值时直接Return。 感谢碎心客@UWA问答社区提供了回答 A2：Time.deltaTime比较大也就是说登录操作的耗时很高，而且是一个同步调用，确实会导致卡顿问题（在Update里的逻辑经历了很长的一帧）。 一般来说，网络请求等操作应该尽可能通过异步调用来实现，如果是SDK的登录接口没有提供异步方法，建议先尝试通过子线程（new Thread）来调用，然后主线程上开个协程每帧轮询下结果。 该回答由UWA提供，欢迎大家转至社区交…

Shader Graph和Amplify Shader Editor有哪些区别
type: item
date: 2022-03-23T06:09:00+02:00
description: 1）Shader Graph和Amplify Shader Editor有哪些区别 ​2）Canvas AdditionalShaderChannels设置无效 3）IL2CPP打包速率问题 4）SRP是否还需要图集 这是第290篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Shader Q：请教各位，Unity自带的Shader Graph或Amplify Shader Editor，在使用和生成的Shader性能问题上，除了Amplify Shader Editor目前自带Node功能的丰富度以及社区资源丰富度应该更胜一筹外，两者还有多少区别？ 项目背景：新项目（手游），基于Unity URP的二次元NPR+PBR，3D场景+3D角色。 A1：参考如下： 体验：Amplify Shader Editor优 使用：Amplify Shader Editor优 通用程度：Amplify Shader Editor优 兼容性：Shader Graph优 未来发展：Shader Graph优 结论：两个都用并不冲突，但是我倾向于优先使用Shader Graph慢慢替代Amplify Shader Editor。 感谢大崔-287520@UWA问答社区提供了回答 A2：实际体验上Amplify Shader Editor会比Shader Graph好用很多。 Amplify Shader Editor生成的是明文Shader文件，可以自行修改调整部分不合理的生成；Shader Graph也能看到他生成的Shader，但是设计上Unity尽量希望黑盒，不想让你看到。 Amplify Shader Editor可以几条管线互相切，Shader Graph只能在URP下使用。 Amplify Shader Editor兼容所有Shader Graph的节点和参数。 Shader Graph界面比Amplify Shader Editor好看，品控比较好，小bug没Amplify Shader Editor多。 每一步计算的节点结果，Shader Graph能预览基本所有的，Amplify Shader Editor如果是自己定义的管线全局参数以及有些内置的，不太好预览。 连连看这种生成的Shader质量都很差，基本需要效率的都得翻新一遍。 常见的优化，比如c=tex2d(uv) 紧接着使用c，是需要等待GPU操作显存回来的时间消耗。 这种一般会插入其他计算，抵消这个消耗。 比如： c = tex2d(uv) //do other thing c = c*c; 目前两种都是按节点逻辑生成，一个uv节点就会生成tex2d(uv)和后续计算的逻辑。 没有整体做语义分析的优化。 感谢uwa菜鸡奶泡泡.@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/620c9123e7172c2775ea8f00 UGUI Q：在UI中使用到的Shader中用到了uv1和uv2通道，在Canvas AdditionalShaderChannels中选择添加了对应通道后没有效果，相同代码资源在2019.4.26f1c1正确显示，请教大家是否是版本问题还是需要特殊设置处理？ Unity版本：2018.4.17f1 A：在2018.4.23f1版本上参考这个链接的Outline效果进行了测试，在小米9真机上没有发现问题。 可以参考一下《基于Shader实现的UGUI描边解决方案》。 PS：看了一下2018.4.18到2018.4.23之间的所有的Release Note，没有发现相关问题的修复。这个文章是使用Shader来实现Outline的，感觉效果挺不错的。 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6228644a80139e0c1f1f661a Build Q：最近在使用12700f的丐版电脑，发现打包（IL2CPP）速度比5950x快了20%（472s对比621s，同工程）。 不知道大家有没有对比过十二代Intel，如果真的这么好，新电脑可以全面十二代Intel加b660了。 补充：内存都足够，没有用到页面缓存。 A：按照官方的说法，IL2CPP在2020.2.0b2之前都是单线程的，AMD的CPU一般是多线程强，Intel的单线程强，所以可以根据Unity版本选择最佳的CPU类型。 官方说法： https://forum.…

2D项目大量物品图标Draw Call优化方案
type: item
date: 2022-02-28T12:05:00+02:00
description: 1）2D项目大量物品图标Draw Call优化方案 ​2）UGUI SpriteAtlas的热更新问题 3）iOS平台突然有一帧UI渲染开销很高 4）iOS上频繁Crash，堆栈很奇怪 这是第288篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Rendering Q：我们有个2D项目，大致布局如下图： 1.场景按格子划分，玩家可以在每个格子上面摆放物品，物品种类非常多（700+）。 2.部分物品还有动画效果，动画会改变该物品的材质属性。 3.物品图标的大小不是严格一个格子大小，因此会有部分物品图标间的重叠。 4.场景的视角可拖动，最远视距下可以看到超过100个物品。 5.场景上除了物品，还有云雾、水波、发光粒子等各种特效。 具体实现： 1. 场景底图采用美术分割方式，每张2048*2048大小，由16张组成，直接使用SpriteRenderer摆放渲染。 2. 物品图标每张256以内，同样采用SpriteRenderer摆放渲染。 3. 使用Unity内置渲染管线。 经过一番Profile，发现目前主要性能问题在于Drawcall数量非常大，没摆满的情况下都接近300，主要消耗点在于特效跟物品图标的渲染。 1.由于物品数量巨大（后续还会增加），目前采用SpriteAtlas方式把所有物品图标进行合图，会产生3张2048*2048尺寸的图集，各个物品可能会随意分散在这3张图里，加上物品穿插显示，导致无法合批。 2.物品有状态，不同状态会对材质属性进行变更，改变灰度明亮度等效果，导致无法合批。 3. 场景上的粒子特效是3D的，会随意在物品图标中移动穿插，导致图标无法合批。 已经考虑过，但否决了的方案： 1.把图标缩小，使生成的图集在一张2048以内。 => 询问过策划，物品种类目前还不是全部的，完整的配置里有超过1000+物品，都合成到一张2048里不现实。 2.图标不合图集，直接散图集成，采用运行时动态合图方式对场上物品进行合图。 => 游戏主要玩法会导致不停拖动物品图标以及旧物品的删除跟新物品的生成，操作会非常频繁，动态合图方式消耗巨大。 鉴于上面所描述的状况，各位大佬有没有什么推荐的优化方案？ A1：可以从以下方式入手： 首先图片和图集减少Draw Call没问题，图集也应该做分类，把能组合的类型分配并归类。图集不是追求少就好，应该是动静分离，还有按项目分类。做到图集数最优，这个就要结合自己的项目分析了。 然后合批的问题要解决。重写一个Shader，挂载到SpriteRenderer上，改变这个材质上的属性可以避免导致无法合批。 格子布局应该避免元素重叠，这个在设计就要避免的。粒子特效尽量用2D效果来做表现，粒子的发射数量与粒子数量也要做权衡。 感谢廖武兴@UWA问答社区提供了回答 A2：建议动态图集，静态图集在这种情况下，基本是负优化。 感谢uwa菜鸡奶泡泡.@UWA问答社区提供了回答 A3：这个问题，其实挺好解决。 1.背包的物品组件做一个循环动态列表功能，这样同屏加载不会很多。 2.物品的同一个组件进行拆分，比如bg一个图集， icon一个图集，数字图片一个图集，特效纹理一个图集。 这样下来DrawCall一般在三四个可以搞定。 感谢郑佳宇joe@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6215b229e7172c2775f6f5c6 Atlas Q：UGUI图集切换后，图集中的图片名称不变，只是改变该图片为其他图片，但是为什么在加载UIPrefab的AssetBundle之后，UI上对应修改的图片会出现白图？会发现SpriteAtlas的CanBindTo返回false。 A：替换图片时，不修改原GUID则可以避免这个问题。 感谢题主Austin@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/621c3e1be7172c277500573d UGUI Q：iOS平台通过Profile定位性能，偶现某一帧UI开销异常高，通过Timeline看到是Render引起的，但是不知道后续应该如何定位。 这里可以看到这一阵Render开销达到了12.35ms，其他帧只有不到2ms。 A：EmitWorldScreenspaceCameraGeometry（图中紫色）这个函数是UGUI的Canvas网格重建生成UI Draw Call的开销，主要工作在Native子线程进行，主线程表现为该函数的等待开销，建…

Timeline技能编辑器如何提取关键帧信息
type: item
date: 2021-12-29T09:16:00+02:00
description: 1）Timeline技能编辑器如何提取关键帧信息 ​2）Mesh资源开启R/W内存的翻倍问题 3）UI的Animator动画对Canvas.SendWillRenderCanvas()耗时影响 4）对Shader变体的理解 这是第281篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Script Q：最近在用Unity做一个技能编辑器，让策划可以在Timeline编辑器里编辑技能动画、施法特效、受击动画、受击特效和播放声音等等。编辑这些都没什么问题，我想根据编辑好的Timeline导出一系列行为节点，在Editor的环境下导出成配置（包含所有行为节点）。现在我不清楚怎么获取Timeline里第m帧开始某动画x，第n帧结束这些行为。同时怎么能给动画打上对应Tag？ A：Timeline的组成如下图： 每个Track对应一个TrackAsset其实就是一个PlayableAsset，整个Timeline也是一个TimelineAsset，里面存了所有Track的数据： 对于每个Clip的开始和结束时间在TimelineClip中可以获取： 感谢羽飞@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61c00a17d8413e18eb546b3b Mesh Q：为什么Profiler中显示开启网格的Read/Write没有使其内存翻倍，而仅仅是上升了一点？ A：如果是FBX中的网格资源，则该网格资源很有可能除了自身网格的顶点数据外，还带有动画和骨骼信息。而当开启FBX的Read/Write时，仅会使网格资源自身的内存翻倍，而动画和骨骼信息不会，且Profiler并不能将他们分开统计，从而产生了整体没有翻倍的现象，如图。 开启Read/Write： 没开Read/Write： 这一点可以通过将FBX设置中Animation Type设置为None、取消Import Animation勾选、取消Import Materials勾选证明。此时Profiler中该网格资源内存显著下降，并基本符合开启Read/Write前后内存翻倍了。 值得一提的是，在较新版本的Unity中（图截自2020.3），选中网格资源是可以看到其内存的理论大小和具体组成部分的。 感谢Faust@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61c05c15d8413e18eb54ec09 UGUI Q：对UGUI添加Animator动画会造成UI更新耗时吗？如果Animator组件处于Active状态但没有进行Loop会有持续耗时吗？ A：在Unity 2018.4版本中对于这个问题实验如下： 步骤1，场景中为一个UI添加修改Scale的动画，并复制1000个； 步骤2，添加了其Position和Color属性的动画； 步骤3，修改了所在Canvas的Render Mode为Overlay模式和Camera模式； 步骤4，修改了所在Canvas的Pixel Perfect为打开。 只进行1的情况下无论动画是否Loop，Canvas.SendWillRenderCanvas()都只会在刚开始时有一段高耗时。 而修改2之后的所有情况下，开启Loop时Canvas.SendWillRenderCanvas()始终具有高耗时。但关闭Loop后，播放完一次动画后Canvas.SendWillRenderCanvas()的耗时就始终接近于0了，如图： 而此时动画系统本身的Animator.Update()耗时仍然很高，如图： 总的来说，从2018.4上的这次实验来看，现版本的UGUI不会被不在播放状态的Animator影响而造成Canvas.SendWillRenderCanvas()耗时。 感谢Faust@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61c05b07d8413e18eb54ea3e Shader Q：除了不建议使用Standard Shader外，还有其他的Unity默认的Shader不推荐使用吗？比如官方的Universal Render Pipeline/Particles/Unlit有6个变体，其中也用到了雾效的计算。但是我们项目中没有用到雾效，而且这个Shader用得非常多，这类情况是否有必要自己弄一个简洁版？ A：关于Shader变体数量主要影响的是Shader的…

高通芯片GPU是否有类似于HSR功能
type: item
date: 2021-12-13T12:22:00+02:00
description: 1）高通芯片GPU是否有类似于HSR的功能 ​2）UGUI上的RT动图会不会导致UI更新 3）UI经常迭代外观，如何尽量少改代码 4）开发过程中该使用AssetBundle包模式，还是模拟模式？ 这是第279篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） GPU Q：高通芯片GPU是否有类似于HSR功能？望解答，谢谢。 A：参考如下： 测试案例1：小米9 点击按钮添加全屏不透明的Quad，使用Standard Shader，离相机越远，RenderQueue越小。 结果：从FPS上看，到100层，帧率也都是60帧。说明小米9上确实有类似于HSR的功能，就是不透明对象不需要手动调整渲染队列从而让靠近相机的先渲染。 测试案例2：小米5X 点击按钮添加全屏不透明的Quad，使用Standard Shader，离相机越远，RenderQueue越小。 结果：从FPS上看，1层的时候43帧，2层就掉到25帧，3层掉到17帧。说明小米5X上没有类似于HSR的功能。 测试案例3：小米5X 点击按钮添加全屏不透明的Quad，使用Standard Shader，离相机越远，不调整RenderQueue。 结果：从FPS上看，1层的时候43帧，5层的时候依旧43帧。不调整RenderQueue后，因为Unity的不透明是按照由近到远绘制的，由于Early-Z的关系，导致被遮挡的物体不会进行Fragment Shader计算了，所以帧率比较稳定。 总结：对于像地形这样的大面积且Shader复杂度通常比较高的对象，还是需要调整渲染队列到比较靠后的，因为对于低端机型，并没有HSR这样的功能。所以可以通过调整地形的渲染队列靠后，从而让Early-Z对于大地形生效，从而减少地形的渲染像素。 对于高端机型，如小米9，因为其GPU硬件支持HSR功能，所以对于不透明物体来说，渲染队列没有关系。 总体来说，还是建议调整大地形的渲染队列，对低端机型有好处。 感谢小苗子@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61b6e84cd8413e18eb47e0f4 UGUI Q：在UGUI组件上使用RenderTexture动图会不会导致UI更新？ A：对于这个问题进行了简单的实验：实验设置了一个相机拍摄不停在运动两个物体，然后得到的RenderTexture赋给UGUI上的100个RawImage。在真机上运行，此时UI更新耗时几乎为0。 理论上，这些RawImage的顶点属性没有发生变化，确实不会导致UI更新。 感谢Faust@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61b6e8a0d8413e18eb47e1c4 UGUI Q：问题：之前的项目是用Lua做UI开发的。一个ToLua，一个XLua。策划或美术经常要调已经做好的UI的外观，一般需要变布局或节点层级。最早，代码里访问某个节点都是用GameObject.Find找到节点，层级关系一变，就需要程序配合改，很麻烦。怎么做到调整UI，不用改代码呢？ 思路：想到一个命名规范方案：程序拿到UI的Prefab绑上UI关联Lua的脚本后，把所有程序需要访问的节点，改名成rd_xxxxx，脚本控制下的子节点rd_开头的不能重复，如果有列表的子节点，上面再挂一个辅助的脚本（比如UItemHelper）。 Lua第一次加载时遍历所有节点，缓存所有rd_xxx的节点关系到脚本里（Lua里），这样，Lua代码里直接用self.rd_xxxxx就能得到对象，也不怕Prefab层级调整，也不用多次调用GameObject.Find。 规定其他人员调整UI Prefab时，不能改掉rd_xxxxx，如果需要改则要程序配合。 参考代码：ToLua项目的一个参考修改: 关联Lua脚本对象的LuaXXX.cs里（比如叫LuaMono.cs）初始化，增加： …… if (m_params.Count == 0) { //lua关联对象自动收集 SetRDObjectRef(luaClass, this.gameObject, true); } else { //节点整理期间的兼容(复用UI也走这里，避免反复搜集) foreach (ParamItem pi in m_params) { luaClass[pi.name] = pi.value; } } …… /// /// 把节点下所有节点查一下，如果名字是"rd_"…

子线程GC导致主线程函数耗时较高的问题
type: item
date: 2021-11-10T04:18:00+02:00
description: 1）子线程GC导致主线程函数耗时较高的问题 ​2）升级Unity大版本后，Text颜色修改问题 3）清除增量式GC导致的Mono堆内存泄漏问题 4）多Pass合批优化问题 这是第274篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Rendering Q：在主线程中有非常多的卡顿，从UWA的报告中看到很多异常的高耗时，请问可能是什么原因造成的？ A：像上面这样的频繁卡顿，且卡顿函数种类非常多的情况，应该是子线程分配了非常多的堆内存，导致子线程GC，从而卡住了主线程。当GC的时候，主线程可能会处于各种阶段，因此对应阶段的函数耗时就会包括等待GC的耗时。可以从UWA的Mono报告中查看是否有子线程分配了大量的堆内存，通常是由这种（Thread）打头的子线程函数的分配导致的。 感谢han@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6180e0f28f8c834241fd6930 UGUI Q：从Unity 4.6.9f1升级到Unity 2020.3.2.f1c1。首次Unity升级之后UI的Text颜色修改是正常。当运行一次之后，所有的Text颜色都无法修改了。就算新创建一个新的Text也无法修改。 A：会有多余的UI-Default和UI-DefaultFont这两个Shader，删除它们，然后重启Unity就好了。 感谢芝麻青豆角@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6185085fd8413e18eb034520 Mono Q：最近在研究Mono堆内存时，发现一帧内分配多次较大内存，会导致内存无法被回收。 过程如下： 在同一帧内调用三次分配100MB内存的方法，分配内存的变量都是在各自的作用域，在这之后调用GC.Collect()。发现有较大几率出现100MB或者200MB无法被回收的Mono堆内存。经过排查之后发现取消勾选Incremental GC之后，内存就能完全被收回。 我推测是每次申请内存的时候会执行一次GC，清除上一次分配的内存，但是由于使用增量GC，无法在本帧完成回收工作。再下一次GC的时候，第二次GC的回收内容被重置了，导致第二次的GC需要被回收的内存就泄漏了。 不知道我这么理解是不是对的，希望大佬们解惑。同时希望大佬告知有没有办法清除这部分内存。 测试环境： Unity 2019.4.15c1 Unity 2020.4.15f2 测试平台： 安卓 Mono 以下为测试代码： using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Profiling; public class TestMonoMemory : MonoBehaviour { void Do() { CallBack01(); CallBack02(); CallBack03(); } public void CallBack01() { List<int> i = new List<int>(1024 * 1024 * 100 / 4); //CallBack02(); } public void CallBack02() { List<int> i = new List<int>(1024 * 1024 * 100 / 4); //CallBack03(); } public void CallBack03() { List<int> i = new List<int>(1024 * 1024 * 100 / 4); } public string text; private void OnGUI() { GUILayout.Label("Allocated Mono heap size :" + Profiler.GetMonoHeapSizeLong() / (1024 * 1024) + "MB"); GUILayout.Label("Mono used size :" + Profiler.GetMonoUsedSizeLong() / (1024 * 1024) + "MB"); GUILayout.Label("Total Reserved memory by Unity: " + Profiler.GetTotalReservedM…