如何让Three.js中两个盒子碰撞时互不穿透(类似墙体阻挡)
解决Three.js中盒子碰撞时的穿透问题
核心思路
替代禁用TransformControls的方案,通过碰撞后的位置修正实现防穿透:在检测到两盒相交时,计算出刚好不相交的位置,将被拖动的盒子重置到该位置,同时保留控件的交互能力。
具体实现步骤
- 利用
THREE.Box3计算两个盒子的边界及交集 - 根据拖动方向,将被碰撞的盒子沿相反方向移动,消除交集
- 监听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
相关产品推荐
相关产品推荐

