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

使用ThreeJS&AmmoJS实现软物体仅沿Y轴下落,禁用X、Z轴偏移倾斜

看起来你在做软物体物理模拟的时候遇到了「只想让它沿Y轴下落、X/Z轴不偏移也不倾斜」的问题,我刚好有几个可行的解决方案,帮你搞定它~

解决方案思路与代码示例

问题的核心在于:Ammo的软物体物理模拟默认会受自身刚体特性、碰撞反馈影响,单纯修改ThreeJS的模型位置是不够的,必须从物理引擎层面限制物体的运动自由度,或者在物理模拟后强制同步正确的状态。

方案一:使用Ammo的6自由度约束(推荐)

这是最“物理原生”的方式,通过约束直接锁定X/Z轴的平移和所有旋转自由度,只允许Y轴方向的移动。这种方式不会有“强制重置”的生硬感,物理模拟会更自然。

代码示例:

// 假设你已经创建了软物体softBody和物理世界physicsWorld
const initialPos = new Ammo.btVector3(0, 5, 0); // 替换成你的初始X/Z位置

// 创建一个静态刚体作为约束的固定端(用来绑定世界空间位置)
const fixedBody = new Ammo.btRigidBody(
  0, // 质量为0表示静态刚体
  new Ammo.btDefaultMotionState(),
  new Ammo.btBoxShape(new Ammo.btVector3(0.1, 0.1, 0.1)) // 随便一个小形状,不影响约束
);
physicsWorld.addRigidBody(fixedBody);

// 创建6自由度约束,连接软物体和静态刚体
const dofConstraint = new Ammo.btGeneric6DofConstraint(
  softBody,
  fixedBody,
  new Ammo.btTransform().setIdentity(), // 软物体本地变换
  new Ammo.btTransform().setOrigin(initialPos), // 静态刚体的位置(也就是我们要锁定的X/Z位置)
  true
);

// 锁定X、Z轴的平移:上下限设为0,不让它移动
dofConstraint.setLinearLowerLimit(new Ammo.btVector3(0, 0, 0));
dofConstraint.setLinearUpperLimit(new Ammo.btVector3(0, 100, 0)); // Y轴允许向下移动100单位,可按需调整

// 锁定所有旋转自由度:上下限都设为0,不让它倾斜/滚动
dofConstraint.setAngularLowerLimit(new Ammo.btVector3(0, 0, 0));
dofConstraint.setAngularUpperLimit(new Ammo.btVector3(0, 0, 0));

// 把约束添加到物理世界
physicsWorld.addConstraint(dofConstraint, true);

方案二:物理模拟后强制同步位置与旋转

如果不想用约束,也可以在每帧物理更新后,强制把软物体的X/Z位置、旋转重置为初始状态,同时清空X/Z方向的速度,避免物理引擎继续推动它偏移。

你之前的尝试无效,大概率是只修改了ThreeJS的Mesh位置,没同步Ammo的物理刚体状态——物理引擎在下一帧会重新计算位置,所以必须同时更新Ammo的transform和速度:

function updatePhysics() {
  // 先让物理世界完成一帧模拟
  physicsWorld.stepSimulation(timeStep, maxSubSteps);

  // 获取软物体的当前物理变换
  const transform = new Ammo.btTransform();
  softBody.getWorldTransform(transform);
  const pos = transform.getOrigin();
  const rot = transform.getRotation();

  // 强制重置X、Z为初始值,保留Y轴的下落位置;旋转重置为初始状态(无倾斜)
  const initialX = 0; // 替换成你的初始X位置
  const initialZ = 0; // 替换成你的初始Z位置
  pos.setX(initialX);
  pos.setZ(initialZ);
  rot.setX(0);
  rot.setY(0);
  rot.setZ(0);
  rot.setW(1);

  // 把修改后的变换设置回软物体
  softBody.setWorldTransform(transform);

  // 清空X/Z方向的线速度和所有角速度,避免物理引擎继续施加力导致偏移
  const linearVel = softBody.getLinearVelocity();
  softBody.setLinearVelocity(new Ammo.btVector3(0, linearVel.getY(), 0));
  softBody.setAngularVelocity(new Ammo.btVector3(0, 0, 0));

  // 同步到ThreeJS的Mesh上
  mesh.position.set(pos.x(), pos.y(), pos.z());
  mesh.quaternion.set(rot.x(), rot.y(), rot.z(), rot.w());

  // 记得销毁Ammo对象,避免内存泄漏
  Ammo.destroy(transform);
  Ammo.destroy(pos);
  Ammo.destroy(rot);
  Ammo.destroy(linearVel);
}

补充说明

  • 如果你用的是btSoftBody创建的软物体,还可以尝试固定软物体的部分节点(比如顶部或底部的节点),但这种方式可能会影响软物体的形变效果,不如全局约束灵活。
  • 方案一的约束方式更推荐,因为它是从物理规则层面限制运动,模拟出来的下落和形变会更符合预期,不会有“瞬移”感。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:51:21