【CocosCreator 3.x】实现物体按指定轨迹移动

效果图

请添加图片描述

方案一:缓动(推荐)

思路

根据输入轨道点集,设置缓动实现

代码实现

/**
 * 轨道
 */
@ccclass('Orbit')
export class Orbit extends Component {
    /** 轨道点集 */
    @property(Vec3)
    points: Vec3[] = [];

    protected onLoad(): void {
        this.node.setPosition(this.points[0]);
    }

    start() {
        // 构造缓动
        let twe = tween().target(this.node);
        // 逐个点移动
        for (let i = 1; i < this.points.length; i++) {
            twe.to(1.0, { position: this.points[i] });
        }
        // 结束后回调启动缓动,一直重复
        twe.call(() => twe.start());
        // 开始播放循环
        twe.start();
    }
}

方案二:直接更新位置(原始)

思路

物体按指定轨道运动的话,轨道用多边形表示。

物体沿着多边形上的边移动,顺序地从一个点移动到下一个点。

将曲线运动简化成直线运动。

物体运动的距离用 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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值