Skip to content

上下楼梯实现

主要还是依靠 THREE.Raycaster(origin, direction) 射线检测。从移动物体的顶部的地方发出射线,方向朝下,检测楼梯,进行距离差值判断。需要注意的点在于要规避人「走在楼梯下」的场景,不要误判为「上楼」。

效果图

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

上下楼梯

上下楼梯射线碰撞检测代码

js
/**
 * target 检测的移动目标
 */
function checkStairCollide(target) {
  // 获取target当前位置,Y轴加一个固定量(50米),代表纵轴射线发射(检测碰撞的)位置
  let origin = target.position.clone().add(new THREE.Vector3(0, 50, 0));

  // 获取target当前朝向
  let direction = new THREE.Vector3(0, -1, 0); //定义一个向下的方向向量

  let raycaster = new THREE.Raycaster(origin, direction);

  let ins = raycaster.intersectObjects(this.objects, true);

  if (ins.length) {
    let { distance, object } = ins[0];

    const name = object.name;
    const oneStairHeight = 0.4; //一个楼梯高度
    const fallenSpeed = 8; //脱离楼梯或高台后的坠落速度
    let diffY = origin.y - distance;
    let targetY = target.position.y;

    // 脱离楼梯或高台,开始坠落
    if (name.includes("floor") || name.includes("ground")) {
      // 坠落动画
      let fallDistance = target.position.y;
      gsap.to(target.position, {
        y: 0,
        duration: fallDistance / fallenSpeed,
      });
      return;
    }

    // 遇到楼梯,上楼检测
    if (name.includes("stair")) {
      let diffHeigh = diffY - targetY;
      // 超过2个楼梯高度不进行上楼检测,说明在楼梯下走路
      if (diffHeigh >= oneStairHeight * 2) {
        return;
      }

      // 射线命中的位置比target的当前Y轴位置高,可以上楼
      if (diffY > targetY) {
        // target.position.y = diffY;

        // 动画过渡一下
        gsap.to(target.position, {
          y: diffY,
          ease: "linear",
          duration: 0.1, //这个延迟如果太久,会导致target.position.y更新不及时而无法判断为上楼梯
        });
        console.log("上楼梯⬆️");
      } else if (diffY < targetY) {
        target.position.y -= 0.1;
        console.log("下楼梯⬇️");
      }
    }
  }
}

一些优化

  • 检测在楼梯下运动:
js
const oneStairHeight = 0.4; //一个楼梯高度

if (name.includes("stair")) {
  let diffHeigh = diffY - targetY;
  // 超过2个楼梯高度不进行上楼检测,说明在楼梯下走路
  if (diffHeigh >= oneStairHeight * 2) {
    return;
  }

  //   ...上下楼梯
}
  • 加入gsap缓动动画,让镜头平滑些。

Released under the MIT License.