Skip to content

物体移动 - 前后碰撞检测

主要还是依靠 THREE.Raycaster(origin, direction) 射线检测。在物体前后移动时,实时获取物体的位置origin=target.position.clone().add(new THREE.Vector3(0, 1, 0))作为「射线发射点」,并通过this.player.getWorldDirection(direction)实时获取物体的方向作为射线「发射方向」,当物体移动的 forward>0,表示前进,否则表示后退。如果是后退,则direction.negate();方向反转。

效果图

GIF 图只有 4fps,20 质量,所以很卡。否则图片太大了

上下楼梯

js
/**
 * JoyStick控制器-角色碰撞检测【前|后】
 * @param {Object3D} target 需要碰撞检测的移动物体
 * @param {THREE.Raycaster} raycaster 移动物体的碰撞检测射线
 * @param {Number} intersectDistance 检测距离,小于此距离表示碰撞了
 * @param {Boolean} recursive 是否递归检测场景中的物体
 * @returns Boolean
 */
function checkCollide(
  target,
  raycaster,
  intersectDistance = 2,
  recursive = true
) {
  // 是否在【前进】
  let isForward = this.js.forward >= 0;

  // 获取target当前位置,Y轴加一个固定量,代表纵轴射线发射(检测碰撞的)位置
  let origin = target.position.clone().add(new THREE.Vector3(0, 1, 0));

  // 获取target当前朝向
  let direction = new THREE.Vector3(); //定义一个方向向量
  this.player.getWorldDirection(direction);
  direction.normalize();

  this.playerLight.position.x = this.player.position.x;
  this.playerLight.position.y = this.player.position.y + 2;
  this.playerLight.position.z = this.player.position.z;

  // 如果在【后退】,检测方向direction取反
  if (!isForward) {
    direction.negate();
    // console.log("move fallback:", direction);
  } else {
    // console.log("move forward:", direction);
  }

  // 设置射线发射位置
  raycaster.ray.origin.copy(origin);

  // 设置射线发射方向
  raycaster.ray.direction.copy(direction);

  // 开始【前、后】检测:对于blender制作的模型,需要递归遍历所有child,否则无法实现射线碰撞检测{[childs], true}
  let ins = raycaster.intersectObjects(this.objects, recursive);
  if (ins.length) {
    let { distance, object } = ins[0];
    if (object.name == "stair") {
      return false;
    }
    if (distance < intersectDistance) {
      console.warn("💥碰撞了:", object);

      // 自动回退1米,给接下来小角度前进留余地,否则会持续触发碰撞条件而无法移动
      if (isForward) {
        console.log("💥前进过程碰撞了,自动后退1米");
        this.player.translateZ(-1);
      } else {
        console.log("💥后退过程碰撞了,自动前进1米");
        this.player.translateZ(+1);
      }
      return true;
    }
  }
  return false;
}

一些优化

  • 发生碰撞时,根据此前移动方向,向相反的方向回退 1m(this.player.translateZ(±1);),这样可以防止转弯时持续碰撞物体。

Released under the MIT License.