从零开始游戏开发Unity优化:UI控件优化 | 全面总结 |建议收藏

Posted 小听歌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始游戏开发Unity优化:UI控件优化 | 全面总结 |建议收藏相关的知识,希望对你有一定的参考价值。

你知道的越多,你不知道的越多 🇨🇳🇨🇳🇨🇳
点赞再看,养成习惯,别忘了一键三连哦 👍👍👍
文章持续更新中 📝📝📝


Proilier性能
drawcall不显示,显示SetPassCalls

渲染材质的先后顺序,材质渲染消耗时间,材质切换渲染切换一次:可以成为SetPassCalls,俗称DrawCall
切换渲染材质的顶点批次
一个批次渲染材质

面试经典:如何解决UI的卡顿
Canvas画布,节点!
Canvas优化要点
一个Canvas下的所有UI元素都是合并在一个Mesh网格中的,过大的网格Mesh更新时开销过大
合批优化:同一个批次进行渲染,三角形放同一个批次
移动会进行重新合批、重新绘制、开销大

一般比较复杂的UI界面,都拆分成一个独立的子Canvas
动静分离
静态UI不会被重建,动态UI会被重建
但是如果Canvas细分太多,会导致DrawCall增多
把同一个面板的UI资源放在同一个图集里,创建一个SpriteAtlas,将Sprite指定此图集
背包资源放在一个图集里

如果一个面板的UI资源来源多个图集,打开面板会卡段
背景大图不要和小图放在一起

还有一个Overdraw GPU优化
绘制区域有半透明元素,同一个位置,同一个像素点被同时绘制
新手引导,空的透明的四边形,点击屏幕任意位置开始
透明的UIImage
无图的UI遮罩点击优化

不执行点击的UI元素,不要勾选RayCaster,每一帧回去检测
少用布局组件LayoutGroup或者Content SizeFitter
用户操作 重绘 消耗大量计算时间
写算法自己算
Layout原理
遍历SetDirty对象会消耗性能

UI控件优化
Unity自带的ScrollView的问题
滚动,UI元素位置变化,Canvas就会重构,重新合批
如果ScrollView下的元素没有UI点击,千万不要去加RayCaster组件
Mask组件可见范围以外禁止显示,但不代表不渲染
ScrollRect控制滚动条,会把所以物品全部渲染,不会判断哪些不能被渲染

高性能滚动视图的实现思路
初始化滚动时
计算可视范围
遍历每一个格子,判定其是否位于可视范围内
如果可见,则显示,否则,隐藏

滚动时
重新计算可见格子
如果有格子移除,则将其格子用于新的格子的显示
类似对象池的思想

字体UI优化
角色名3D字体
文字+图标+血条
不用Text组件(有性能问题),使用TextMeshPro


🎁🌻🌼🌸 粉丝福利来喽 🎁🌻🌼🌸

  1. 免费领取海量资源 🎁
    简历自测评分表、Unity职级技能表、面试题库、入行学习路径等
  2. 《Unity游戏开发五天集训营 》50个名额 🎁
    我给大家争取到了 50个《游戏开发五天集训营 》名额,原价198,前50个免费
    扫码加入,暗号小听歌
    即可参加ARPG狼人战斗系统、饥荒生存类游戏开发、回合制RPG口袋妖怪游戏等游戏开发训练营
  3. 额外抽奖机会🎁
    参加游戏训练营、还有机会获得大厂老师在线面试指导、或者有机会获得价值1998元的《Unity极速入门与实战》课程
🔻🔻🔻🔻 扫下方二维码,获取游戏开发福利,暗号小听歌 🔻🔻🔻🔻

Unity游戏开发初探Unity动画优化

一、简介

  在最近的优化工作中,马三发现项目中的动画文件内存占比实在是太大了,峰值竟然有200多mb,很明显需要进行优化。经过一番网上查阅资料并结合自己实际操作以后,得到一些需心得体会,在这里马三记录一下并且分享给大家,希望对大家能有一些帮助。

二、动画压缩的注意事项

