如何让Cannon.js物理刚体以固定速度朝动态目标移动?
解决Cannon.js刚体固定速度追踪目标的问题
我完全懂你的困扰——直接拿位置差当速度确实会让追踪的刚体“疯跑”,距离越远速度越快,完全没法控制节奏。下面给你几个实用的方案,完美实现固定速度的AI追踪,直到碰撞目标:
1. 基础方案:归一化方向向量(最直接)
核心思路是先算出追踪的方向向量,把它变成长度为1的单位向量,再乘以你想要的固定速度,这样不管距离多远,刚体的移动速度都是恒定的。
代码示例:
// 计算目标与当前刚体的方向向量(这里假设是X-Z平面的2D追踪,Y轴设为0) const direction = new CANNON.Vec3( target.position.x - body.position.x, 0, target.position.z - body.position.z ); // 归一化方向向量:把向量长度缩为1,确保后续乘以速度后大小固定 direction.normalize(); // 设置固定移动速度,比如设为5(数值根据你的场景调整) const moveSpeed = 5; body.velocity.x = direction.x * moveSpeed; body.velocity.z = direction.z * moveSpeed;
⚠️ 注意:如果两个物体位置完全重合,归一化会出问题,所以可以加个距离判断:
const distance = direction.length(); if (distance > 0.1) { // 小于0.1就认为已经接近目标,停止移动 direction.normalize(); body.velocity.x = direction.x * moveSpeed; body.velocity.z = direction.z * moveSpeed; } else { body.velocity.set(0, 0, 0); }
2. 物理真实方案:用冲量/力控制(推荐)
直接修改velocity有时候会显得很生硬,尤其是当刚体受到摩擦力、重力等其他力影响时。用applyImpulse或applyForce可以让追踪更符合物理引擎的特性,自动补偿外力的影响。
代码示例(用冲量):
const moveSpeed = 5; const direction = new CANNON.Vec3( target.position.x - body.position.x, 0, target.position.z - body.position.z ); direction.normalize(); // 计算期望的目标速度 const desiredVelocity = direction.scale(moveSpeed); // 计算当前速度与目标速度的差值 const velocityDelta = desiredVelocity.vsub(body.velocity); // 冲量 = 质量 × 速度变化,直接施加冲量让刚体快速趋近目标速度 const impulse = velocityDelta.scale(body.mass); body.applyImpulse(impulse, body.position);
这个方法的好处是:如果刚体在移动中碰到障碍物、受到摩擦力减速,引擎会自动调整冲量,保持你设置的追踪速度,效果更真实。
3. 流畅AI优化:平滑转向
如果要让AI的追踪更自然,不会突然猛转方向,可以加个缓动因子,让速度平滑过渡:
const moveSpeed = 5; const smoothFactor = 0.1; // 数值越小转向越平滑,范围0-1 const direction = new CANNON.Vec3( target.position.x - body.position.x, 0, target.position.z - body.position.z ); direction.normalize(); const desiredVelocity = direction.scale(moveSpeed); // 缓动更新速度,让转向更丝滑 body.velocity.x = body.velocity.x + (desiredVelocity.x - body.velocity.x) * smoothFactor; body.velocity.z = body.velocity.z + (desiredVelocity.z - body.velocity.z) * smoothFactor;
碰撞后停止追踪
最后,如果你需要刚体碰到目标后立刻停止,可以监听Cannon.js的碰撞事件:
body.addEventListener('collide', (event) => { // 判断碰撞的物体是不是目标 if (event.body === target) { // 停止移动 body.velocity.set(0, 0, 0); // 这里可以加碰撞后的逻辑,比如触发攻击、播放动画等 } });
根据你的场景选对应的方案就行:简单需求用归一化方向,追求物理真实用冲量,做流畅AI就加平滑转向~
内容的提问来源于stack exchange,提问作者Matt DeVenge
相关产品推荐
相关产品推荐

