跳到主要内容

碰撞形状

collision 组件为实体提供物理形状。与 rigidbody 配合时,定义刚体形状;单独使用时充当触发体积。形状不必完全匹配外观,通常也不应该:物理引擎每秒反复检测形状,给精细模型包围简单盒体或胶囊,比逐面匹配的网格更快、更稳定。

形状类型​

类型属性说明
BoxHalf Extents默认类型,表示宽、高、深的一半;0.5, 0.5, 0.5 对应边长一个单位的立方体
SphereRadius球体
CapsuleRadius、Height、Axis两端圆润的圆柱,常用于角色;高度沿所选局部轴从一端顶点量到另一端
CylinderRadius、Height、Axis圆柱
ConeRadius、Height、Axis圆锥
MeshModel Asset 或 Render Asset、Convex Hull来自资源的几何体,见网格碰撞体
Compound将子实体形状组合为一个刚体,见复合形状

基本几何体比网格模拟开销低得多,应使用合理贴合对象的最简单形状。角色通常使用沿 Y 轴站立的胶囊:

player.addComponent('collision', {
type: 'capsule',
radius: 0.5,
height: 2,
axis: 1 // 0 = X, 1 = Y (the default), 2 = Z
});

网格碰撞体​

网格碰撞体从模型或渲染资源获得几何体,通常与实体显示的资源相同,分两类:

  • 三角形网格:默认方式,精确匹配几何体,包括凹陷,只能用于静态、运动学刚体及触发器。
  • 凸包:用包含几何体的最小凸形状包裹模型,会丢失碗内部等凹陷,但这是动态刚体唯一支持的网格形状。

从大型模型构建网格形状需要时间和内存。移动对象优先使用基本形状或复合形状,三角形网格主要用于环境。

// The environment: a static triangle mesh built from the render asset it draws with
ground.addComponent('collision', {
type: 'mesh',
renderAsset: groundAsset
});

// A dynamic prop needs a convex hull
crate.addComponent('collision', {
type: 'mesh',
renderAsset: crateAsset,
convexHull: true
});

形状偏移​

每种形状都支持 Position Offset 和 Rotation Offset,相对于实体原点移动与旋转。例如角色原点在脚底,胶囊就需要向上偏移半个高度,避免仅为偏移碰撞体而增加子实体。

// Raise the capsule so that the entity's origin sits at the character's feet
player.addComponent('collision', {
type: 'capsule',
radius: 0.5,
height: 2,
linearOffset: new pc.Vec3(0, 1, 0)
});

// Tilt a box by 45 degrees about the entity's Y axis
crate.collision.angularOffset = new pc.Quat().setFromEulerAngles(0, 45, 0);
偏移碰撞形状

复合形状​

复合形状将子实体碰撞形状组合为一个刚体,让动态刚体无需网格碰撞体也能具备复杂轮廓。例如椅子由盒形座面、靠背和四条腿组成,仍能与其他动态刚体碰撞,而凹网格不能。复合形状也比网格模拟开销低。

父实体包含 Compound 类型碰撞组件和刚体组件。各子实体包含基本几何碰撞组件,位置与旋转相对于父实体,无需独立刚体。父刚体拥有整个形状;具有自身 rigidbody 的后代视为独立刚体,不加入复合形状。

// The parent owns the body and combines the children's shapes
const chair = new pc.Entity('chair');
chair.addComponent('collision', { type: 'compound' });
chair.addComponent('rigidbody', { type: pc.BODYTYPE_DYNAMIC, mass: 5 });

// Children carry the shapes, positioned relative to the parent, and need no rigidbody
const seat = new pc.Entity('seat');
seat.addComponent('collision', { type: 'box', halfExtents: new pc.Vec3(0.25, 0.025, 0.25) });
seat.setLocalPosition(0, 0.45, 0);
chair.addChild(seat);

复合形状椅子

动态或运动学复合形状的子实体可以在运行时移动。变换变化会更新形状在复合体中的偏移,并唤醒父刚体。静态复合体与其他静态刚体一样,保留创建时形状。父实体也是质心,应位于形状内部,通常在中心,否则施加力或扭矩时可能产生绕隐形支点旋转等异常。

复合碰撞形状

上面的椅子来自复合物理形状教程。

缩放​

基本几何形状忽略缩放。盒体始终使用以世界单位表示的半尺寸,不受实体和父实体缩放影响,因此放大实体不会放大碰撞体。所有刚体类型、触发器、场景设置和运行时缩放均遵循此规则。

原因是物理引擎中的刚体变换只有位置和旋转,没有缩放。尺寸属于碰撞形状,改变尺寸就需要新形状。因此 PlayCanvas 只将实体位置和旋转传给刚体,基本形状按自身属性构建。

调整基本形状尺寸,应修改这些属性。引擎立即重建形状和刚体,动态刚体保留速度:

// Double the size of a one unit box collider
entity.collision.halfExtents = new pc.Vec3(1, 1, 1);

网格例外,因为其几何体按实体缩放显示。网格碰撞形状按实体世界缩放构建,也包括内部模型节点的缩放。实体或祖先导致世界缩放变化时,会在下一物理步开始时重建;仅缩放模型内部节点不会重建。每次重建都会重建刚体,避免逐帧修改网格碰撞体缩放。

备注

按新缩放重建网格,需要 ammo.js 包含 btScaledBvhTriangleMeshShape。引擎仓库从 2.23 起的构建,以及入门中 Web Components 加载的本站文件包含该功能;PlayCanvas React 使用的 sync-ammo 0.1.2 不包含。旧构建会保留首次缩放,共享同一网格但缩放不同的实体也会使用首次创建的三角网格尺寸。调试引擎首次创建缩放网格时会警告。可通过 typeof Ammo.btScaledBvhTriangleMeshShape === 'function' 判断。

复合体子实体的基本形状也保留自身尺寸。上层缩放仍会改变位置,因此复合体各部分会分开,而非变大。建议复合体不缩放,通过各部分的形状属性设置尺寸。

显示形状​

编辑器会在视口中勾勒所选实体的碰撞形状,便于检查尺寸和偏移。运行时要显示所有形状,可在任意开发方式下为实体添加 render-physics.js,启用 Draw Shapes,以半透明网格显示碰撞形状。

另请参阅​