碰撞与触发器
刚体互相碰撞,撞击时播放声音,进入触发区域后回到斜坡上方。
本教程介绍刚体物理、碰撞事件和触发区域。可以在教程项目中探索完成后的场景。
如果在新项目中重建场景,请先启用物理,再添加刚体。
碰撞形状
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 脚本,监听刚体进入区域。
- ESM
- 传统脚本
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);
}
}
var Trigger = pc.createScript('trigger');
Trigger.prototype.initialize = function () {
var triggerEnterEvent = this.entity.collision.on('triggerenter', this.onTriggerEnter, this);
this.on('destroy', function () {
triggerEnterEvent.off();
});
};
Trigger.prototype.onTriggerEnter = function (entity) {
entity.rigidbody.linearVelocity = pc.Vec3.ZERO;
entity.rigidbody.angularVelocity = pc.Vec3.ZERO;
// Return the body to the top of the ramp.
var 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 可避免每个物理步都重复播放。
- ESM
- 传统脚本
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');
}
}
var Collider = pc.createScript('collider');
Collider.prototype.initialize = function () {
var collisionStartEvent = this.entity.collision.on('collisionstart', this.onCollisionStart, this);
this.on('destroy', function () {
collisionStartEvent.off();
});
};
Collider.prototype.onCollisionStart = function () {
this.entity.sound.play('hit');
};
将此脚本挂到每个下落实体上,并为其添加含有 hit 槽位的 Sound 组件。处理函数在每次物理碰撞开始时执行一次并播放该槽位。触发区域重叠会发出触发事件,因此不会调用此碰撞处理函数。
现在,场景包含静态地面、动态下落刚体、用于重置它们的触发器,以及播放撞击声音的碰撞事件。更多信息见物理基础和 Collision 组件 API。