垃圾回收
|------|-----------------------------------------------------------------------------------------------------------------------------------------|
| 是什么? | 垃圾回收(Garbage Collection)GC |
| 工作机制 | 1、Unity 为用户生成的代码和脚本采用了自动内存管理。 2、小块数据(如值类型的局部变量)分配在栈上。大块数据和长期存储分配在托管堆上。 3、垃圾收集器 (GC)会定期识别并释放未使用的堆内存。虽然这是自动运行的,但检查堆中所有对象的过程可能导致游戏卡顿或运行缓慢。 |
要注意某些不必要的堆分配,这可能会导致 GC 尖峰 :
1、在 C# 中,字符串是引用类型,而不是值类型。减少不必要的字符串创建或操作。如果需要在运行时构建字符串,请使用StringBuilder类。
2、缓存数组引用,不要在循环中分配数组。
3、使用 GameObject.CompareTag 而不是手动将字符串与 GameObject.tag 进行比较 (返回新字符串会产生垃圾)。
4、虽然 yield 不会产生垃圾,但创建新的 WaitForSeconds 对象会。缓存并用 WaitForSeconds 对象,而不要在 yield 行中创建它。
cs
using System.Collections;
using UnityEngine;
public class Test : MonoBehaviour
{
public WaitForSeconds logic;
private void Start()
{
logic = new WaitForSeconds(3);
}
IEnumerator Check()
{
yield return logic;
Debug.Log("打印");
}
}
5、LINQ 和正则表达式 :它们幕后都会进行装箱,从而产生垃圾。如果性能很重要,请避免使用 LINQ 和正则表达式。
6、如有可能,可以使用 System.GC.Collect 主动触发垃圾收集。
UI优化
1、避免使用过多层级
拆分层级!在层级视图中如果游戏对象不需要嵌套,请简化父子化。较少的层级关系将受益于多线程刷新场景中的变换 (Transform)。复杂层级关系会发生不必要的变换 (Transform) 计算以及更多垃圾收集开销。
2、不勾选Raycast Target
对与不需要Raycast Target的UI,建议将其禁用。这些小的更改可以减少不必要的计算。

美术资源优化
正确导入纹理
纹理会占用大部分内存,因此,导入设置非常重要。通常,请遵循以下指导原则 :
-
减小 Max Size :使用能生成视觉上可接受的结果的最低设置。这种非破坏性方式,可以快速降低纹理内存。
-
使用 2 的幂 (POT) :Unity 要求移动端纹理压缩格式 (PVRCT 或 ETC)采用 POT 纹理尺寸。
-
制作纹理图集 :将多个纹理放置到单个纹理中,可以减少绘制调用和加快渲染速度。使用Unity 精灵图集 或第三方 Texture Packer 可以制作纹理图集。
-
关闭 Read/Write Enabled 选项 :如果启用,此选项在 CPU 和 GPU 可寻址内存中都会创建副本,纹理会占用双倍内存。大多数情况下,应保持此选项为禁用状态。如果要在运行时生成纹理,请通过 Texture2D.Apply 强制执行,并且传入设置为 true 的 makeNoLongerReadable。
-
禁用不必要的 Mip Map :对于在屏幕上大小保持不变的纹理(如 2D 精灵和 UI 图形),Mip Map 不是必需的,对于与摄像机的距离会变化的 3D 模型,请保留 Mip Map为启用状态。
检查多边形数量
分辨率越高的模型,需要的内存使用量越大,并且可能占用更长的 GPU 时间。您的背景几何体是否需要五十万个多边形?考虑减少所选 DCC 包中的模型。删除摄像机的视角看不到的多边形。使用纹理和法线贴图而不是高密度网格来实现精细的细节。
优化技术或操作
LOD技术
|------|----------------------------|
| 是什么? | 多层次细节(Level Of Detail ) |
| 作用 | 根据相机距离物体的远近来显示不同精度的模型 |
| 优点 | 减少场景中模型绘制的面数,提高渲染的性能 |
| 缺点 | 需要准备多套不同精度的美术资源,同时也增大了游戏体积 |
对象池技术
|------|----------------------------------------------------|
| 是什么? | 在开始时初始化若干对象,将它们存到对象池中。需要使用的时候从对象池中取出,使用完后重新放回对象池中。 |
| 优点 | 可以避免频繁创建和销毁对象带来性能消耗。 |
| 适用场景 | 如果需要对某种对象进行频繁创建和销毁时,例如应用在子弹、敌人等 |
批处理技术
Unity的批处理(Batching)技术是优化渲染性能的重要手段,它通过减少Draw Call次数来提高渲染效率。
光照烘焙
光照烘焙是Unity中重要的光照优化技术,它通过预计算场景中的光照信息,将结果"烘焙"到光照贴图(Lightmap)中,从而减少实时计算开销。
遮挡剔除
(occlusion culling)
遮挡剔除是Unity中一项重要的渲染优化技术,它通过剔除被其他物体完全遮挡的物体来减少不必要的渲染计算。
限制后期处理效果
全屏幕后期效果处理(如发光)会极大降低性能。请在游戏的美术设计中谨慎使用这些效果。
避免过多使用 Animator
Animator 主要用于人形角色,但也常用于动画化单个值(如 UI 元素的 Alpha 通道)。
避免过多使用Animator,尤其在处理UI动画时。尽可能对移动设备使用旧版 Animation 组件。
考虑创建补间函数或者使用第三方库来实现简单动画(如 DOTween)。
选择正确的帧率
1、移动端项目必须在帧率和电池续航时间以及热节流之间获得平衡。不需要将设备限值推向 60 fps,可以折衷以 30 fps 运行。Unity 默认移动端为 30 fps。
2、也可以通过 Application.targetFrameRate 在运行时动态调整帧率。例如,甚至可以将缓慢或相对静止的场景降至 30 fps 以下,而将玩游戏时的 fps 设置保留为较高值。
代码优化
1、如果确实需要使用 Update,可以考虑每 n 帧运行一次代码。这是一种应用时间切片 (将繁重的工作负载分布到多个帧的常用技术)的方法。在下面的示例中,我们每三帧运行一次 逻辑
cs
private int interval = 3;
void Update()
{
if (Time.frameCount % interval == 0)
{
//运行逻辑
}
}
2、避免空Unity 事件
即使是空的 MonoBehaviour 也需要资源,因此应删除空的 Update 或 LateUpdate 方法。
3、删除调试日志语句Debug.Log
日志语句(尤其是在 Update、LateUpdate 或 FixedUpdate 中)可能会降低性能。在进行构建之前,请禁用日志语句。
4、缓存游戏对象和组件
GameObject.Find、GameObject.GetComponent 和 Camera.main( 在 2020.2之前的版本中)可能开销较大,应避免在 Update 方法中调用它们。而应在 Start 中调用它们,并且缓存相应结果。