【CocosCreator 3.x】实现物体按指定轨迹移动
效果图
思路
物体按指定轨道运动的话,轨道用多边形表示。
物体沿着多边形上的边移动,顺序地从一个点移动到下一个点。
将曲线运动简化成直线运动。
物体运动的距离用 t t t表示,线段用向量表示。比如点 a ⃗ ( 0 , 0 ) \vec{a}(0, 0) a(0,0)和点 b ⃗ ( 2 , − 1 ) \vec{b}(2, -1) b(2,−1)连成的线段用向量 b ⃗ − a ⃗ = ( 2 , − 1 ) \vec{b} - \vec{a} = (2, -1) b−a=(2,−1)表示。
算出多边形每一线段的长度,依次遍历并累减,得出 t t t当前在哪一条线段上,并得出 t t t在累减遍历过的线段长度后,剩余的长度 s s s。
将长度
s
s
s除以当前线段长度得出线段向量的缩放比例。乘以线段向量
b
⃗
−
a
⃗
\vec{b} - \vec{a}
b−a,得出当前点相对该线段起始点
(
2
,
−
1
)
(2, -1)
(2,−1)的偏移量。将该偏移量加上线段起始点的向量
(
2
,
−
1
)
(2, -1)
(2,−1)得出当前点的坐标向量。
代码实现
多边形的代码实现:
import { assert, Vec3 } from "cc";
/**
* 多边形
*/
export class Polygon {
/** 点集 */
points: Vec3[] = [];
/** 线段长度集 */
private lengths: number[] = [];
/** 总长度 */
private totalLength: number = 0;
/** 线段向量 */
private lines: Vec3[] = [];
constructor(points: Vec3[]) {
this.points = points;
this.init();
}
/**
* 初始化线段信息
*/
init() {
assert(!!this.points && this.points.length > 1, "The number of points must greater than 1.");
/** 遍历线段 */
for (let i = 0; i < this.points.length - 1; i++) {
// 每段线段的长度
let lindLength = Vec3.distance(this.points[i], this.points[i + 1]);
this.lengths.push(lindLength);
// 总长度累加
this.totalLength += lindLength;
// 得出两点的线段向量
this.lines.push(Vec3.subtract(new Vec3(), this.points[i + 1], this.points[i]));
}
}
/**
* 根据距离参数获取多边形边上的点位置
* @param curLength 当前长度
* @returns 当前点所在多边形边上的位置
*/
getPoint(curLength: number): [number, Vec3] {
// 对当前长度取余,以免溢出
curLength %= this.totalLength;
// 保存取余后的长度
let newLength = curLength;
// 遍历线段
for (let i = 0; i < this.lengths.length; i++) {
if (curLength < this.lengths[i]) {
// 当前长度与线段长度的比例
let ratio = curLength / this.lengths[i];
// 当前线段向量
let line = this.lines[i];
// 在当前线段上的长度/总线段长度乘以线段向量,得出当前坐标
return [newLength, Vec3.add(new Vec3(), this.points[i], Vec3.multiplyScalar(new Vec3(), line, ratio))];
} else {
// 当前长度减去线段长度
curLength -= this.lengths[i];
}
}
throw new Error("Cannot find the line point");
}
}
轨道的代码:
import { _decorator, Component, Vec3 } from 'cc';
import { Polygon } from './framework/Polygon';
const { ccclass, property } = _decorator;
/**
* 轨道
*/
@ccclass('Orbit')
export class Orbit extends Component {
/** 轨道点集 */
@property(Vec3)
points: Vec3[] = [];
/** 运动速度 */
@property
speed: number = 10;
/** 多边形轨道 */
private polygon: Polygon = null;
/** 当前遍历长度 */
private curLength: number = 0;
protected onLoad(): void {
// 初始化多边形
this.polygon = new Polygon(this.points);
}
start() {
// 初始化位置
this.refreshPosition(0);
}
update(deltaTime: number) {
// 更新位置
this.refreshPosition(deltaTime);
}
/**
* 刷新位置
* @param deltaTime 帧间隔时间
*/
refreshPosition(deltaTime: number) {
// 当前长度累加帧间间隔时间乘以速度
this.curLength += deltaTime * this.speed;
let curPosition = null;
// 获取当前位置
[this.curLength, curPosition] = this.polygon.getPoint(this.curLength);
// 设置当前位置
this.node.setPosition(curPosition);
}
}