性能优化入门

19.1 性能优化的核心指标

指标 目标 说明
FPS ≥ 30(移动端),≥ 60(PC) 帧率
Draw Calls < 200(移动端),< 1000(PC) CPU 向 GPU 发送的渲染命令数
Tris/Verts 视平台而定 每帧渲染的三角形/顶点数
SetPass Calls 越低越好 切换渲染状态的次数
Memory 不超设备限制 内存占用(纹理、网格、音频)

19.2 使用 Profiler

Window → Analysis → Profiler(Ctrl+7)

主要模块

模块 用途
CPU Usage 各项操作的 CPU 耗时
Rendering 渲染相关数据(Draw Calls、Tris 等)
Memory 内存分配和回收
GPU Usage GPU 执行时间
Audio 音频性能

Profiler 使用技巧

  • 连接真机进行性能分析(Profile 真实设备表现)
  • 关注 GC Alloc 列,寻找不必要的内存分配
  • Deep Profile 模式(性能开销大但能看到每个函数的调用耗时)
  • 使用 Profiler Markers 标记自定义代码段

19.3 渲染优化

Draw Call 合并

1
2
3
4
5
6
7
8
9
10
11
// 检查当前 Draw Calls
Debug.Log($"当前 Draw Calls: {UnityStats.drawCalls}");

// 开启静态合批(Static Batching)
// 将 Static 物体标记为 Batching Static

// 动态合批(Dynamic Batching)
// Unity 自动合拼小物体(要求同材质、顶点限制等)

// GPU Instancing(针对大量相同物体)
// 材质开启 Enable GPU Instancing

优化原则

  • 材质数量越少越好:多个物体共用同一材质减少 SetPass Calls
  • 纹理图集(Atlas):将多个小纹理合并为一张大纹理
  • LOD(Level of Detail):远距离使用低面模型
  • 遮挡剔除(Occlusion Culling):被遮挡的物体不渲染
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 手动控制物体的可见性
public class LODController : MonoBehaviour
{
[SerializeField] private GameObject highDetail;
[SerializeField] private GameObject lowDetail;
[SerializeField] private float distanceThreshold = 20f;

void Update()
{
float dist = Vector3.Distance(
transform.position,
Camera.main.transform.position
);

highDetail.SetActive(dist < distanceThreshold);
lowDetail.SetActive(dist >= distanceThreshold);
}
}

19.4 脚本优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// ❌ 避免:每帧 Find
void Update()
{
GameObject player = GameObject.Find("Player");
}

// ✅ 推荐:缓存引用
private Transform player;

void Start()
{
player = GameObject.Find("Player").transform;
}

void Update()
{
// 使用缓存的 player 引用
}

// ❌ 避免:每帧分配内存
void Update()
{
Vector3[] positions = new Vector3[10]; // 每帧分配
}

// ✅ 推荐:重用数组
private Vector3[] positions = new Vector3[10];

void Update()
{
// 重用 positions 数组
}

// ❌ 避免:字符串拼接
void Update()
{
Debug.Log("Health: " + health + "/" + maxHealth);
}

// ✅ 推荐:使用 StringBuilder 或 string.Format
private StringBuilder sb = new StringBuilder(64);

void Update()
{
sb.Clear();
sb.Append("Health: ").Append(health).Append("/").Append(maxHealth);
Debug.Log(sb.ToString());
}

19.5 内存优化

资源管理

1
2
3
4
5
6
7
// 使用 AssetBundle 按需加载
AssetBundle bundle = AssetBundle.LoadFromFile(path);
GameObject obj = bundle.LoadAsset<GameObject>("Enemy");
bundle.Unload(false); // 卸载 AssetBundle,保留已加载的对象

// Resources 目录的资源随场景常驻内存
// 尽量使用 Addressables 或 AssetBundle 管理

纹理压缩

平台 推荐格式
iOS ASTC
Android ETC2(支持 OpenGL ES 3.0)
Desktop DXT / BC7

其他技巧

  • 使用 Sprite Atlas 合并 2D 图集
  • 禁用不必要的组件(如禁用未使用的 AudioSource)
  • 对象池(详见第 16 章)减少 GC 压力
  • 使用 Object.DontDestroyOnLoad 谨慎(对象不会被卸载)

19.6 各平台优化目标参考

平台 目标 FPS 建议 Draw Calls 建议 Tris
低端 Android 30 < 100 < 100k
中端 Android 30-60 < 200 < 200k
iOS(iPhone 12+) 60 < 300 < 300k
PC 60+ < 1000 < 500k
Console 30-60 < 800 < 500k

19.7 本章小结

  • 性能优化的核心是减少 Draw Calls 和内存分配
  • Profiler 是定位性能瓶颈的首选工具
  • 静态合批、GPU Instancing、LOD、遮挡剔除是渲染优化四大手段
  • 脚本优化重在缓存引用和避免每帧分配
  • 纹理压缩和资源按需加载减少内存占用
  • 优化应该先定位瓶颈再精准优化,避免过度优化

练习题

  1. 使用 Profiler 分析场景,找出 CPU 耗时最高的模块
  2. 在场景中放置 1000 个 Cube,对比开启/关闭静态合批的 Draw Calls
  3. 用对象池优化一段频繁 Instantiate/Destroy 的代码,对比 GC Alloc
  4. 为角色模型设置 LOD Group,观察不同距离的面数和性能变化
ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
Unity3D开发基础教程 系列
第 19/20 篇
分享: