如何在Three.js中让BoxGeometry墙体实现碰撞阻挡物体穿透
Three.js 3D迷宫碰撞检测解决方案
问题概述
使用Three.js构建3D迷宫,通过BoxGeometry创建墙体,需要实现控制的car物体无法穿越墙体的碰撞效果,尝试过RayCaster和Cannon.js但未成功。
方案一:基于RayCaster的射线碰撞检测
你之前的问题在于直接修改car的位置,没有在移动前进行碰撞检测。以下是修正后的实现:
步骤1:准备墙体集合
先将所有墙体存入数组,方便统一检测:
const walls = [box1, box2, box3, box4]; const rayCaster = new THREE.Raycaster(); // 假设car的尺寸是x:2, z:4(根据你的car模型实际尺寸调整) const carHalfSize = new THREE.Vector3(1, 0, 2);
步骤2:修改键盘事件,加入碰撞检测
替换原有的document.onkeydown事件,在移动前检测是否会碰撞墙体:
document.onkeydown = function (e) { let moveDir = new THREE.Vector3(); // 确定移动方向,修正旋转角度为标准弧度 switch(e.keyCode) { case 87: // W moveDir.set(0, 0, 1); car.rotation.y = 0; break; case 83: // S moveDir.set(0, 0, -1); car.rotation.y = Math.PI; break; case 65: // A moveDir.set(1, 0, 0); car.rotation.y = -Math.PI/2; break; case 68: // D moveDir.set(-1, 0, 0); car.rotation.y = Math.PI/2; break; default: return; } // 计算car的碰撞检测起点(从car边缘向外偏移,避免误判) const startPos = car.position.clone().add(moveDir.multiply(carHalfSize)); // 设置射线:起点 + 移动方向,长度为移动步长+余量 rayCaster.set(startPos, moveDir.normalize()); // 检测与墙体的碰撞 const intersects = rayCaster.intersectObjects(walls); // 无碰撞或碰撞距离大于步长时,执行移动 if (intersects.length === 0 || intersects[0].distance > 1.1) { car.position.add(moveDir.multiplyScalar(1)); } };
注意:必须根据car模型的实际尺寸调整carHalfSize,确保射线从car的边缘发出,避免car还未接触墙体就被阻挡。
方案二:基于Cannon.js的物理引擎碰撞(更稳定)
物理引擎能精准处理复杂碰撞,你之前的失败大概率是没正确同步Three.js与Cannon的物体状态。以下是完整实现:
步骤1:初始化Cannon物理世界
// 引入Cannon.js后创建物理世界 const world = new CANNON.World(); world.gravity.set(0, -9.8, 0); // 重力向下 world.broadphase = new CANNON.SAPBroadphase(world); // 优化碰撞检测性能 world.defaultContactMaterial.restitution = 0; // 关闭弹性,避免碰撞后反弹
步骤2:为墙体、地面和car创建物理体
// 辅助函数:同步Three.js网格到Cannon刚体 function createPhysicsBody(mesh, type = CANNON.Body.STATIC) { const geoParams = mesh.geometry.parameters; const shape = new CANNON.Box(new CANNON.Vec3( geoParams.width/2 || geoParams.x/2, geoParams.height/2 || geoParams.y/2, geoParams.depth/2 || geoParams.z/2 )); const body = new CANNON.Body({ mass: type === CANNON.Body.DYNAMIC ? 1 : 0, // 静态物体质量设为0 shape: shape, type: type }); body.position.copy(mesh.position); body.quaternion.copy(mesh.quaternion); world.addBody(body); return body; } // 为墙体创建静态物理体 const wallBodies = walls.map(wall => createPhysicsBody(wall)); // 为地面创建静态物理体 const planeBody = createPhysicsBody(plane); // 为car创建动态物理体 const carBody = createPhysicsBody(car, CANNON.Body.DYNAMIC); carBody.fixedRotation = true; // 禁止car翻滚 carBody.linearDamping = 0.5; // 增加阻力,避免car滑动不止
步骤3:动画循环中同步物理体与Three.js物体
在你的渲染循环中加入物理世界更新与状态同步:
function animate() { requestAnimationFrame(animate); // 更新物理世界,固定步长1/60 world.step(1/60); // 同步car的位置与旋转 car.position.copy(carBody.position); car.quaternion.copy(carBody.quaternion); // 执行渲染 renderer.render(scene, camera); } animate();
步骤4:修改键盘控制,通过物理力移动car
替换原有的键盘事件,改为给物理体施加力:
const moveForce = 50; // 移动力度,根据需求调整 document.onkeydown = function (e) { switch(e.keyCode) { case 87: // W carBody.applyLocalForce(new CANNON.Vec3(0, 0, moveForce), new CANNON.Vec3(0,0,0)); car.rotation.y = 0; break; case 83: // S carBody.applyLocalForce(new CANNON.Vec3(0, 0, -moveForce), new CANNON.Vec3(0,0,0)); car.rotation.y = Math.PI; break; case 65: // A carBody.applyLocalForce(new CANNON.Vec3(moveForce, 0, 0), new CANNON.Vec3(0,0,0)); car.rotation.y = -Math.PI/2; break; case 68: // D carBody.applyLocalForce(new CANNON.Vec3(-moveForce, 0, 0), new CANNON.Vec3(0,0,0)); car.rotation.y = Math.PI/2; break; } };
内容的提问来源于stack exchange,提问作者You dunno
相关产品推荐
相关产品推荐

