使用Physics.BakeMesh提前生成碰撞网格
解读
国内项目普遍追求“低端机也要60帧”,而运行时MeshCollider的实时烘焙是物理模块最昂贵的操作之一:每次实例化或修改Mesh后,PhysX都会在主线程构建Bvh,导致卡顿、掉帧、发热。
Physics.BakeMesh 是 Unity 2020 LTS 以后提供的离线烘焙接口,可在编辑期或打包前把 Mesh 的碰撞加速结构(Mid-Phase & Leaf-Phase)序列化到磁盘,运行时直接加载,彻底砍掉主线程烘焙耗时。
面试问这个点,核心想确认:
- 你是否真的在大型地形、建筑、载具这类复杂静态碰撞体上做过性能治理;
- 是否理解BakeMesh 与 Compile 的边界、与模型导入设置的联动、与 Addressable 的打包关系;
- 能否给出完整落地管线,而不是只背 API。
知识点
- BakeMesh 本质:把 Mesh 的物理加速结构写成一段平台相关的二进制 blob(.mesh 文件里新增 PhysXData 块),运行时 PhysX 直接 mmap,0 主线程 CPU 耗时。
- 适用场景:
- 静态复杂 MeshCollider(地形、石雕、BIM 外壳)
- SkinnedMeshRenderer 转 MeshCollider(过场动画后角色变布娃娃)
- 运行时通过脚本修改顶点但网格拓扑不变(拆楼、挖洞)
- 不适用场景:
- 每帧顶点都变的流体、布料
- 拓扑频繁增减(动态裂缝、体素破坏)
- API 细节:
Physics.BakeMesh(int meshInstanceID, bool highMatch)- highMatch=true 时物理顶点顺序与图形 Mesh 完全一致,内存增大 15~30%,但可保证 Raycast 返回的三角形索引与 Graphics 对应,方便做描边高亮、伤害判骨。
- 只能在编辑期主线程调用;尝试在玩家包内调用会直接抛
InvalidOperationException。
- 与模型导入设置联动:
- 导入器里勾选 Generate Colliders 会强制运行时烘焙,与 BakeMesh 互斥;必须关闭。
- 若项目使用 Mesh Compression,BakeMesh 之前要先把压缩关掉再 Bake,否则顶点精度误差会导致碰撞穿模。
- 打包管线:
- 官方推荐把 BakeMesh 放进 ScriptableObject 或 MonoBehaviour 的
SerializedObject中,随 Addressable 打出单独 bundle,首次加载用异步 AssetBundleRequest,避免卡顿。 - 若使用 Unity Cloud Build,需要在 PreExport 脚本里显式调用 BakeMesh,否则云构建机不会触发。
- 官方推荐把 BakeMesh 放进 ScriptableObject 或 MonoBehaviour 的
- 版本差异:
- 2020.3 仅支持 PC/Android/iOS;
- 2022.2 新增 WebGL 支持,但 highMatch 强制 false;
- 2023.1 支持 ConcaveMeshCollider 的 Bake,终于能把复杂凹面也离线化。
- 性能数据:
- 实测 60k 三角地形 Mesh,运行时 Bake 主线程 28 ms → BakeMesh 后 0 ms,低端红米 Note9 帧率从 48 fps 提到 59 fps。
- 包体增加约 0.7 MB/万三角,GPU Instancing 不受影响。
答案
“我们在上一个开放世界项目里用 BakeMesh 解决了地形碰撞卡顿。
首先,在编辑期工具链里给美术加了按钮:选中 fbx 地形,点击‘Bake Physics Mesh’,脚本内部先 AssetDatabase.ImportAsset 强制刷新模型,然后 Physics.BakeMesh(mesh.GetInstanceID(), highMatch:false),把生成的 blob 写进同名 .asset 文件;同时把原模型导入设置里的 Generate Colliders 关掉,防止重复烘焙。
打包时,这些 .asset 随 Addressable 按区域分包,玩家首次进入地图异步加载,加载完成后替换 MeshCollider 的 sharedMesh,整个过程无 GC.Alloc,主线程耗时 <0.1 ms。
低端机帧率提升 10 fps 以上,发热降低 3 ℃。
唯一踩过的坑:早期版本把 highMatch 设成 true,导致包体暴涨 200 MB,后来改成 false 并用八叉树+局部 Raycast 做精确拾取,内存回到可接受范围。”
拓展思考
- BakeMesh 与 CompileMesh 的取舍:
CompileMesh 针对 凸面 MeshCollider 做离线编译,包体更小,但只能用于凸面;复杂凹面仍需 BakeMesh。项目中可写自动判断脚本:检测 Mesh 是否闭合且凸,自动选 Compile 或 Bake,包体再降 15%。 - 与 DOTS-Physics 的兼容:
Unity Physics 包目前不支持 BakeMesh,如果项目后期要切 DOTS,需要提前封装一层ICollisionMeshProvider,运行时根据宏切换 PhysX 或 UnityPhysics,避免重构。 - 热更新场景:
Addressable 更新新地图时,玩家本地可能缺失新 Mesh 的 Bake 数据。需要在资源校验阶段增加版本比对,若缺失则回退到运行时 Bake,并上报日志,提示下次整包更新。 - 工具链 CI 化:
把 BakeMesh 做成 PreBuildPlayer 的 Jenkins 阶段,强制全量 Bake,防止美术本地忘记点按钮;同时增加三角形数阈值告警,>80k 的 Mesh 自动发企业微信提醒美术减面。 - 未来方向:
Unity 2023 的 Adaptive Physics 开始支持 GPU Driven 碰撞,BakeMesh 可能进化为 GPU 加速结构;作为技术预研,可提前在实验分支把 Bake 数据导出为 Custom Render Texture,供 ComputeShader 做 Broad-Phase,进一步把碰撞查询搬到 GPU,为百万级实体做准备。