1.fbx中的动画无法压缩精度,即降低动画文件的浮点数精度

  fbx中的动画无法压缩精度,压缩完重启Unity会发现又恢复为原来的样子,并且在版本控制中看不出差别。原因是fbx在Unity中被识别为只读文件,精简动画这个修改的结果实际上是保存在Library/metadata。也就是说这个修改是本地化的操作,无法放入版本管理。导入fbx中的animation是read-only的(考虑到re-import,可编辑的意义其实不大),要编辑需要将动画文件复制出来。可以选中fbx中的动画文件,ctrl+D复制一份出来。复制出的文件是可以编辑的,运行脚本也无问题。然后项目中去使用这个复制的动画文件。

2.Ctrl+D复制出来Anim以后会发现,复制出来的这个anim的文件体积会比原来的fbx动画体积还要大

  Ctrl+D复制出来Anim以后会发现,复制出来的这个anim的文件体积会比原来的fbx动画体积还要大这个也是正常的。项目中fbx一般是二进制方式存储的,复制出来的anim如果是用text存储的话,体积会比原来大很多。这个没有太大的影响,最后还要看打出来的ab的大小,实测证明anim打出来的ab要比fbx的体积小很多。

  下面列举了两幅图,对比说明了anim动画和fbx动画打出的bundle文件大小对比和运行时内存占用的对比情况:

  anim动画assetbundle文件大小:

  anim动画运行时占用内存:

  fbx动画assetbundle文件大小:

  fbx动画占用运行时内存:

  可以看到无论是AssetBundle的体积还是运行时内存占用,使用抽离出来的anim动画都比使用fbx中的动画要节省。

3.去除动画文件的scale信息

  对于一般的人形动画需求,不会有模型骨骼scale变化的情况。因此我们可以把动画信息的scale部分去除,可以节约一部分大小。

4.为什么压缩动画的float精度、剔除Scale曲线,可以达到减少运行时内存占用

  Mecanim的动画系统的压缩确实不是靠改变float类型来达到的,而是通过降低数值位数后,将曲线上过于接近的数值(例如相差数值出现在小数点4位以后)直接变为一致,可以产生更多的const曲线,从而让引擎达到更高效存储的效果,进而达到所谓的“压缩”结果。缩短float类型的精度,导致动画文件内点的位置发生了变化,引起Constant Curve和Dense Curve的数量也有可能发生变化,最终可能导致动画的点更稀疏,而连续相同的点更多了。所以Dense Curve是减少了,Constant Curve是增多了,总的内存是减小了。

5.尽量使用从fbx中复制出来的anim动画,而不是直接引用fbx中的动画文件

  很多项目在开发初期阶段,为了快速迭代,并没有使用后处理工具将导入的带有动画的fbx文件进行动画抽离,而是直接是用fbx中的动画文件。实际上这种做法也会造成内存占用较多。因为fbx文件有可能依赖了一些贴图、材质,而且如果项目处理的不够好的话,还会导致交叉引用的出现。比如有一个主角的fbx动画文件,由于美术同学的一些操作,将它引用了怪物的一些材质,然后这个材质又会引用一些纹理。我明明只想加载简简单单的一个主角待机动画,结果就像从泥土里面拎花生一样,带出了一连串的其实不必要加载的文件,白白占用了大块的内存空间,很有可能就因为这一些内存空间被占用就导致了游戏的闪退和崩溃,这个问题是在我们项目中真实遇见过的情况,很值得注意一下。

6.动画文件压缩方式(Anim.Compression)

  一般项目都会对这个进行设置,所以就放在最后讲了。对于包含有anim动画的fbx文件,Unity提供了下面的这个设置面板。在Animation选项卡中,我们可以通过设置Anim.Compression来调整动画的文件的压缩方式:

  • Off 关闭压缩

  • Keyframe Reduction 减少没有必要的关键帧

  • Optimal 优化压缩,官方会选择最优的压缩方式来进行压缩,建议选择这个,我们项目也是选择的这个。

