关于 unity-performance
面向 Unity 游戏开发者的性能分析与优化参考,涵盖 CPU、GPU、内存、渲染和平台差异,并提供反复分析与对比的工作流程。
主要功能
- 01Unity 性能分析流程
- 02CPU 与垃圾回收优化
- 03GPU 与渲染优化技巧
- 04内存与资源加载指导
- 05平台性能注意事项
适用场景
- 01排查目标设备上的帧耗时瓶颈
- 02减少游戏运行时分配和对象创建
- 03优化复杂场景的渲染
- 04管理不同平台的内存与资源加载
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ josiahsiegel/ claude-plugin-marketplace --skill unity-performance安装后在 Claude Code 中输入/unity-performance确认已加载。
详细介绍
Unity 性能优化
概述
采用系统化方法分析和优化 Unity 游戏。涵盖性能分析工具、CPU/GPU 优化、内存管理、渲染优化和平台特定注意事项。
性能分析工具
| 工具 | 显示内容 | 使用时机 |
|---|---|---|
| Unity Profiler | 每帧的 CPU、GPU、内存、音频和物理数据 | 处理任何性能问题时的第一站 |
| Frame Debugger | 绘制调用细分、着色器/材质状态 | 渲染瓶颈 |
| Memory Profiler | 堆快照、纹理/网格内存 | 内存泄漏、内存膨胀 |
| Profile Analyzer | 对比采集数据、统计分析 | 优化前后对比 |
| Physics Debugger | 碰撞体可视化、接触点 | 物理性能 |
Profiler 工作流
- 启用 Development Build 和 Autoconnect Profiler 进行构建
- 在目标设备上进行性能分析(不要在 Editor 中进行——Editor 的额外开销会扭曲结果)
- 确定瓶颈类别:受 CPU 限制、受 GPU 限制,或内存压力
- 深入分析导致问题的具体系统
- 优化后重新进行性能分析并比较
读取 Profiler
If frame time > 16.6ms (60 FPS target):
CPU timeline > GPU timeline -> CPU-bound
GPU timeline > CPU timeline -> GPU-bound
GC.Alloc column shows per-frame allocations -> GC pressure
留意尖峰(单帧异常)与持续高耗时(基准负载过重)的区别。
CPU 优化
减少每帧分配(GC)
| 反模式 | 修复方法 |
|---|---|
在 Update 中使用 string + string | 使用 StringBuilder 或缓存结果 |
每帧执行 new List<T>() | 只分配一次,然后用 Clear() 清空并复用 |
| 在热点路径中使用 LINQ | 改用手动循环 |
每帧调用 GetComponent<T>() | 在 Awake/Start 中缓存 |
每帧调用 GameObject.Find() | 缓存引用或使用事件 |
对非泛型集合使用 foreach | 使用 for 循环或泛型集合 |
SendMessage() / BroadcastMessage() | 使用直接调用、事件或接口 |
| 装箱值类型 | 使用泛型集合,避免转换为 object |
对象池
public class ObjectPool<T> where T : Component
{
readonly Queue<T> _pool = new();
readonly T _prefab;
readonly Transform _parent;
public ObjectPool(T prefab, int preWarm, Transform parent = null)
{
_prefab = prefab;
_parent = parent;
for (int i = 0; i < preWarm; i++)
_pool.Enqueue(CreateInstance());
}
public T Get(Vector3 position, Quaternion rotation)
{
var obj = _pool.Count > 0 ? _pool.Dequeue() : CreateInstance();
obj.transform.SetPositionAndRotation(position, rotation);
obj.gameObject.SetActive(true);
return obj;
}
public void Return(T obj)
{
obj.gameObject.SetActive(false);
_pool.Enqueue(obj);
}
T CreateInstance()
{
var obj = Object.Instantiate(_prefab, _parent);
obj.gameObject.SetActive(false);
return obj;
}
}
对频繁实例化/销毁的对象使用对象池,例如子弹、粒子、敌人和 UI 元素。Unity 2021+ 还内置了 UnityEngine.Pool.ObjectPool<T>。
Update 优化
| 技术 | 说明 |
|---|---|
| 错开更新 | 不要每帧更新所有 AI;使用分组轮询 |
| 基于距离的 LOD | 降低远处对象的更新频率 |
| 事件驱动 | 尽可能用事件替代轮询 |
| 禁用未使用的脚本 | 对屏幕外组件设置 enabled = false |
使用 InvokeRepeating | 用于定期检查(比让协程挂起更省开销) |
GPU / 渲染优化
减少绘制调用
| 技术 | 方法 | 节省效果 |
|---|---|---|
| 静态合批 | 将不移动的对象标记为 Static | 在构建时合并网格 |
| 动态合批 | 自动处理小型网格(<300 顶点) | 仅适用于 URP/Built-in |
| GPU 实例化 | 为重复对象启用材质实例化 | 树木、草地、岩石 |
| SRP Batcher | URP/HDRP 中默认启用 | 减少 SetPass 调用 |
| 纹理图集 | 将纹理合并到图集中 | 减少材质切换 |
| 网格合并 | 在运行时使用 CombineMeshes() | 自定义合批 |
LOD(细节层次)
LOD Group Setup:
LOD 0 (0-30%): Full-detail mesh (5000 tris)
LOD 1 (30-60%): Medium mesh (2000 tris)
LOD 2 (60-90%): Low mesh (500 tris)
Culled (90%+): Not rendered
网格可以使用 LOD,同时也要减少远处对象的脚本复杂度、粒子数量和物理计算。
遮挡剔除
为室内或复杂场景烘焙遮挡数据。将大型静态遮挡物(墙壁、地板)标记为遮挡物。根据场景规模配置单元格大小。使用 Occlusion Culling 窗口进行可视化和测试。
着色器优化
| 问题 | 解决方案 |
|---|---|
| 片元着色器过于复杂 | 减少纹理采样次数,简化数学运算 |
| 过度绘制 | 尽量减少透明对象,尽可能使用不透明对象 |
| 变体过多 | 在构建设置中剔除未使用的着色器变体 |
| 后处理开销过大 | 在移动端禁用效果,改用开销更低的替代方案 |
内存管理
常见内存问题
| 问题 | 症状 | 修复方法 |
|---|---|---|
| 纹理臃肿 | 内存占用高,加载时间长 | 压缩纹理,并按平台降低最大尺寸 |
| 已卸载的场景仍持有引用 | 内存随时间不断增长 | 场景切换后使用 Resources.UnloadUnusedAssets() |
| 未释放 Addressables | 资源包仍留在内存中 | 调用 Addressables.Release(handle) |
| 音频剪辑未压缩 | 内存占用巨大 | 音乐使用内存压缩;音效在加载时解压 |
| 网格启用了读/写 | 每个网格的内存占用翻倍 | 如果运行时不需要读/写,则将其禁用 |
各平台的纹理压缩
| 平台 | 格式 | 备注 |
|---|---|---|
| PC/Console | BC7 (DXT) | 质量与大小的比率最佳 |
| Android | ASTC 6x6 | 通用格式,质量可调 |
| iOS | ASTC 6x6 | 与 Android 相同 |
| WebGL | ETC2 / DXT | 取决于目标 GPU |
在纹理导入设置中使用“Override for [Platform]”。将纹理最大尺寸设为所需的最小值(UI 图标为 512,道具为 1024,主视觉资源为 2048)。
平台特定注意事项
| 平台 | 主要限制 |
|---|---|
| Mobile | 热降频、内存有限、电池消耗、受填充率限制 |
| WebGL | 不支持线程(Unity 6 之前),下载体积大,不支持计算着色器 |
| Console | 认证要求、硬件固定、内存预算 |
| VR/XR | 最低 72-90 FPS、立体渲染开销、帧率下降会导致晕动症 |
Addressables 和资源加载
使用 Addressables 异步加载资源,避免加载时卡顿:
// Preload during loading screen
var handle = Addressables.LoadAssetAsync<GameObject>("enemy_boss");
await handle;
// Release when done
Addressables.Release(handle);
使用 Addressable 组控制资源包粒度。将不常使用的资源标记为远程/按需加载。使用 Addressables Event Viewer 分析资源包内存占用。
快速优化检查清单
- 在目标设备上分析性能,而不是在 Editor 中分析
- 游戏逻辑代码中每帧 GC 分配为零
- 为所有频繁生成的对象使用对象池
- 为静态对象启用静态批处理
- 为所有会在不同距离下显示的 3D 模型设置 LOD 组
- 按平台压缩纹理,并设置合适的最大尺寸
- 禁用运行时不会修改的网格和纹理的读/写
- 为音频剪辑设置合适的压缩选项
- 为室内/复杂场景烘焙遮挡剔除
- 在构建设置中剥离着色器变体
更多资源
参考文件
references/profiling-deep-dive.md-- Profiler 高级用法、内存分析器快照、逐帧分析、自定义 Profiler 标记、自动化性能测试、构建体积分析、ECS/DOTS 性能模式
评论
支持 Markdown