开讲之前,先上个案例
演示地址: http://www.yxyy.name/example/...
下载地址:https://github.com/buglas/exa... > supPath.html
一,原理
1.路径跟随必备对象:移动物体和路径
- 如飞机和飞机移动路线
2.需要考虑的情况:
- 物体移动方向和位置必须受路径约束
- 物体的旋转方向是否受路径约束
旋转方向不受路径约束
旋转方向受路径约束
3.物体的旋转方向若要受路径约束,有两种实现方式:
-
硬旋转:物体旋转角度和其所在线段的方向匹配(假设路径是由线段组成的)。
- 物体在拐点处的旋转比较僵硬。如上图。
-
软旋转:物体在路径的转折处,圆滑过度。
- 物体在拐点处的旋转是有过度的,转折圆滑。如下图:
4.实现软旋转的思路有两种:
- 预先圆滑拐点:在动画之前,就对路径做圆滑处理。就像上图那样。
- 实时圆滑拐点:物体在移动过程中,对自己的下一个位置进行圆滑处理。见下图。
5.预先圆滑中,物体跟随的路径是已经被修改过的,所以它的移动轨迹也会和最初的路径不吻合。
6.实时圆滑可以将物体的旋转属性和位置属性分离的,如图
- 只圆滑旋转角度,物体跟随原始路径移动
- 圆滑旋转角度和移动位置。物体的位置会根据两点的位置做插值计算。后面案例会详解。
二,示例:用实时圆滑的方式实现路径跟随
此示例需要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) 是用顶点建模,后面的线性材质和网格就不细说了。
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);
}
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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。