7.动画精度压缩与曲线剔除代码

  1 //----------------------------------------------
  2 //            ColaFramework
  3 // Copyright © 2018-2049 ColaFramework 马三小伙儿
  4 //----------------------------------------------
  5 
  6 using System;
  7 using System.IO;
  8 using System.Reflection;
  9 using Sirenix.OdinInspector;
 10 using Sirenix.OdinInspector.Editor;
 11 using UnityEditor;
 12 using UnityEngine;
 13 using UnityEngine.Profiling;
 14 
 15 /// <summary>
 16 /// 动画优化,存储占用/内存占用/加载时间
 17 /// 通过降低float精度,去除无用的scale曲线
 18 /// 从而降低动画的存储占用、内存占用和加载时间.
 19 /// 使用方法
 20 /// 通过菜单ColaFramework/OptimiseToolKits/优化动画打开窗口,
 21 /// 在Assets目录下选择要优化的动画,点击Optimize按钮,等待一段时间即可
 22 /// </summary>
 23 public class AnimtionClipOptimizeToolKit : OdinEditorWindow
 24 {
 25     [ShowInInspector]
 26     [InfoBox("剔除Scale曲线")]
 27     private bool m_excludeScale;
 28 
 29     private static AnimtionClipOptimizeToolKit _window;
 30 
 31     [MenuItem("ColaFramework/Optimise/AnimtionClipOptimize")]
 32     [MenuItem("Assets/Optimise/AnimtionClipOptimize")]
 33     protected static void Open()
 34     {
 35         _window = GetWindow<AnimtionClipOptimizeToolKit>("动画优化压缩工具");
 36         _window.Init();
 37         _window.Show();
 38     }
 39 
 40     private Vector2 m_scoll;
 41     private bool m_ing;
 42     private int m_index;
 43 
 44     private string animclipPath;
 45     private AnimationClip animClip;
 46     private static MethodInfo getAnimationClipStats;
 47     private static FieldInfo sizeInfo;
 48 
 49     private void Init()
 50     {
 51         Assembly asm = Assembly.GetAssembly(typeof(Editor));
 52         getAnimationClipStats =
 53             typeof(AnimationUtility).GetMethod("GetAnimationClipStats", BindingFlags.Static | BindingFlags.NonPublic);
 54         Type aniclipstats = asm.GetType("UnityEditor.AnimationClipStats");
 55         sizeInfo = aniclipstats.GetField("size", BindingFlags.Public | BindingFlags.Instance);
 56     }
 57 
 58     protected override void OnGUI()
 59     {
 60         var selects = Selection.objects;
 61 
 62         using (var svs = new EditorGUILayout.ScrollViewScope(m_scoll))
 63         {
 64             m_scoll = svs.scrollPosition;
 65             foreach (var obj in selects)
 66             {
 67                 var clip = obj as AnimationClip;
 68                 if (clip == null)
 69                     continue;
 70                 EditorGUILayout.ObjectField(clip, typeof(AnimationClip), false);
 71             }
 72         }
 73 
 74 
 75         using (new EditorGUILayout.HorizontalScope())
 76         {
 77             m_excludeScale = EditorGUILayout.ToggleLeft("Exclude Scale", m_excludeScale);
 78 
 79             if (GUILayout.Button("Optimize"))
 80             {
 81                 m_ing = true;
 82             }
 83         }
 84 
 85         if (m_ing)
 86         {
 87             if (m_index >= selects.Length)
 88             {
 89                 m_ing = false;
 90                 m_index = 0;
 91                 EditorUtility.ClearProgressBar();
 92                 return;
 93             }
 94 
 95             var info = string.Format("Process {0}/{1}", m_index, selects.Length);
 96             EditorUtility.DisplayProgressBar("Optimize Clip", info, (m_index + 1f) / selects.Length);
 97 
 98             var obj = selects[m_index];
 99             m_index++;
100             var clip = obj as AnimationClip;
101             if (clip == null)
102                 return;
103             animClip = clip;
104             animclipPath = AssetDatabase.GetAssetPath(clip);
105             Log("优化前---->");
106             FixFloatAtClip(clip, m_excludeScale);
107             Log("优化后---->");
108         }
109     }
110 
111     private static void FixFloatAtClip(AnimationClip clip, bool excludeScale)
112     {
113         try
114         {
115             if (excludeScale)
116             {
117                 foreach (var theCurveBinding in AnimationUtility.GetCurveBindings(clip))
118                 {
119                     var name = theCurveBinding.propertyName.ToLower();
120                     if (name.Contains("scale"))
121                     {
122                         AnimationUtility.SetEditorCurve(clip, theCurveBinding, null);
123                     }
124                 }
125             }
126 
127             var curves = AnimationUtility.GetCurveBindings(clip);
128             foreach (var curveDate in curves)
129             {
130                 var curve = AnimationUtility.GetEditorCurve(clip, curveDate);
131                 if (curve == null || curve.keys == null)
132                 {
133                     continue;
134                 }
135 
136                 var keyFrames = curve.keys;
137                 for (var i = 0; i < keyFrames.Length; i++)
138                 {
139                     var key = keyFrames[i];
140                     key.value = float.Parse(key.value.ToString("f3"));
141                     key.inTangent = float.Parse(key.inTangent.ToString("f3"));
142                     key.outTangent = float.Parse(key.outTangent.ToString("f3"));
143                     keyFrames[i] = key;
144                 }
145 
146                 curve.keys = keyFrames;
147                 clip.SetCurve(curveDate.path, curveDate.type, curveDate.propertyName, curve);
148             }
149         }
150         catch (System.Exception e)
151         {
152             Debug.LogError(string.Format("CompressAnimationClip Failed !!! animationPath : {0} error: {1}", clip.name,
153                 e));
154         }
155     }
156 
157     #region LogInfo
158 
159     private void Log(string title)
160     {
161         Debug.LogFormat("{0} FileSize:{1},MemorySize:{2},InspectorSize:{3}", title, GetFileSize(), GetMemorySize(),
162             GetInspectorSize());
163     }
164 
165     private long GetFileSize()
166     {
167         var fileInfo = new FileInfo(animclipPath);
168         return fileInfo.Length;
169     }
170 
171     private long GetMemorySize()
172     {
173         return Profiler.GetRuntimeMemorySizeLong(animClip);
174     }
175 
176 
177     private int GetInspectorSize()
178     {
179         var stats = getAnimationClipStats.Invoke(null, new object[] {animClip});
180         return (int) sizeInfo.GetValue(stats);
181     }
182 
183     #endregion
184 }
View Code

