threejs 加载碰撞环境后,之前点击事件不起作用

加载碰撞器环境loadCollider后,之前正常工作的opendoor点击不起作用了。而且通过日志发现这个门点击区域有了偏转

img

img

    loadCollider(model) { 
      const that = this
      const gltfScene = model // 模型
      this.box3d = new THREE.Box3() // 表示三维空间中的一个轴对齐包围盒
      that.box3d.setFromObject(model) // 计算和世界轴对齐的一个对象 Object3D(含其子对象)的包围盒
      gltfScene.updateMatrixWorld(true) //更新物体及其后代的全局变换。

      const toMerge = {} // 合并组合
      gltfScene.traverse(c => {
        if (c.isMesh && c.material.color !== undefined) {
          // 根据颜色将所有模型分类
          const MeshBVH = c.material.color.getHex()
          toMerge[MeshBVH] = toMerge[MeshBVH] || []
          toMerge[MeshBVH].push(c)
        }
      })

      // 环境 :创建一个组合
      that.environment = new THREE.Group()
      for (const hex in toMerge) {
        const arr = toMerge[hex] // 获得同一个颜色的模型数组
        arr.forEach(mesh => {
          that.environment.attach(mesh) // 将mesh作为子级来添加到该对象中,同时保持该object的世界变换。
        })
      }

      that.geometries = [] //视觉几何数组
      that.environment.updateMatrixWorld(true) //更新模型世界坐标
      that.environment.traverse(c => {
        if (c.geometry) {

          // if (c.name === 'Plane056') {
          //   that.id = c.id
          //   console.log(c.id, 'cid')
          // }
          const cloned = c.geometry.clone()  //克隆几何结构
          cloned.applyMatrix4(c.matrixWorld) // 对当前物体应用这个变换矩阵,并更新物体的位置、旋转和缩放。
          // 移除位置外的其他属性
          for (const key in cloned.attributes) {
            if (key !== 'position') {
              cloned.deleteAttribute(key)
            }
          }

          that.geometries.push(cloned) //添加到视觉几何数组
        }
      })

      that.mergedGeometry = mergeBufferGeometries(that.geometries, false)
      that.mergedGeometry.boundsTree = new MeshBVH(that.mergedGeometry, {
        lazyGeneration: false
      }) //生成 BVH 并使用新生成的索引
      that.collider = new THREE.Mesh(that.mergedGeometry)// 生成网格
      that.visualizer = new MeshBVHVisualizer(that.collider, that.params.visualizeDepth) // 网格bvh可视化工具()
      // that.visualizer.layers.set(that.currentlayers)
      that.scene.add(that.visualizer) //可视化工具
      that.scene.add(that.environment) // 环境
      that.scene.add(model)
    },

getIntersects(event, group) {
      event.preventDefault()// 阻止默认的点击事件执行,   
      let rayCaster = new THREE.Raycaster()
      let mouse = new THREE.Vector2()
      //通过鼠标点击位置,计算出raycaster所需点的位置,以屏幕为中心点,范围-1到1;event.clientX鼠标点击位置
      mouse.x = (event.clientX / window.innerWidth) * 2 - 1
      mouse.y = -(event.clientY / window.innerHeight) * 2 + 1 //  这里为什么是-号,没有就无法点中

      //通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
      rayCaster.setFromCamera(mouse, this.camera)
      let intersects = rayCaster.intersectObjects([group], true)
      return intersects
    },
    // 打开门
    opendoor(event) {
      // let arr = null
      for (const k in this.doors) {
        if (this.doors[k].group && this.camera) {
          const result = this.getIntersects(event, this.doors[k].group)
          if (result.length > 0) {
            this.open_door(k)
          }
        }
      }
    },
    open_door(index) {
      console.log("index:" + index);
      const { isclose, axis, group, rotation, rotate_deg } = this.doors[index]
      group.rotation[axis] = isclose ? rotation + rotate_deg : rotation
      this.doors[index].isclose = !isclose
    }

检查下控制台日志,看看是否有与碰撞器环境或点击门相关的错误消息。如果有,请找到错误消息并确定是哪个脚本或代码导致的。或者重新加载场景模型看看。

出现您所描述的问题可能是由于加载碰撞器环境后,场景中的物体在位置或者旋转上发生了变化,导致之前的点击事件不再适用。这种情况下,您可以考虑在场景更新后重新设置之前的点击事件,或者在加载碰撞器环境前先记录场景中各个物体的位置和姿态信息,之后再使用这些信息更新点击事件。

关于点击区域偏转的问题,可能是由于加载的碰撞器环境和场景中的物体坐标系不同,导致物体的位置和姿态信息计算错误。您可以尝试在加载碰撞器环境时根据坐标系的差异进行相应的转换,来保证计算的正确性。同时,也建议您检查一下加载的碰撞器环境是否正确,是否与场景中的物体对应。

以下答案由GPT-4.5Plus大模型(语音)与博主A努力中.......共同编写:

这是因为碰撞检测会占用鼠标/触摸事件。当启用碰撞检测后,三维场景不再响应鼠标点击等事件。

有几种解决方法:

  1. 在发生点击时暂时禁用碰撞检测。例如:
js
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

function onMouseClick() {
  // 临时禁用碰撞检测
  scene.children.forEach(obj => { obj.children[0].visible = false; });
  
  // 执行点击事件逻辑
  // ...
  
  // 恢复碰撞对象可见性
  scene.children.forEach(obj => { obj.children[0].visible = true; });
}
  1. 使用PointerLockControls 的函数与鼠标事件结合。例如:
js
const controls = new PointerLockControls(camera);
controls.addEventListener( 'click', () => { 
  // 单击时执行点击逻辑
} );
PointerLockControls 可以同时支持碰撞检测和鼠标事件。

  1. 使用外部绑定的射线(通过鼠标位置创建)来检测对象的点击,而不是使用THREE.Raycaster。例如:
js
const raycaster = new THREE.Raycaster(); 
const mouse = new THREE.Vector2();

function getIntersects(point, scene) {
  raycaster.setFromCamera(point, camera);
  return raycaster.intersectObjects(scene.children); 
}

function onMouseClick() {
  mouse.x = event.clientX / window.innerWidth * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  
  const intersects = getIntersects(mouse, scene);
  
  if (intersects.length > 0) {
     const object = intersects[0].object;
     // 执行点击对象的逻辑
  }
}

这种方法可以同时支持碰撞检测和点击事件。


可能是由于加载Collider后对门(opendoor)的物理属性或Collider组件造成了影响导致的。您可以尝试检查门的物理属性和Collider组件是否已正确设置,并确保它们与其他组件和GameObject的参数相匹配。

此外,您还可以考虑在运行时使用调试工具检查门的Collider组件是否与它本来应该匹配的位置、大小和旋转相符合。您可以通过手动调整Collider组件的参数来解决偏移问题。另外,您还可以检查门的触发类型(例如,它是否应该是一个触发器或一个坚实的物体)以及它是否有正确的物理材质。

最后,如果以上方法都无法解决您的问题,您可以考虑重新创建门对象或尝试使用更稳定的Collider组件(例如MeshCollider或BoxCollider)来代替原来的Collider。