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

Three.js中GLTF场景碰撞检测实现:阻止玩家穿墙

实现GLTF关卡的碰撞检测(阻止玩家穿墙)

我在游戏中导入了一个GLTF场景/关卡(Backrooms Level 0),希望实现碰撞检测以阻止玩家穿墙。我的加载器代码如下:

loader.load(
// resource URL
"../../models/scene.gltf",
// called when the resource is loaded
function (gltf) {
  scene.add(gltf.scene);
  level = gltf.scene;
  gltf.animations; // Array<THREE.AnimationClip>
  gltf.scene; // THREE.Group
  gltf.scenes; // Array<THREE.Group>
  gltf.cameras; // Array<THREE.Camera>
  gltf.asset; // Object
},
// called while loading is progressing
function (xhr) {
  console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
},
// called when loading has errors
function (error) {
  console.error(error);
}
return { level };

方法1:为关卡生成简易碰撞体(性能优先)

由于原GLTF模型面数通常较高,直接用原网格做碰撞会导致性能问题,建议为每个网格生成凸面体碰撞体:

function (gltf) {
  scene.add(gltf.scene);
  level = gltf.scene;

  // 遍历关卡网格,生成碰撞体
  level.traverse((child) => {
    if (child.isMesh) {
      // 基于原网格顶点生成凸面体碰撞几何体
      const convexGeometry = new THREE.ConvexGeometry(child.geometry.attributes.position.array);
      // 创建不可见的碰撞网格
      const collider = new THREE.Mesh(convexGeometry);
      // 同步原网格的位置、旋转、缩放
      collider.position.copy(child.position);
      collider.rotation.copy(child.rotation);
      collider.scale.copy(child.scale);
      // 标记为碰撞体,方便后续检测
      collider.userData.isCollider = true;
      scene.add(collider);
    }
  });

  gltf.animations; // Array<THREE.AnimationClip>
  gltf.scene; // THREE.Group
  gltf.scenes; // Array<THREE.Group>
  gltf.cameras; // Array<THREE.Camera>
  gltf.asset; // Object
},

方法2:用Raycaster做射线碰撞检测(适合第一/第三人称玩家)

在玩家移动逻辑中,通过发射射线预判是否会穿墙:

// 假设player为玩家对象,moveDirection为移动方向向量,moveStep为单次移动距离
const raycaster = new THREE.Raycaster();
raycaster.set(player.position, moveDirection.normalize());

// 筛选所有标记为碰撞体的对象
const colliders = scene.children.filter(child => child.userData.isCollider);
const intersects = raycaster.intersectObjects(colliders);

// 若射线检测到碰撞且距离小于移动步长,阻止移动
if (intersects.length > 0 && intersects[0].distance < moveStep) {
  return;
} else {
  player.position.add(moveDirection.multiplyScalar(moveStep));
}

方法3:使用物理引擎实现真实碰撞(如Cannon.js)

如果需要跳跃、碰撞反弹等复杂物理效果,推荐使用专业物理引擎:

  1. 引入Cannon.js库
  2. 加载关卡时创建对应物理体:
// 初始化Cannon物理世界
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);

// 加载完成回调中添加物理体
function (gltf) {
  scene.add(gltf.scene);
  level = gltf.scene;

  level.traverse((child) => {
    if (child.isMesh) {
      // 基于原网格创建Cannon碰撞形状
      const shape = new CANNON.ConvexPolyhedron(child.geometry.attributes.position.array);
      // 创建静态物理体(关卡不移动,质量设为0)
      const body = new CANNON.Body({
        mass: 0,
        shape: shape,
        position: new CANNON.Vec3(child.position.x, child.position.y, child.position.z),
        quaternion: new CANNON.Quaternion(child.rotation.x, child.rotation.y, child.rotation.z, child.rotation.w)
      });
      world.addBody(body);
      child.userData.physicsBody = body;
    }
  });
},

// 每一帧同步物理世界与Three.js场景
function animate() {
  requestAnimationFrame(animate);
  // 更新物理模拟
  world.step(1/60);
  // 同步玩家对象与物理体的位置、旋转
  player.position.copy(player.userData.physicsBody.position);
  player.quaternion.copy(player.userData.physicsBody.quaternion);
  renderer.render(scene, camera);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 02:22:42