Unity休闲游戏性能checklist
本文由 NRatel 历史笔记整理而来,如有错误欢迎指正。
总则:关闭/删除无用项,调低不必要项
以真机检验为标准
以正式包为标准(日志、使用Mono等,对性能影响较大)
一、项目设置
- 图形质量(Project Settings/Quality)
- 保留唯一/少量质量等级,关闭/调低不必要选项,引用唯一/少量URP设置资源
- V Sync Count 设为 Don't Sync
- URP设置(Assets/Settings/URP-Balanced)
- 在隐藏部分确保开启 SRP Batcher 和 Dynamic Batching
- 在保证显示效果的情况下,关闭/调低 HDR、抗锯齿、光源、阴影等不必要选项
- 在保证显示效果的情况下,关闭/调低引用的 URP-Balanced-Renderer 中的不必要选项
- 检查设置 Render Scale,若项目允许,可适当降低渲染分辨率(如0.9或0.8)。
- 其他参考:
https://docs.unity3d.com/cn/Packages/com.unity.render-pipelines.universal@12.1/manual/universalrp-asset.html https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@12.1/manual/configure-for-better-performance.html https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@12.1/manual/optimize-for-better-performance.html
- 物理设置(Project Settings/Physics)、2D物理设置(Project Settings/Physics 2D)
- 若未使用3D物理,则关闭 Auto Simulation。(注意,若关闭,用到时会自动开启)
- 关闭/调低不必要项
- 关闭不必要的层间碰撞。
- 启用 Reuse Collision Callbacks(启用后,需注意其潜在风险(回调覆盖))
- 物理更新间隔(Project Settings/Time)
- 保证物理效果的情况下(仅使用物理时),调大 Fixed Timestep 的值(默认为0.02,目标帧率30的话,可以调到0.5)
- Player Platform设置(Project Settings/Player/OtherSettings)
- 勾选 Strip Engine Code,并将 Managed Stripping Level 调为 High(需在意外裁剪的类或字段或方法上添加 [Preserve],或加入link.xml)
- 根据项目需要,选择是否勾选 Optimize Mesh Data
- 根据项目需要,选择是否勾选 Texture MipMap Stripping
- 正式包脚本后端使用 IL2CPP
- 相关参考:
https://docs.unity3d.com/2021.3/Documentation/Manual/class-PlayerSettingsAndroid.html https://docs.unity3d.com/2021.3/Documentation/ScriptReference/PlayerSettings-stripUnusedMeshComponents.html
二、场景和光照相关设置
原则:
尽量不使用光照!光照在抖音/微信小游戏端非常昂贵
尽量关闭阴影!如有需要,考虑使用假阴影
相关参考:
https://developer.android.com/games/optimize/lighting-for-mobile-games-with-unity?hl=zh-cn
若必须使用光照:
- 静态物体,可使用光照贴图
- 动态物体,可将URP/Lit 材质改为 URP/SimpleLit(抖音/微信小游戏)
具体:
- 环境光
- 可在 Window/Rendering/Lighting 的 Envrironment 调整,注意,环境光的修改是生效到 Scene 文件上。
- 平行光
- 存在烘焙时,Mode改为 Baked 或 Mixed
- RenderMode 改为 Not Important
- CullingMask 改为 仅玩法场景中的层 Default/Battle。去掉UI
- ShadowType 改为 No Shadows
- 抖音/微信小游戏中不要使用其他类型的光源!不要多光源!
- 场景相机(若无需要,直接关闭 或 使用URP设置且在URP中关闭)
- 关闭 PostProcessing(后处理)
- 关闭 Anti-aliasing(抗锯齿)
- 关闭 StopNaNs
- 关闭 Dithering
- 关闭 Renderering Shadows
- 关闭 HDR
- 关闭 MSAA
- UI相机
- 关闭 PostProcessing(后处理)
- 关闭 Renderering Shadows
三、资源设置/处理
- 清理废弃/冗余资源(借助工具扫描,但注意排除代码中主动加载的资源目录)
- Texture/Sprite(模型/Spine/特效等贴图、2的场景内图片元素)
- 压缩格式(全平台统一):默认为 ASTC 6x6,战斗/主界面等核心高品质要求处可设为 ASTC4x4,避免直接使用 RGBA32。
- 导入设置:若无需要,关闭 Read/Write、关闭GenerateMipMaps、关闭 GeneratePhysics/Shape、尽量不使用 Trilinear。
- 大小尽量避免超长条,不利于充分利用图集
- Sprite(UI)
- 压缩格式(全平台统一):RGBA32(因为要进图集,若不是RGBA32,较新Unity版本会Warnning)。
- 导入设置:若无需要,关闭 Read/Write、关闭GenerateMipMaps、关闭 GeneratePhysics/Shape、尽量不使用 Trilinear。
- SpriteAltas(UI)
- 压缩格式(全平台统一):默认为 ASTC 6x6,战斗/主界面等核心高品质要求处可设为 ASTC4x4,避免直接使用 RGBA32。
- 导入设置:若无需要,关闭 Read/Write、关闭GenerateMipMaps、关闭 GeneratePhysics/Shape、尽量不使用 Trilinear。
- 勾选 Allow Rotation,且不勾选 Tight Packing、Padding 设为 4(注意:两者只能勾选其一。否则图集中元素在取用时会发生错误粘连(截止2021.3.42还是如此))
- 材质
- 关闭 ReceiveShadows(若无需要)
- Render Face 尽量使用 Front(Cull Front) 而非 Both(Cull Off)(如:绳子项目中,绳子在静止时可使用 Front,仅当剪除表现时,改为Both)
- Shader
- 减少if/else(分支会打断GPU并行)
- 尽可能使用低精度的浮点值,如 fixed 的计算速度是 float 的四倍以上。
- 优先使用Swizzle,而非独立操作单个通道,合理运用Mul_Add以减少不必要的移动指令。
- 删除shader中没用到的关键字,避免生成不必要的着色体变体。详细可以参考Declaring and using shader keywords in HLSL - Unity 手册
- 将 multi_compile 改为 shader_feature。(multi_compile将所有定义的变体都会被编译并包含在最终构建中,即使场景中从未使用过这些变体;hader_feature仅编译并包含实际使用的变体。Unity 会分析场景材质和全局关键字,只保留需要的变体)
- 网格/模型
- 简化模型,降低面数、顶点数
- 导入设置:若无需要,关闭 Read/Write(默认)、关闭GenerateColliders(默认)、如无动画,修改Rig选项卡中AnimationType 为None
- LOD(Level Of Detail)(多层次细节) 设置(抖音/微信小游戏中多不涉及)
- MeshRender 关闭 Cast Shadows(在渲染场景时,即使不为对象提供光照,Cast Shadows 等设置也会增加开销)
- Spine
- 导出模式必须为 bytes,而非 json
- 导出为2的幂次
- 检查导出图集大小(避免美术仅隐藏节点,导致混入废弃内容)
- 检查面数
- 设定压缩格式(全平台统一):默认为 ASTC 6x6,战斗/主界面等核心高品质要求处可设为 ASTC4x4,避免直接使用 RGBA32。
- 预打同层图集进行合并
- 音频
- Foce To Mono(强制混合为单通道)
- 压缩格式,通常应选为 Vorbis(压缩后文件较小,但质量与 PCM 音频相比略低。)
- Quality 适当降低
- 动画
- 浮点数精度
- 字体
- 英文版使用静态TMP字体。
- 没有用户输入内容的中文版使用静态TMP字体(需列出所有用到的字)。项目实际打出的图集大小为 1024x1024。(WebGl下格式为 Alpha8 UNorm, 占用1M内存),可考虑修改其格式(需研究方案)
- 用户输入内容的中文版,可使用的动态字体文件,注意,其引用的ttf文件最好控制在3M之内。
- 特效/粒子
- 同屏大量相同的多材质特效时,必须对材质层级进行排序(美术来做),否则可导致Batches飙升。注意这种情况不要对特效预设挂 SortingGroup,否则会导致断批。
- 利用工具检测不符合要求的粒子/拖尾。包括:
- 渲染器数(3~5):尽量以更少粒子层数表现出想要的效果
- 最大粒子数(30)
- 是否开启物理碰撞(OFF)
- 是否开启投影(OFF)
- 引用贴图的最大宽/高(512)
- 引用网格的最大面数(256)
- 复杂吃性能的粒子动画考虑改为序列帧
- 注意 SRP Batcher 对 粒子系统 不适用
- 其他资源
- 绳子项目:降低绳子插件 Resolver 设置,关闭/降低不必要的项,加快计算
四、代码层面
- C# 基础
- 字符串拼接 GC alloc
- LINQ GC alloc
- 闭包和匿名函数GC alloc
- 使用数组替代List<T>,或对 List指定大小
- 避免装箱/拆箱
- 将 Dictionary 操作的连续的 ContainsKey 和 Get 改为1次 TryGet
- 使用官方接口时,使用无GC版本的实现
- 避免使用反射
- Unity基础
- 使用CompareTag代替tag的字符串比较
- 在Start/Awake/OnEnable 执行重度逻辑,导致打开时卡顿
- 移除空的回调,比如:Awake,Start,Update等,尤其时大量出现的物体
- Camera.main 调用
- Transform once, not twice - Transform.SetPositionAndRotation
- Use OnBecameVisible() and OnBecameInvisible() callbacks
- Use sqrMagnitude for comparing vector magnitudes
- 缓存 GetComponent 的结果,包括 .transform
- 缓存 GameObject.Find 的结果、尽量不使用 Find
- 避免使用 Monobehaciour的SendMessage、Invoke
- 替换GameObject的显示隐藏实现Active&Deactive -> Renderer enable&disable -> Renderer.forceRenderingOff
- 集中更新大量物体的Update
- 使用DOTS的全部或部分(ECS、Burst、Jobs)(注意 Burst、Jobs 可单独使用)
- 常见优化策略
- 使用结构体(struct)替代频繁小类,避免 GC alloc
- 使用 距离平方而不是距离
- 复用集合, Clear 而非 New
- 使用类对象池
- 使用游戏物体对象池,并预加载(进入战斗时需要创建的物体、特效等。)
- 分帧处理卡顿
- 选用最符合当前(查找/增加/删除/排序等)使用场景的 数据结构(另,减少Hash耗时等、提高缓存命中率等)
- 对于超高频访问的、key为枚举/简单Int的 Dictionary,可用 List 替代
- 备忘录算法,缓存频繁计算的结果
- 脏标记模式,避免频繁计算
- 将 try catch 改为返回 错误码处理
- AOI ( Area Of Interest )(只处理可见/关注区域,隐藏大部分不可见/不关注区域)
- 按需加载、及时卸载
- 处理高中低端机的品质设置
- 在差的设备上,使用较低目标帧率 Application.targetFrameRate
- 减少IO频率
- 减少序列化/反序列化频率、提高序列化/反序列化效率(如用pb代替json)、分帧处理大量序列化/反序列化
- 正式包必须关闭日志(使用封装的Debug),尤其是高频日志
- 恰当利用动态、静态批处理、SRPBatcher、GPUInstancing
- 预热Shader变体
五、一些模块
- 物理
- 碰撞盒形状,避免使用 Mesh
- 控制同屏碰撞物体数量级。(明显影响性能
- UI(UGUI)
- 检查并处理UI批次(借助 FrameDebuger),常见打断合批/高批次原因:
- 不同图集Image穿插打断合批
- 文本与Image穿插打断合批
- 自定义材质球打断合批
- 元素position的Z值不为0打断合批
- 使用Mask
- 避免网格重建
- 动静分离。注意频繁变动元素,尤其是倒计时组件
- 避免重新Layout
- 避免OverDraw:
- 避免以覆盖在上的方式打开界面(关闭/不渲染下层)。
- 禁用无需交互元素的 Raycast Target。避免透明像素参与射线检测,但更重要的是减少驱动 GPU 绘制全尺寸透明网格。(尤其是透明的界面背景)
- UI元素出图时,尽量不要让四周存在较多不透明像素,避免无意义的重叠。
- 处理仅为产生点击响应区域而完全透明的情况。
- 不使用 UGUI自带的 shadow 和 outline(复制N份的实现方式),增加overdraw且顶点数翻N倍。
- 对于类似头像框的框体,取消勾选Image组件Sliced(九宫)模式下的 FillCenter 选项,使不渲染中心区域。
- 使用 RectMask2D 代替 Mask(原因:RectMask2D 裁剪性能优于 Mask,且不造成批次升高
- 部分“同一位置大量动态加载替换的图”,使用动态图集。
- 检查并处理UI批次(借助 FrameDebuger),常见打断合批/高批次原因:
- 网络
- 序列化与反序列化耗时(使用pb而非json
- 合并网络请求
- 压缩网络包
- 减少无用字段
- 降低字段精度
- 配置表
- 不要导出不会实际使用的描述性字段
- 客户端服务器字段分别导出
- 按需加载配置表
- 配置表相同内容,考虑索引式压缩(lua表中做过)
- 属于渲染方面
- 从输入GPU前:
优化资源大小(如面数、图大小)、资源格式和导入设置(降显存)
LOD 简化网格
使用烘焙贴图(减少实时光计算)
降低Drawcall(降渲染负载) - 从输入GPU后:
URP设置(光照、阴影、抗锯齿、Render Scale等)
Shader相关优化、简化Shader
简化/优化后处理效果
- 从输入GPU前:
- 耗电/发热大头
Top1:GPU。
过度绘制(Overdraw)
复杂的着色器(Shader)
过高的分辨率
未压缩/过高分辨率的纹理(导致采样速度下降)Top2: CPU
控制好总体帧率
频繁GC
物理计算
复杂的每帧业务计算其他:
屏幕亮度
高刷屏
高频网络
大量IO
音频视频解码 - 其他 问题
较多SpriteMask,每个占2个 batches,仅使用时启用/考虑合并GetComponent<MeshRenderer>().material 每次调用时会创建实例
六、抖音/微信小游戏包体
- 删除无用 Package
- 清理掉所有/大多数无用资源(尤其是因拷贝其他项目/切换Platform分支带入的冗余资源,如安卓/ios下sdk插件等)
- 合并利用率低的图集
- 将形状相同,色调不同的多图,利用定制shader为灰度图上色
- 要求美术涉及时,多考虑九宫、平铺等,减少出图大小
- 将 ProjectSettings 中代码裁剪级别设到最高
- 将 Build Settings 中的 Code Optimization 设为 Size
- 将 Build Settings 中的 IL2CPP Code generation 修改为 Fast(Smaller) builds(仅抖音/微信小游戏。Android 和 iOS 平台应设为相反的 Fast runtime)
- 代码分包(抖音和微信小游戏)
- 资源分包(按需下载
七、性能排查手段
- Profiler中,CPU整体能跑到 60FPS(排查整体运行性能、耗电)
- Profiler中,无或很少有周期性CPU波峰(排查整体GC问题)
- Profiler中,无剧烈CPU波峰,用 Profiler.BeginSample、Profiler.EndSample 细查波峰(排查卡顿点)
- Profiler中,无每帧 GC Alloc(排查每帧GC问题)
- Memory Profiler 中,对初始、短时、长时运行做内存快照(排查内存泄漏),注意看是托管堆(强引用)/非托管堆(主动分配的资源未销毁或卸载)的泄漏。
- Game视图Stats里,Batches 静止时<20,峰值<100。利用FrameDebuger检查断批原因。
- Game视图Stats里,控制同屏渲染面数/顶点数
- Overdraw视图,检查overdraw情况。(注意UI的OverDraw和特效的)
内置管线:Scene视图的 ShadeMode为 OverDraw
URP:Rendering Debugger
https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@12.1/manual/features/rendering-debugger.html - 使用 UPR 或 UWA 工具生成报告