关于 physics
基于 BVH 的物理技能,面向使用 @pmndrs/viverse 构建交互场景的开发者。它提供静态与运动学几何体、角色控制器及传感器,并将游戏规则留给项目自行实现。
主要功能
- 01创建 BVH 物理世界
- 02添加静态和运动学刚体
- 03控制角色速度与冲量
- 04查询角色接地状态
- 05检测传感器进入与离开
适用场景
- 01为场景几何体添加碰撞
- 02实现角色移动与跳跃基础
- 03响应角色进入或离开区域
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ drawcall-ai/ skills --skill physics安装后在 Claude Code 中输入/physics确认已加载。
详细介绍
物理
使用 BVH(包围体层次结构)处理静态/运动学几何体。目前,动态刚体之间无法相互作用。
物理世界
创建物理世界
import { BvhPhysicsWorld } from '@pmndrs/viverse'
const world = new BvhPhysicsWorld()
addBody(object, kinematic) — 第二个参数表示刚体是否为运动学刚体(由脚本移动,例如平台或火车),而不是表示它是否静态。对于静态世界几何体(地面、墙壁),传入 false;对于由你自行移动的物体,传入 true。
world.addBody(ground, false) // static
world.addBody(train, true) // kinematic (you move it each frame)
注意这两个参数的含义正好相反:
addBody的标志是kinematic,而下面addSensor的第二个标志是isStatic。
角色控制器
BvhCharacterPhysics 是角色控制器——它移动角色刚体并处理碰撞。它不会为角色制作动画,也不是摄像机。要实现完整的可操控人形角色,请将它与 CharacterCameraBehavior(摄像机)和 Acta 组合使用,由 Acta 负责所有动画及动画产生的运动(参见 acta 技能)。不要使用 SimpleCharacter——它包含自己的动画,会与 Acta 冲突。
创建角色控制器
import { BvhCharacterPhysics, type BvhCharacterPhysicsOptions } from '@pmndrs/viverse'
const characterPhysics = new BvhCharacterPhysics(world)
控制器假定角色对象的原点位于脚部(胶囊体底部)。如果模型的原点位于中心或头部,角色就会悬浮或下沉——请将它包在一个原点位于脚部的 Group 中。可通过可选的 BvhCharacterPhysicsOptions(update 的第三个参数)进行调整:capsuleRadius、capsuleHeight、gravity、maxGroundSlope、updatesPerSecond、linearDamping。
驱动角色
characterPhysics.inputVelocity.copy(velocity) // continuous (walking) — set each frame
characterPhysics.applyVelocity(jumpImpulse) // one-off impulse (e.g. jumping)
对于 Acta 角色,信息流是输入 → Acta → 物理:向 Acta 传入移动意图(相机相对的
moveDirection,长度为 0..1),Acta 会将当前动画转换为期望速度,并在其motion回调中将该速度传给你,然后你在回调中将该速度赋给inputVelocity并更新控制器。不要根据原始输入设置inputVelocity,也不要在其他位置移动角色。参见 acta 技能。
更新控制器
设置速度后,每帧调用:
characterPhysics.update(character, delta) // pass a BvhCharacterPhysicsOptions third arg to tune
接地状态
characterPhysics.isGrounded // true when character is on the ground
控制器不会做的事——请自行实现
控制器只提供两个基本操作——inputVelocity(连续作用)和 applyVelocity(冲量)——以及 isGrounded。具体游戏叠加的规则需要由你编写:
- **多段跳 / 二段跳、土狼时间、跳跃缓冲:**控制器没有跳跃计数器——只有在
isGrounded为真时才允许首次调用applyVelocity,自行记录空中跳跃次数,并在接地时重置。 - 蹬墙跳 / 滑墙:
isGrounded只报告是否接地,不检测墙壁。使用胶囊体发出水平的world.raycast(ray, far)来检测墙壁——返回的交点包含命中距离和表面法线——然后调用applyVelocity,施加远离墙壁并向上的速度。 - **随移动平台移动:**站在运动学平台上不会让角色随之移动。角色站在平台上时,需要自行将平台每帧的位移增量加到角色身上。
- **掉出世界 / 越界:**只有当胶囊体向下进行形状投射并命中碰撞几何体时,
isGrounded才为真——越过有限地面网格的边缘后就没有东西可命中,因此角色会无限下落,并卡在空中/下落姿势(通常看起来像意外的“飞行”或滑翔状态)。控制器不会阻止这种情况。请自行让玩家留在坚实地面上:将位置限制在可碰撞区域内,添加越界地板平面或触发重生的死亡屏障,而不要假定世界是无限的。应将玩家限制在物理网格范围内,该网格可能超出可见游戏区域。
这些属于游戏玩法规则,并非缺失的功能——控制器刻意保持精简,让每款游戏都能自行调整手感。
传感器
传感器会检测角色进入或离开某个区域。
添加传感器
world.addSensor(object, isStatic, (intersected) => {
if (intersected) {
// character entered the sensor
} else {
// character exited the sensor
}
})
移除传感器
world.removeSensor(object)
如果你更希望使用单个回调来获取角色进入的传感器对象,请按以下方式操作
function onEnter(object: Object3D) {}
world.addSensor(obj1, isStatic, (intersected) => intersected && onEnter(obj1))
world.addSensor(obj2, isStatic, (intersected) => intersected && onEnter(obj2))