4

开讲之前,先上个案例
演示地址: http://www.yxyy.name/example/...
下载地址:https://github.com/buglas/exa... > supPath.html

一,原理

1.路径跟随必备对象:移动物体和路径

  • 如飞机和飞机移动路线

clipboard.png

2.需要考虑的情况:

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

旋转方向不受路径约束

图片描述

旋转方向受路径约束

图片描述

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

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

    • 物体在拐点处的旋转比较僵硬。如上图。
  • 软旋转:物体在路径的转折处,圆滑过度。

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

图片描述

4.实现软旋转的思路有两种:

  • 预先圆滑拐点:在动画之前,就对路径做圆滑处理。就像上图那样。
  • 实时圆滑拐点:物体在移动过程中,对自己的下一个位置进行圆滑处理。见下图。

5.预先圆滑中,物体跟随的路径是已经被修改过的,所以它的移动轨迹也会和最初的路径不吻合。

6.实时圆滑可以将物体的旋转属性和位置属性分离的,如图

  • 只圆滑旋转角度,物体跟随原始路径移动

clipboard.png

  • 圆滑旋转角度和移动位置。物体的位置会根据两点的位置做插值计算。后面案例会详解。

clipboard.png

二,示例:用实时圆滑的方式实现路径跟随

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

1.搭建场景:

//获取html 里的canvas
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 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) 是用顶点建模,后面的线性材质和网格就不细说了。

clipboard.png

3.建立运动物体。
先用一个简单的box 表示汽车。飞机的话,还得导入模型进来。先讲重点。

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);
}

clipboard.png

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);

注:{} 包裹代码的写法是跟let const 命令的块级作用域有关,如此可以避免变量污染上层对象的命名空间。

实例效果演示: http://www.yxyy.name/funk/路...

实例源文件:https://github.com/buglas/fun... > 路径跟随.html

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


已注销
148 声望9 粉丝

« 上一篇
路由的原理

引用和评论

0 条评论