一.Unity UI Optimization Tool下载安装
访问主页可阅读基础使用流程,性能优化的方案:
https://github.com/JoanStinson/UnityUIOptimizationTool#reduce-game-objects-inside-prefabs
下载unitypackage包
https://github.com/JoanStinson/UnityUIOptimizationTool/releases

导入到unity:

二.Unity UI Optimization Tool支持的优化技术.
2.1 对于不属于Button的 Image 组件,禁用Raycast Target
每次有 UI 输入(单击、点击、滚动等)时,Unity 的GraphicsRaycaster 都会迭代场景中的所有Raycast 目标,因此我们的处理量越少,节省的处理就越多。
cs
private static void DisableRaycastTargetForNonButtonImages(Transform child)
{
bool childHasInteractable = child.gameObject.TryGetComponent<Button>(out var childButton) || child.gameObject.TryGetComponent<Toggle>(out var childToggle);
bool childHasImage = child.gameObject.TryGetComponent<Image>(out var childImage);
if (!childHasInteractable && childHasImage)
{
childImage.raycastTarget = false;
}
}
2.2 避免在UI中使用 Animator 组件
Unity 的 Animator 组件主要用于3D动画。将其用于 UI 元素会导致额外的处理。
相反,最好的方法是使用自定义补间工具,例如 DOTween。
cs
private static void LogWarningIfAnimatorComponentsAreFound(Transform child, string selectedGameObjectName)
{
bool childHasAnimator = child.gameObject.TryGetComponent<Animator>(out var childAnimator);
if (childHasAnimator)
{
Debug.LogWarning($"Child <b>{child.name}</b> of <b>{selectedGameObjectName}</b> has an Animator component. Please consider using a DOTween Animation or ShowHideAnimator instead if possible.");
}
}
2.3 在ScrollView中使用RectMask2D组件
做法:找到ScrollView子对象Viewport ,若发现它身上挂着的是Mask组件。直接移除Mask组件,然后添加RectMask2D组件。
效果:假设你的 ScrollView里有一个超长的列表,包含100个列表项。屏幕一次只能看到5个。如果没有 RectMask2D,Unity仍然会尝试生成全部100个项的网格顶点,并向GPU提交渲染,只是最终被模板(stencil)测试丢弃或遮挡,这浪费了大量CPU和GPU资源。有了 RectMask2D,只有可见的那5-8个附近项的UI元素会真正进入渲染管线。
cs
private static void AddARectMask2DToTheScrollRectIfItsMissingOne(Transform child, out bool childHasScrollRect)
{
childHasScrollRect = child.gameObject.TryGetComponent<ScrollRect>(out var childScrollRect);
bool childHasRectMask2D = child.gameObject.TryGetComponent<RectMask2D>(out var childRectMask2D);
if (childHasScrollRect && !childHasRectMask2D)
{
child.gameObject.AddComponent<RectMask2D>();
}
}
Mask组件和RectMask2D组件的区别:
| 特性 | Mask |
RectMask2D |
|---|---|---|
| 实现原理 | 依赖GPU的模板缓冲区 (Stencil Buffer) | 通过CPU进行矩形区域裁剪,精确剔除被遮挡的UI元素 |
| 性能 | 相对较差。视图外的元素仍参与渲染(但被模板测试丢弃),可能增加GPU开销 | 性能极高 。视图外的元素直接不参与渲染,有效降低DrawCall和Overdraw |
| 灵活性 | 支持任意形状的遮罩(如圆形、星形) | 仅支持矩形遮罩,是最适合ScrollView的形状 |
| 额外开销 | 可能修改材质,破坏UI渲染批处理(Batching) | 无需修改材质,无额外DrawCall |
| 适用场景 | 需要非矩形遮罩效果的UI | 滚动视图、列表等标准矩形裁剪区域 |
为什么ScrollView的标准模板用的是 Mask?
这主要是因为历史原因。在Unity的早期版本中,创建ScrollView时默认使用的是Mask组件。这种使用GPU模板缓冲区的做法在当时很普遍。
但随着UI系统的发展,RectMask2D因其针对性的优化性能更好,已经成为了更优的选择。它的裁剪原理非常适合滚动列表------只渲染显示区域内的内容,区域外的直接跳过,完美契合ScrollView的需求
2.4 禁用Canvas的 Pixel Perfect
-
Pixel Perfect的作用:开启后,Unity会尝试将Canvas下所有UI元素的顶点坐标对齐到屏幕的整数像素位置,避免UI元素因浮点坐标产生"模糊"或"像素边缘锯齿"。
-
性能影响:为了实现这种对齐,Unity需要在每帧或每次UI布局变化时,对每个顶点的位置进行额外的数学运算(舍入、取整等)。对于包含大量UI元素(文本、图片、复杂布局)的Canvas,这部分计算会显著增加CPU开销,特别是UI频繁变动(如滚动列表、动态数值更新)时。
-
禁用后:顶点直接使用原始的浮点坐标渲染,跳过了对齐转换步骤,加快了网格构建和布局更新速度。
-
只有在需要像素级精确显示(如像素风游戏、精细小图标)且性能充裕时,才值得开启此选项。
最佳实践:默认不勾选Pixel Perfect;只有在发现UI有明显模糊且无法通过其他方式(如调整RectTransform锚点、使用整数坐标)解决时,再评估是否开启。

cs
private static void DisablePixelPerfectForCanvasContainingAScrollRect(ref Canvas canvas, ref bool hasScrollRect, Transform child, bool childHasScrollRect)
{
bool childHasCanvas = child.gameObject.TryGetComponent<Canvas>(out var childCanvas);
if (childHasCanvas)
{
canvas = childCanvas;
}
if (childHasScrollRect)
{
hasScrollRect = true;
}
if (hasScrollRect && canvas != null)
{
canvas.pixelPerfect = false;
}
}
三.文档中提到的优化技巧
3.1 减少Prefab内的gameobject
做法:在可能的情况下,减少Prefab内的gameobject数量
效果:
-
降低实例化耗时
每个GameObject及其组件在实例化时都需要分配内存、初始化属性、建立父子关系。对象越多,实例化时的CPU开销越大,尤其是复杂Prefab在运行时动态生成时,影响明显。
-
减少绘制调用(Draw Call)与渲染开销
即使GameObject使用相同材质,若网格、渲染组件或层级结构复杂,也可能导致渲染批次增加。减少不必要的GameObject可降低渲染管线压力。
-
降低场景加载与内存占用
每个GameObject都会增加场景序列化数据量,加载时需解析更多数据。同时,Transform等组件的内存开销虽小,但大量对象累积会显著增加内存占用,并可能触发频繁的GC
减少Prefab内的GameObject数量本质上是降低整体复杂度,减少CPU/内存/渲染管线的负载
3.2 不要使用 Alpha 来隐藏 UI 元素
做法:通过gameObject.SetActive(false)隐藏UI元素,不用通过color.alpha = 0隐藏
原因:
1.即使 Alpha = 0,UI 元素的 Mesh(网格)依然会被提交给 GPU 进行渲染,
2.填充率(Fill Rate)浪费
3.仍然会阻挡射线检测(Raycast)
4.不利于节省DrawCall:UI 系统的合批仍然需要考虑它
5.逻辑与状态的不清晰(比如放在布局组中依然会占位)