参考:
Unite Now - 性能优化技巧 BV1Bp4y1i7wK BV1Tt4y1X7f6Unity 技术开放日 北京站 BV1r44y1z7X3Unity 浅谈Unity内存管理 BV1aJ411t7N6花桑-UGUI性能优化总结 https://www.drflower.top/posts/aad79bf1/Unity UI优化(1~5) https://gwb.tencent.com/community/detail/127013知乎-Unity的内存管理与性能优化 https://zhuanlan.zhihu.com/p/362941227
(相关资料图)
总览:
1.通用优化
2.UI优化
3.设置(纹理设置,Mesh设置,音频设置,Graphics设置)
根据大佬们的文章理解写出来的,有错误欢迎指正!
1.通用优化
1.多对象的Mono类上,通用的配置字段使用So配置代替,可以减少Mono复制字段占用内存 2.避免使用Resource 文件夹, 游戏启动时加载索引表,影响启动时间3.清除空的Mono方法,即使方法内是空的也会调用,例如Start,Update4.动画机的属性使用Hash替代属性名, 同理 材质,shader也适用
可以先将Hash缓存起来,减少字符串查找的消耗
5.避免Hierarchy的物体之间有很深的层级
因为当对父节点的GameObject进行坐标转换时,就会产生OnTransformChanged事件,这消息会传递给该GameObject下所有子对象,即使这些对象没有任何渲染组件
6.Accelerometer Frequency 关闭加速度计频率(Project Settings->Player->IOS->Other Settings)这个功能定义Unity从设备读取加速度仪信息的频率,在不需要加速仪的游戏中,将它启动或设置了高于需求的频率,会影响性能表现。因为读取硬件设备信息,会增加CPU的处理时间7.对于移动有rigidbody的物体,使用rb.MovePosition (在FixUpdate执行)代替transform.Translate , 可以减少PsysX物理引擎的重计算
8.避免AddComponent使用过多 , 避免多次 Find, GetComponent, 这些函数的消耗都比较大
9.减少装箱拆箱操作
10.避免匿名函数
所有的匿名函数和闭包在C#编IL代码时都会被new成一个Class(匿名class),所以在里面所有函数,变量以及new的东西,都是要占内存的。11.使用单例需要注意,静态的字段会一直保留占用内存。
12.对于频繁调用的属性(getter/setter)改为字段,因为调用属性相当于调用函数,会在堆栈上分配内存。
13.当发现Native Memory大量上升时,可以先着重检查Scene因为是C++引擎,所有的实体最终都会反映在C++上,而不会反映在托管堆上。所以当构建一个GameObject的时候,实际上在Unity的底层会构建一个或多个Object来存储这一个GameObject的信息(Component信息等)。所以当一个Scene里面有过多的GameObject存在的时候,Native Memory就会显著的上升,甚至可能导致内存溢出。
2.UI优化
UI的基本原理
Canvas:Native层实现的Unity组件,Canvas负责将其内部的几何形状合并到批处理、生成合适的渲染指令并发送到Unity图形系统
这些操作都由原生C++代码完成,这称为 重新批处理(Rebatch) 或 批处理构建(Batch Build) 。当一个画布被标记为含有需要重新批处理的几何形状时,称这个画布为 脏(dirty) 画布。
Graphic:类是由Unity UI系统的C#库提供的基类,所有的向画布系统提供可绘制几何内容的UI系统C#类都继承它。大多数内置的UI系统绘图类都是通过 MaskableGraphic 子类实现的,这个子类实现了 IMaskable 接口,可以被遮罩。
Layout组件 控制RectTransform的尺寸和位置,它通常用于创建具有复杂布局并且内部组件需要相对尺寸或者相对位置的UI。Layout组件只依赖RectTransform并且只影响与其关联的RectTransform的属性。
Graphic和Layout组件都依赖 CanvasUpdateRegistry 类,该类没有在Unity编辑器中公开。这个类跟踪那些需要进行更新的Layout组件和Graphic组件集合,并在与其相关的画布调用 WillRenderCanvases 事件时根据需要触发更新(update)。
Batch Build/Rebatch(Canvases):Canvas把表示它UI元素的网格合并起来,并生成合适的渲染命令发送到Unity的图形管线中。这一过程的结果会被缓存并重,直到画布被标记为脏画布。
计算批处理需要根据深度(Depth)对网格进行排序、检查网格的重叠、共享材质等情况。这个操作是多线程的,因此在不用的CPU架构上性能差异很大。
Rebuild(Graphics):指重新计算Graphic的Layout和网格的过程(Layout重建+Graphic重建),在CanvasUpdateRegistry中执行。
关注指标:Canvas.BuildBatch 函数耗时Canvas.SendWillRenderCanvases 函数耗时Draw Call Overdraw堆内存
合批规则(Draw Call)
UGUI的合批除了要求相同贴图和材质外,还对Depth和Hierarchy的层级等有要求。
比如两个相邻Image的图是同个图集,则不打断合批
可以借助FrameDebugger实时调试合批情况。
优化
1.尽量减少层次 -> 减少Layout Rebuild计算时间2.慎用Mask组件和Outline、Shadow组件
都是通过重复绘制多个Mesh实现的,其中Showdow绘制为原文本Mesh的2倍,而Outline为5倍,对渲染面数、顶点数,BuildBatch和SendWillRenderCanvases的耗时,Overdraw都有影响3.Raycast关闭, 不需要射线遮挡的关闭
4.隐藏界面时,可用CanvasGroup.Alpha=0,或者Canvas设置enable=false,代替SetActive。5.使用RectMask2D代替MaskMask:裁剪依赖Image组件,可根据Image裁剪不规则形状。增加两个DrawCallRectMask2D:裁剪按照Rect大小,不依赖Image
6.尽量保持UI上的粒子特效简单,尽量使用序列帧实现。
7.界面上的特效不要直接挂在在UI的Prefab上,而是用动态加载的方式,有助于减小UI的Prefab体积以及加载时间。
8.Canvas动静分离(减少BuildBatch耗时),把更新频繁的UI就放在另一个Canvas里。例如血条,飘字。还有尽量减低更新频率,如隔帧更新。
9.使用图集(减少内存碎片,减少合批次数)
3.设置
纹理设置
1.关闭Read/Write。减少一半内存2.UI不需要MipMaps (MipMap的意思是分级细化纹理,也可以理解为图片的LOD)3.不透明图移除Alpha通道4.UI使用图集
纹理格式选择:
Mesh设置1.Mesh Compression ->选择High(高比特率压缩)2.关闭Read/Write3.不是动画对象关闭Rig4.Blendshapes:无使用则关闭
5.Material:如果Material没有用到法向量和切线信息,则关闭
音频设置1.使用Force To Mono , 强制单声道2.格式:Android:vorbis iOS:ADPCM或者MP33.LoadType设置
Graphics设置1.静态批处理,将不动的对象标记为静态批次
2.Mesh Render关闭阴影投射: Mesh Render>Lighting-> Shadow Casting Off3.光照使用Culliing Masks (遮罩),只对特定层的物体光照4.避免直接使用手机分辨率,因为有的手机自身过高,会增加渲染负担
Screen.SetResolution(width,height,false);
相对的,关闭则可能会导致分辨率过低,这就需要设置一个合适的分辨率
关键词: