项目框架:vue+typescript

最终效果视频
线上地址

相机跟随曲线运动

首先要实现人物的曲线运动,坐标点是设计师直接放在模型上的,导入模型后,用模型上的点生成曲线,再用曲线的getPoint方法获取点坐标。

获取模型上的点并转为世界坐标

1
2
3
4
5
6
7
8
9
const points = []

for (let i = 0; i < walkPoints.children.length; i++) {
if (walkPoints.children[i].type == 'Mesh') {
let worldPosition = new THREE.Vector3(0, 0, 0)
walkPoints.children[i].getWorldPosition(worldPosition) // 转为世界坐标
points.push(worldPosition.clone()) // 存入数组
}
}

生成曲线获取新的坐标组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const ARC_SEGMENTS = 50 // 希望得到的点数量

getPoints(points: any) {
let geometry = new THREE.BufferGeometry()
let curve: any = new THREE.CatmullRomCurve3(points) // 生成曲线

let walkPoints = [] // 最终生成的点数组
let point = new THREE.Vector3()

for (let i = 0; i < ARC_SEGMENTS; i++) {
let t = i / (ARC_SEGMENTS - 1)
curve.getPoint(t, point)
walkPoints.push(point.clone())
}

return walkPoints
}

相机跟随

将人物和相机放到一个父对象下,用父对象去做运动

1
2
3
4
5
6
private myAnimationBox: any =  new THREE.Object3D() // 父元素
...
this.myAnimationBox.add(obj) // 添加人

this.myAnimationBox.add(this.camera) // 添加相机
...

用tweenjs动画库实现运动

这里顺便提一下在vue+typescript里使用tween.js的问题,直接引用threejs里面的tween会报错,所以要npm i @tweenjs/tween.js,再引入:

1
2
import * as T from '@tweenjs/tween.js'
const TWEEN:any = T.default

跟随运动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...

// walkPoints即上面使用``getPoint``方法获得的点坐标
// animationIndex是当前的下标
_this.myAnimation = new TWEEN.Tween(_this.myAnimationBox.position)
.to({
x: _this.walkPoints[_this.animationIndex].x / 1,
y: _this.walkPoints[_this.animationIndex].y / 1,
z: _this.walkPoints[_this.animationIndex].z / 1
}, tweenTime)
.onUpdate(() => {
var a = _this.myAnimationBox.position.clone()
a.y += 20 // 这里在y轴加20是因为视角要往上一些
_this.control.target.copy(a)

// 保证相机朝向正确
_this.myAnimationBox.matrix.lookAt(_this.myAnimationBox.position, new THREE.Vector3(0, _this.myAnimationBox.position.y, 0), new THREE.Vector3(0, 1, 0))
_this.myAnimationBox.quaternion.setFromRotationMatrix(_this.myAnimationBox.matrix)
})
.onComplete(() => {
_this.animationIndex++
animation(_this.animationIndex)
})
.start()
...

无限循环

这边要实现一个人物无限向上运动的功能,当然模型不可能做成无限循环的,这边的处理非常简单,就是不停改变模型的位置,使用三组模型,每次把最下面一层移到最上面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 每次改变坐标点的Y值,因为X和Z是不变的
for (let j = 0; j < _this.walkPoints.length; j++) {
_this.walkPoints[j].y += ADD_HEIGHT
}

let changeIndex = 0

// roadIndex是一个不断往上增长的数字变量,代表当前所在的层数
// 由此算出现在要移动第几个模型
if (_this.roadIndex % 3 == 0) {
changeIndex = 2
} else if ((_this.roadIndex + 1) % 3 == 0) {
changeIndex = 1
} else {
changeIndex = 0
}

// ADD_HEIGHT是每层的高度,每次将最下面一层移到最上面
_this.groupRoad.children[changeIndex].position.y += ADD_HEIGHT * 3
_this.groupModel.children[changeIndex].position.y += ADD_HEIGHT * 3
_this.groupModelShadow.children[changeIndex].position.y += ADD_HEIGHT * 3

参考文档

https://threejs.org/examples/#webgl_geometry_extrude_splines