2. 性能瓶颈分析:物理引擎中的碰撞检测开销
做物理引擎优化,第一步不是动手改代码,而是搞清楚——时间到底花在哪了。
我见过太多团队,上来就把碰撞网格减到最低,结果物理表现崩了,帧率也没提升多少。说白了,盲目简化不如精准打击。这一章我们就来聊聊,怎么找到真正的性能瓶颈。
2.1 碰撞检测的开销构成
一次完整的碰撞检测,远不止“两个三角形是否相交”那么简单。我习惯把它拆成三个阶段:
| 阶段 | 主要工作 | 时间复杂度 | 典型占比 |
|---|---|---|---|
| Broad Phase | 空间划分(BVH、四叉树、Sweep-and-Prune) | O(n log n) ~ O(n²) | 10% - 20% |
| Mid Phase | 基本体相交测试(AABB、OBB、球体) | O(1) ~ O(n) | 5% - 15% |
| Narrow Phase | 三角形-三角形精确相交、SAT、GJK+EPA | O(m) 每对 | 60% - 80% |
你看,Narrow Phase 才是大头。Broad Phase 再快,如果 Narrow Phase 里塞了几十万个三角形对,帧率照样崩。
核心结论:碰撞检测的性能瓶颈,90% 在 Narrow Phase 的三角形数量上。网格简化主要影响的就是这个阶段。
2.2 网格复杂度对帧率的真实影响
我记得有一次接手一个开放世界项目,角色碰撞体用了 5000 个三角形的网格。场景里同时有 30 个角色在跑,你算算——
30 × 30 / 2 = 450 对,每对 Narrow Phase 要测 5000 × 5000 = 2500 万次三角形相交。
嗯,帧率直接掉到 15 FPS。
为什么会这样?因为三角形数量对 Narrow Phase 的影响是平方级的。我画了张图帮你理解:
经验法则:碰撞网格的三角形数量每翻一倍,Narrow Phase 开销大约翻 4 倍。所以简化 50% 的三角形,往往能换来 75% 的性能提升。
2.3 Profiling 工具的使用
光靠猜不行,得用数据说话。我常用的 Profiling 工具分三类:
2.3.1 内置 Profiler(Unity Profiler / Unreal Insights)
这些工具能直接看到物理引擎的耗时占比。我个人习惯这样操作:
- 先跑一个典型场景——不是空场景,也不是极限压力场景,而是你游戏里最常见的玩法状态。
- 抓取 5-10 秒的 Profile 数据,重点关注
Physics.Simulate或PhysX::simulate的耗时。 - 看调用栈——是 Broad Phase 慢,还是 Narrow Phase 慢?
注意:Unity Profiler 默认不展开物理子系统的详细调用栈。你需要勾选 "Deep Profile" 或使用 PhysX Visual Debugger 才能看到 Narrow Phase 的细节。
2.3.2 自定义计时(Scoped Timer)
内置工具不够细的时候,我习惯自己埋点。比如这样:
// 一个简单的 Scoped Timer 宏
#define SCOPED_TIMER(name) \
static auto ___timer_##name = Profiler::CreateTimer(#name); \
Profiler::ScopedScope ___scope_##name(___timer_##name);
// 在碰撞检测关键路径上使用
void PhysicsSystem::Update() {
SCOPED_TIMER(Physics_Total);
{
SCOPED_TIMER(BroadPhase);
broadPhase->Update();
}
{
SCOPED_TIMER(NarrowPhase);
narrowPhase->TestPairs();
}
}
这样你就能精确知道每个子阶段花了多少毫秒。我曾经用这个方式发现,某个项目的 Broad Phase 居然占了 40% 的物理时间——原因是场景里塞了 2000 个静态碰撞体,BVH 树重建太频繁了。
2.3.3 第三方工具(RenderDoc / GPA / Intel VTune)
这些工具更多用于 GPU Profiling,但也能帮你发现 CPU 端的物理瓶颈。我一般用它们来验证 CPU Profiler 的结果——有时候 CPU Profiler 显示物理不慢,但帧率就是上不去,这时候可能是物理线程阻塞了渲染线程。
2.4 性能瓶颈定位清单
当你拿到 Profile 数据后,按这个清单排查:
| 现象 | 可能原因 | 下一步行动 |
|---|---|---|
| Broad Phase 耗时 > 30% | 动态物体太多 / 空间划分策略不当 | 检查 BVH 更新频率,考虑使用 Sweep-and-Prune |
| Narrow Phase 耗时 > 70% | 碰撞网格太精细 / 碰撞对太多 | 优先简化网格,或使用多层碰撞体 |
| 物理线程等待时间长 | 物理与渲染线程同步开销大 | 开启异步物理模拟,或减少每帧物理步数 |
| 帧率波动大 | 物理开销不均匀(如某帧突然大量碰撞) | 使用物理预算控制,超时则跳过部分碰撞对 |
我的建议:不要一上来就优化。先跑 Profile,找到真正的瓶颈,再动手。我曾经花了一周优化 Broad Phase,结果发现 Narrow Phase 才是元凶——白干了。
2.5 一个真实案例
最后分享一个我实际处理过的案例。某个射击游戏,角色碰撞体用了 8000 个三角形。Profile 数据如下:
- 物理总耗时:12 ms(目标 4 ms)
- Broad Phase:1.5 ms(正常)
- Narrow Phase:10 ms(严重超标)
- 其中 80% 的时间花在角色-角色碰撞上
解决方案很简单:把角色碰撞网格从 8000 三角减到 500 三角,同时保留视觉网格不变。结果:
- Narrow Phase 降到 1.2 ms
- 物理总耗时降到 2.8 ms
- 帧率从 45 FPS 升到 85 FPS
你看,精准定位 + 针对性简化,效果立竿见影。
避坑指南:我曾经把碰撞网格简化到 100 个三角形,结果角色卡在门框上出不来——因为碰撞体太粗糙,无法准确表达门框的轮廓。简化要有度,保留关键几何特征比一味追求低面数更重要。