此工具也已经集成进了ColaFramework:https://github.com/XINCGer/ColaFrameWork/blob/master/Assets/Editor/OptimizeToolkits/AnimtionClipOptimizeToolKit.cs

三、总结

  在本篇博客中,马三跟大家一起分享了一下在优化项目动画文件内存占用中的一些注意事项,希望可以对大家起到一些帮助。同时这里也有一些非常不错的关于动画内存优化的博客和uwa的问答,马三在这里贴给大家,可以自己阅读一下,加深理解。

  1. Anim动画压缩优化探究

  2. Unity动画文件Animation的压缩和优化总结

  最后的最后,还不得不提一下 ACL 这个非常牛逼的C++编写的动画压缩库,至于它的原理和如何使用,马三在这里先买个关子,我会在后面的博客中进行讲解,敬请期待!

 

 

 

 

如果觉得本篇博客对您有帮助,可以扫码小小地鼓励下马三,马三会写出更多的好文章,支持微信和支付宝哟!

       

 

作者:马三小伙儿
出处:https://www.cnblogs.com/msxh/p/14090805.html
请尊重别人的劳动成果,让分享成为一种美德,欢迎转载。另外,文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!

以上是关于从零开始游戏开发Unity优化:UI控件优化 | 全面总结 |建议收藏的主要内容,如果未能解决你的问题,请参考以下文章

从零开始学Unity游戏开发

Unity3D 官方移动游戏优化指南9.用户界面

Unity3d游戏开发UGUI插件入门之游戏菜单

unity小地图掉帧严重

Unity3D 官方移动游戏优化指南9.用户界面

Unity3D 官方移动游戏优化指南9.用户界面