You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Three.js与Ammo.js开发3D FPS时,玩家刚体仅绕Y轴旋转的实现问题

解决Ammo.js玩家刚体倾倒、限制仅绕Y轴旋转的方案

直接设置setAngularVelocity只会强制刚体绕Y轴持续旋转,根本没限制X/Z轴的转动自由度,自然会倾倒。给你几个实用的解决思路:

方案1:修改刚体惯性张量,让X/Z轴难以转动

转动惯量越大,刚体越难改变转动状态。创建玩家刚体时,把X和Z轴的转动惯量设为极大值,Y轴保持正常,这样物理引擎很难让它绕X/Z轴转动,自然不会倾倒:

// 初始化玩家刚体时的代码
const mass = 80; // 玩家质量
const collisionShape = new Ammo.btCapsuleShape(0.5, 1.8); // 用胶囊形贴合玩家模型
const startTransform = new Ammo.btTransform();
startTransform.setIdentity();
startTransform.setOrigin(new Ammo.btVector3(0, 2, 0));

// 计算惯性张量,然后修改X/Z轴的值
const inertia = new Ammo.btVector3(0, 0, 0);
collisionShape.calculateLocalInertia(mass, inertia);
// X和Z轴设为极大值,比如10000,Y轴保持原计算值或设为1
inertia.setValue(10000, inertia.y(), 10000);

const motionState = new Ammo.btDefaultMotionState(startTransform);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, collisionShape, inertia);
const playerRigidBody = new Ammo.btRigidBody(rbInfo);

// 记得把刚体加入物理世界
physicsWorld.addRigidBody(playerRigidBody);

方案2:物理更新后强制修正旋转,清零X/Z轴旋转

在动画循环的物理更新步骤后,手动重置玩家刚体的X/Z轴旋转和角速度,只保留Y轴的旋转分量:

// 动画循环中的物理更新后执行
const physicsBody = this.playerEntity.player.userData.physicsBody;
const transform = new Ammo.btTransform();
physicsBody.getMotionState().getWorldTransform(transform);

// 提取当前旋转的欧拉角(注意Ammo用ZYX顺序)
const euler = new Ammo.btVector3(0, 0, 0);
transform.getRotation().getEulerZYX(euler.x(), euler.y(), euler.z());

// 清零X和Z轴的旋转,只保留Y轴
euler.setValue(0, euler.y(), 0);

// 把修正后的欧拉角转回四元数,设置回刚体
const newRotation = new Ammo.btQuaternion();
newRotation.setEulerZYX(euler.x(), euler.y(), euler.z());
transform.setRotation(newRotation);
physicsBody.setWorldTransform(transform);

// 同时清零X/Z轴的角速度,避免物理引擎继续产生旋转
const currentAngularVel = physicsBody.getAngularVelocity();
physicsBody.setAngularVelocity(new Ammo.btVector3(0, currentAngularVel.y(), 0));

方案3:使用6DOF约束限制旋转自由度

用btGeneric6DofConstraint创建约束,直接锁定X和Z轴的旋转自由度,只允许刚体绕Y轴旋转:

// 创建约束:玩家刚体作为约束主体,第二个参数设为null表示绑定到世界
const transformA = new Ammo.btTransform();
transformA.setIdentity();
const transformB = new Ammo.btTransform();
transformB.setIdentity();

const dofConstraint = new Ammo.btGeneric6DofConstraint(
  physicsBody, 
  null, 
  transformA, 
  transformB, 
  false // 不使用参考帧
);

// 设置旋转限制:X和Z轴的最小/最大角度都设为0,Y轴允许360度旋转
dofConstraint.setAngularLowerLimit(new Ammo.btVector3(0, 0, 0));
dofConstraint.setAngularUpperLimit(new Ammo.btVector3(0, Math.PI * 2, 0));

// 把约束加入物理世界
physicsWorld.addConstraint(dofConstraint);

这三个方案里,方案1最省心,靠物理属性本身限制转动;方案2适合需要精细控制旋转的场景;方案3则是用物理引擎的约束系统来实现,稳定性较好。

内容的提问来源于stack exchange,提问作者iona

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 09:34:53