使用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
相关产品推荐
相关产品推荐

