在游戏开发和物理仿真流程中,我们常常需要给Blender里的三维模型配备碰撞体,以便引擎能正确计算物体之间的接触、反弹与阻挡。Blender本身并不只靠手动拉盒子来近似形状,它内置了多种自动生成碰撞体的方式,其中最常用的两类就是凸包(Convex Hull)与网格碰撞体(Mesh Collider)。这两种方式在生成逻辑、计算开销和适用场景上差异明显,选错了轻则物理表现失真,重则让实时引擎直接掉帧。

凸包碰撞体的生成原理与操作方式
凸包碰撞体的核心思路是:不管原模型长什么样,都用一个“向外鼓”的多面体把它整个包起来,且这个多面体任何两点连线都不会穿出表面。数学上这等价于计算原网格顶点的凸包,Blender在物理属性面板里将其称为Convex Hull。因为结果一定是凸的,所以像茶杯的把手、建筑物的凹门廊这类凹陷结构,都会被填平变成实心障碍。
在Blender中生成凸包非常直接。选中物体后切换到物理属性,添加刚体并设置类型为主动或被动,然后在碰撞形状下拉菜单里选择Convex Hull即可。也可以用几何体节点或脚本批量处理。下面这段Python代码演示了如何给当前选中物体自动添加刚体并指定凸包碰撞:
import bpy
obj = bpy.context.active_object
if obj:
# 添加刚体物理设置
bpy.ops.rigidbody.object_add()
obj.rigidbody.type = 'PASSIVE'
# 设置碰撞形状为凸包
obj.rigidbody.collision_shape = 'CONVEX_HULL'
print('已为', obj.name, '生成凸包碰撞体')
凸包的最大优势是稳定且极快。由于凸形状在物理引擎中可用专门的算法(如SAT或GJK)快速检测,它几乎不会导致性能瓶颈,而且永远不会出现自身面穿插导致的穿模。缺点也同样突出:一旦模型有内部空腔或明显凹陷,角色就可能站在“空气墙”上,或者小车开不进看起来能进的隧道。因此它更适合用作角色胶囊的近似、箱子石块等凸形道具,以及作为复杂物体的粗略阻挡层。
网格碰撞体的保留细节特性与限制
网格碰撞体不会重新计算外形,而是直接把原始网格的三角面拿去参与碰撞,Blender里对应Mesh碰撞形状。这意味着茶杯把手、阶梯、雕塑褶皱都能被精确识别,物体可以钻进凹处、卡在缝隙里。对于静态场景如地形、建筑外壳、装饰物,网格碰撞体常常是唯一能兼顾视觉与物理一致性的选择。
使用网格碰撞体时,建议在导出前对原模型做适度减面,因为每一面都会增加引擎的窄相检测成本。Blender用户可以在物理面板选择Mesh,并注意勾选“碰撞网格”相关选项。如果通过FBX或glTF导出到Unity、Unreal,需确认目标引擎读取了Blender的网格碰撞而非自动生成盒体。示例代码如下,用于把选中物体的碰撞形状切到网格并开启静态刚体:
import bpy
obj = bpy.context.active_object
if obj:
bpy.ops.rigidbody.object_add()
obj.rigidbody.type = 'PASSIVE'
obj.rigidbody.collision_shape = 'MESH'
# 静态网格碰撞体通常不参与动力学积分
obj.rigidbody.kinematic = False
print(obj.name, '已设为网格碰撞体')
网格碰撞体虽准,但限制很多。首先绝大多数实时引擎不支持用网格碰撞体做动态物体之间的互相碰撞,只能用于静态对动态;其次面数过高会显著拉长每帧的物理耗时,甚至触发引擎跳帧。此外,如果原始网格有开放边或非流形结构,可能产生错误碰撞响应。因此实践中常把网格碰撞体用在不动的地图件上,而动件一律用凸包或基础体。
项目中的组合策略与导出注意事项
真实项目很少二选一,而是组合使用。比如一个带凹陷的机器人在Blender里可保留网格碰撞体供静态环境阻挡,同时为它的躯干附加一层隐形的凸包碰撞体用于角色互推,从而避免凹面计算炸裂。也可以在Blender用父子关系把“显示用高精度模型”和“碰撞用低模”分开,碰撞低模设为凸包或简化网格,导出时只让碰撞低模进物理系统。
导出环节最易出问题。Blender的.blend直接拖进Unity并不会自带刚体设置,需要靠导出插件把碰撞形状写成对应引擎能认的字段。使用FBX时,可把碰撞体命名成UCX_前缀让Unreal识别为自定义碰撞;使用glTF时则常借助扩展字段。下面展示一个在Blender里批量把子级碰撞低模设为凸包并改名的小工具脚本:
import bpy
for obj in bpy.context.selected_objects:
if 'collider' in obj.name.lower():
bpy.context.view_layer.objects.active = obj
bpy.ops.rigidbody.object_add()
obj.rigidbody.collision_shape = 'CONVEX_HULL'
obj.name = 'UCX_' + obj.name
print('处理碰撞体:', obj.name)
最后要提醒,凸包与网格碰撞体都只是形状来源,真正跑得顺不顺还看物体质量、步长和求解器迭代次数。建议在Blender里先用自带刚体世界粗略预览,再进目标引擎微调。理清二者定位,才能让自动生成的碰撞体既省事又靠谱,而不是反过来变成后期优化的沉重包袱。