射线检测
射线检测查询两个 3D 点之间的直线是否与碰撞形状相交,以及交点位置。方法位于刚体系统 app.systems.rigidbody,各开发方式用法相同,只需应用和两个点。
通过射线拾取
常见用途是拾取:用户点击或触摸屏幕选中实体。下面从相机穿过指针位置发射射线,返回最近命中刚体:
射线检测
射线从相机出发,结束于指针映射到远裁剪平面的位置,因此穿过指针下的对象:
function pick(screenX, screenY) {
// From the camera position...
const from = cameraEntity.getPosition();
// ...to the pointer position projected onto the far clip plane
const to = cameraEntity.camera.screenToWorld(screenX, screenY, cameraEntity.camera.farClip);
// Return the closest hit, or null
const result = app.systems.rigidbody.raycastFirst(from, to);
if (result) {
console.log(`You selected ${result.entity.name}`);
}
}
app.mouse.on(pc.EVENT_MOUSEDOWN, (event) => pick(event.x, event.y));
app.touch?.on(pc.EVENT_TOUCHSTART, (event) => pick(event.touches[0].x, event.touches[0].y));
编辑器中可放到相机实体脚本,以 this.app 和 this.entity 代替 app、cameraEntity;物理实体拾取提供完整脚本。React 使用 useApp(),Web Components 通过 whenReady('pc-app') 访问。相关模式见移动刚体。
射线结果
刚体系统提供两个方法:
raycastFirst(start, end, options)返回最近命中,未命中返回null。raycastAll(start, end, options)返回全部命中数组,未命中为空。默认无序,传入sort: true可按由近到远排序。
每个命中为 RaycastResult:
| 属性 | 说明 |
|---|---|
entity | 碰撞形状被命中的实体 |
point | 世界空间命中位置 |
normal | 命中表面的世界空间法线 |
hitFraction | 命中位置沿射线的比例,start 为 0,end 为 1 |
射线同时命中刚体和触发体积,可通过以下筛选跳过触发器。
筛选射线命中
两个方法均接受选项对象:
| 选项 | 说明 |
|---|---|
filterTags | 仅匹配指定标签。格式为将 Tags#has 参数放入数组:['enemy'] 要求该标签,['enemy', 'boss'] 要求同时具有两者,[['red', 'blue']] 要求任一 |
filterCallback | 接收各命中实体,返回 true 保留 |
filterCollisionGroup | 射线的碰撞组,与各刚体掩码比较 |
filterCollisionMask | 射线的碰撞掩码,与各刚体组比较 |
sort | 仅 raycastAll,按由近到远排序 |
// Find every enemy the shot passes through, nearest first
const hits = app.systems.rigidbody.raycastAll(from, to, {
filterTags: ['enemy'],
sort: true
});
for (const hit of hits) {
hit.entity.script.health.damage(10 * (1 - hit.hitFraction));
}
// Pick anything except the player's own body
const result = app.systems.rigidbody.raycastFirst(from, to, {
filterCallback: (entity) => entity !== player
});
标签或回调筛选需要沿射线取得全部命中,因此 raycastFirst 使用它们时工作量与 raycastAll 相同。组和掩码在物理引擎内部筛选,开销更低。按标签射线检测展示完整项目。
探测环境
实体也可通过射线探测周围。例如判断角色是否站在地面,从角色位置向下发射短射线,检查是否命中自身以外的对象:
const from = player.getPosition();
const to = new pc.Vec3(from.x, from.y - 1.1, from.z);
const hit = app.systems.rigidbody.raycastFirst(from, to, {
filterCallback: (entity) => entity !== player
});
const onGround = hit !== null;
另请参阅
- 物理实体拾取:编辑器完整拾取脚本。
- 按标签射线检测:使用
filterTags。 - 通过物理放置实体:在射线命中地面的位置生成对象。
- RigidBodyComponentSystem:
raycastFirst和raycastAllAPI。