路径跟随

一,原理

1.必备对象:路径和移动物体
2.需要考虑的情况:

  • 物体移动方向和位置必须受路径约束
  • 物体的旋转方向是否受路径约束

3.物体的旋转方向若要受路径约束,有两种实现方式:

  • 硬旋转:物体旋转角度和其所在线段的方向匹配(假设路径是由线段组成的)。

    • 物体在拐点处的旋转比较僵硬。
  • 软旋转:在路径上,获取距离物体一定距离的点,以此点为目标点,让物体朝向这个点。

    • 物体在拐点处的旋转是有过度的,转折圆滑。

二,示例:用three.js 实现软旋转的路径跟随

此示例需要three.js 基础,对于three.js 环节,我只做简单概述。

1.搭建场景

const canvas = document.querySelector('#c');
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const renderer = new THREE.WebGLRenderer({canvas});
renderer.setSize(width, height, false);
const fov = 45;
const aspect = width / height; 
const near = 0.01;
const far = 10;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 2, 0);
camera.updateProjectionMatrix();
const controls = new THREE.OrbitControls(camera, canvas);
const scene = new THREE.Scene();

2.绘制路径

let curve;
let curveObject;
{
  const points = [
      [0.5,0,0.5],
      [-0.5,0,0.5],
      [-0.5,0,-0.5],
  ];
  curve = new THREE.CatmullRomCurve3(
      points.map((p, ndx) => {
          return (new THREE.Vector3()).set(...p);
      }),
      true,
      'catmullrom',
      0.01
  );
  {
      const points = curve.getPoints(6);
      const geometry = new THREE.BufferGeometry().setFromPoints(points);
      const material = new THREE.LineBasicMaterial({color: 0xff0000});
      curveObject = new THREE.Line(geometry, material);
      scene.add(curveObject);
  };
}

  • THREE.CatmullRomCurve3 是一种数学概念上的曲线,它若想被显示出来,还要被细分成n 条线段。
  • curve.getPoints(6) 就是获取将曲线细分成六段后的定点集合。
  • BufferGeometry().setFromPoints(points) 是用顶点建模,后面的线性材质和网格就不细说了。

3.建立运动物体

let car;
{
    const geometry =new THREE.BoxBufferGeometry(.1,.1,.2);
    const material = new THREE.MeshBasicMaterial( {color: 0xcccccc} );
    car = new THREE.Mesh( geometry, material );
    scene.add(car);
}

4.路径跟随动画

//汽车位置
const carPosition = new THREE.Vector3();
//汽车目标点
const carTarget = new THREE.Vector3();
function render(time) {
    //将递增的时间转化为距离
    let distance = time*0.0002;  // convert to seconds
    {
        //目标点到目标的距离
        const targetOffset = 0.1;
        //从曲线上获取汽车点位。getPointAt 详情查手册。
        curve.getPointAt(distance % 1, carPosition);
        //从曲线上获取汽车目标点位
        curve.getPointAt((distance + targetOffset) % 1, carTarget);
        //汽车定位
        car.position.copy(carPosition);
        //实现软旋转
        car.lookAt(carTarget);
        //此方法还可以实现软位移
        //car.position.lerpVectors(carPosition, carTarget, 0.5);
    }
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
requestAnimationFrame(render);

5.完整代码

<!DOCTYPE html>
<html>
<head>
    <title>路径跟随</title>
        <style>
        html, body {
            margin: 0;
            height: 100%;
        }
        #c {
            width: 100%;
            height: 100%;
            display: block;
        }
    </style>
</head>
<body>
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/js/controls/OrbitControls.js"></script>
<script>
    const canvas = document.querySelector('#c');
    const width = canvas.clientWidth;
  const height = canvas.clientHeight;
    const renderer = new THREE.WebGLRenderer({canvas});
    renderer.setSize(width, height, false);
    const fov = 45;
    const aspect = width / height; 
    const near = 0.01;
    const far = 10;
    const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
    camera.position.set(0, 2, 0);
    camera.updateProjectionMatrix();
    const controls = new THREE.OrbitControls(camera, canvas);
    const scene = new THREE.Scene();

    let curve;
  let curveObject;
    {
      const points = [
          [0.5,0,0.5],
          [-0.5,0,0.5],
          [-0.5,0,-0.5],
      ];
      curve = new THREE.CatmullRomCurve3(
          points.map((p, ndx) => {
              return (new THREE.Vector3()).set(...p);
          }),
          true,
          'catmullrom',
          0.01
      );
      {
          const points = curve.getPoints(6);
          const geometry = new THREE.BufferGeometry().setFromPoints(points);
          const material = new THREE.LineBasicMaterial({color: 0xff0000});
          curveObject = new THREE.Line(geometry, material);
          scene.add(curveObject);
      };
  }

  let car;
  {
      const geometry =new THREE.BoxBufferGeometry(.1,.1,.2);
    const material = new THREE.MeshBasicMaterial( {color: 0xcccccc} );
    car = new THREE.Mesh( geometry, material );
    scene.add(car);
  }

  //汽车位置
  const carPosition = new THREE.Vector3();
  //汽车目标点
  const carTarget = new THREE.Vector3();
  function render(time) {
      //将递增的时间转化为距离
    let distance = time*0.0002;  // convert to seconds
    {
        //目标点到目标的距离
      const targetOffset = 0.1;
      //从曲线上获取汽车点位。
      curve.getPointAt(distance % 1, carPosition);
      //从曲线上获取汽车目标点位
      curve.getPointAt((distance + targetOffset) % 1, carTarget);
      //汽车定位
      car.position.copy(carPosition);
      //实现软旋转
      car.lookAt(carTarget);
      //此方法还可以实现软位移
      //car.position.lerpVectors(carPosition, carTarget, 0.5);
    }
    renderer.render(scene, camera);
        requestAnimationFrame(render);
  }
  requestAnimationFrame(render);

</script>
</body>
</html>

参考网址:https://threejsfundamentals.o…

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理