跳到主要内容

碰撞与触发器

刚体互相碰撞,撞击时播放声音,进入触发区域后回到斜坡上方。

本教程介绍刚体物理、碰撞事件和触发区域。可以在教程项目中探索完成后的场景。

如果在新项目中重建场景,请先启用物理,再添加刚体。

碰撞形状​

Collision 组件定义实体周围的空间区域。单独使用时,它是触发区域;与 Rigid Body 组件组合时,则定义刚体的物理形状。

Type(类型)属性提供七种碰撞形状:

  • Box:长方体。
  • Sphere:球体。
  • Capsule:两端圆润的圆柱体,常用于角色。
  • Cylinder:沿选定局部轴对齐的圆柱体。
  • Cone:沿选定局部轴对齐的圆锥体。
  • Mesh:从 Model 或 Render 资源生成的碰撞形状。与动态刚体一起使用时,请启用 Convex Hull(凸包)。
  • Compound:由后代实体上的基本碰撞形状组合而成。

基本形状的模拟开销通常低于网格形状,因此应选择能合理近似对象的最简单形状。

触发区域​

创建触发区域时,为实体添加 Collision 组件,但不添加 Rigid Body 组件。触发器不会在物理上阻挡其他刚体,而是在动态或运动学刚体穿过边界时发出 triggerenter 和 triggerleave 事件。

本教程在斜坡下方放置大型箱状触发器,用于捕获下落的刚体并将其送回顶部。

编辑器物理视图模式下的教程场景

使用编辑器的 Physics(物理)视图模式,可以在视口中检查碰撞区域和触发区域。

刚体​

Rigid Body 组件使实体参与物理模拟,控制质量、摩擦、恢复系数和阻尼等属性。实体还需要 Collision 组件来定义形状。

默认情况下,同时添加这两个组件会创建静态箱形刚体。

动态刚体组件

Rigid Body 的 Type 决定刚体如何运动:

  • Static(静态):不移动,通常用于环境。
  • Dynamic(动态):由物理模拟控制,响应重力、力和碰撞。
  • Kinematic(运动学):由代码显式移动,可以影响动态刚体,但不响应力。

配置地面​

地面是斜坡下方的大型绿色方块。

地面实体

添加 Render、Collision 和 Rigid Body 组件。将 Collision 类型设为 Box,Half Extents(半尺寸)设为 10, 1, 10,Rigid Body 类型设为 Static。本示例将摩擦设为 0.9,恢复系数设为 0.5,使表面具有较大摩擦和轻微弹性。

配置触发器​

在斜坡下方创建触发器实体。

触发器实体

添加箱形 Collision 组件,将 Half Extents 设为 20, 2, 20,但不要添加 Rigid Body 组件。添加 Script 组件并挂载下面的 trigger 脚本,监听刚体进入区域。

import { Script, Vec3 } from 'playcanvas';

export class Trigger extends Script {
static scriptName = 'trigger';

initialize() {
const triggerEnterEvent = this.entity.collision.on('triggerenter', this.onTriggerEnter, this);

this.on('destroy', () => {
triggerEnterEvent.off();
});
}

onTriggerEnter(entity) {
entity.rigidbody.linearVelocity = Vec3.ZERO;
entity.rigidbody.angularVelocity = Vec3.ZERO;

// Return the body to the top of the ramp.
const position = entity.getPosition();
entity.rigidbody.teleport(position.x, 10, 0);
}
}

刚体进入区域时,triggerenter 事件触发一次。回调收到进入区域的 Entity。也就是说,this.entity 是触发器实体,回调的 entity 参数是下落的刚体。on 的第三个参数将回调的 this 绑定到脚本实例。

脚本销毁时会解除事件监听。在 onTriggerEnter 中,脚本清零刚体的线速度和角速度,保留当前 X 坐标,并使用 rigidbody.teleport 将其移动到斜坡上方。动态刚体必须通过 Rigid Body API 重新定位,不能直接设置实体变换。刚体离开区域时,对应的 triggerleave 事件以相同方式工作。

配置动态刚体​

为箱体、球体和胶囊体添加 Rigid Body 与 Collision 组件。将每个 Rigid Body 的类型设为 Dynamic,并选择与各自形状匹配的 Collision 类型。

箱体实体

示例箱体的质量为 1,摩擦和恢复系数均为 0.5。球体和胶囊体采用相同的刚体设置。

处理碰撞事件​

Collision 组件会为刚体间的物理接触发出三种事件:

  • contact:刚体接触期间,每个物理步都会触发,结果包含该步的全部接触点。
  • collisionstart:刚体开始接触时触发一次。
  • collisionend:刚体结束接触时触发一次。

需要持续更新接触信息时使用 contact。对于撞击音效等单次响应,使用 collisionstart 可避免每个物理步都重复播放。

import { Script } from 'playcanvas';

export class Collider extends Script {
static scriptName = 'collider';

initialize() {
const collisionStartEvent = this.entity.collision.on('collisionstart', this.onCollisionStart, this);

this.on('destroy', () => {
collisionStartEvent.off();
});
}

onCollisionStart() {
this.entity.sound.play('hit');
}
}

将此脚本挂到每个下落实体上,并为其添加含有 hit 槽位的 Sound 组件。处理函数在每次物理碰撞开始时执行一次并播放该槽位。触发区域重叠会发出触发事件,因此不会调用此碰撞处理函数。

现在,场景包含静态地面、动态下落刚体、用于重置它们的触发器,以及播放撞击声音的碰撞事件。更多信息见物理基础和 Collision 组件 API。