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

如何让Three.js中两个盒子碰撞时互不穿透(类似墙体阻挡)

解决Three.js中盒子碰撞时的穿透问题

核心思路

替代禁用TransformControls的方案,通过碰撞后的位置修正实现防穿透:在检测到两盒相交时,计算出刚好不相交的位置,将被拖动的盒子重置到该位置,同时保留控件的交互能力。

具体实现步骤

  1. 利用THREE.Box3计算两个盒子的边界及交集
  2. 根据拖动方向,将被碰撞的盒子沿相反方向移动,消除交集
  3. 监听TransformControls的objectChange事件,在每次位置变更后执行碰撞检测与修正(比在animate循环中检测更高效)

修改后的代码示例

1. 重构碰撞检测与修正函数

function handleCollision(boxA, boxB, transformAnchorA) {
    const boundsA = new THREE.Box3().setFromObject(boxA);
    const boundsB = new THREE.Box3().setFromObject(boxB);
    
    if (!boundsA.intersectsBox(boundsB)) return;

    // 计算交集区域
    const intersection = new THREE.Box3();
    intersection.intersectBox(boundsA, boundsB);
    
    // 获取交集的尺寸与方向
    const intersectionSize = new THREE.Vector3();
    intersection.getSize(intersectionSize);
    
    // 计算需要移动的距离,将boxA推到刚好不相交的位置
    const moveDir = new THREE.Vector3();
    boxA.position.sub(boxB.position).normalize();
    
    // 根据交集最大的轴方向移动(优先解决最明显的穿透)
    let moveAxis = 'x';
    if (intersectionSize.y > intersectionSize.x && intersectionSize.y > intersectionSize.z) {
        moveAxis = 'y';
    } else if (intersectionSize.z > intersectionSize.x) {
        moveAxis = 'z';
    }
    
    const moveDistance = intersectionSize[moveAxis] / 2;
    transformAnchorA.position[moveAxis] += moveDir[moveAxis] * moveDistance;
}

2. 监听TransformControls的位置变更事件

替换原animate函数中的碰撞检测逻辑,改为在控件事件中触发:

// 为woodBox的控件添加事件监听
transformControls.addEventListener('objectChange', () => {
    handleCollision(woodBox, metalBox, transformAnchor);
});

// 为metalBox的控件添加事件监听
transformControlsMetal.addEventListener('objectChange', () => {
    handleCollision(metalBox, woodBox, transformAnchorMetal);
});

// 原animate函数保留渲染逻辑即可
function animateTheeBox() {
    transformControls.setSize(70 / transformControls.worldPosition.distanceTo(camera.position) * Math.min(1.9 * Math.tan(Math.PI * camera.fov / 360) / camera.zoom, 7));
    transformControlsMetal.setSize(70 / transformControlsMetal.worldPosition.distanceTo(camera.position) * Math.min(1.9 * Math.tan(Math.PI * camera.fov / 360) / camera.zoom, 7));

    renderer.setClearColor(0xffffff);
    renderer.render(scene, camera);
    requestAnimationFrame(animateTheeBox);
}

// 启动动画循环
animateTheeBox();

3. 移除原animate函数中的碰撞禁用逻辑

删除原animate函数中关于transformControls.enabled的修改代码,保留控件始终可用。

关键说明

  • 该方案通过位置修正替代禁用控件,既保留了用户的移动能力,又避免了穿透
  • 优先处理交集最大的轴方向,确保修正后的位置更自然
  • 使用objectChange事件触发检测,减少不必要的计算,提升性能

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 18:55:00