各位大佬...3.0的Tween还能写个抛物线吗...

版本3.3.4

大伙说的 2.x 的 cc.tween.bezierTo 没了…貌似只能用 3.x 的 Tween.to/by…
props 里也不给单独写 progress 了…opts 里的 progress 又限制了…只能写 number 的…

看了一下源码…
props 里还是可以单独写 value 和 progress 的…只是 ts 语法没兼容而已…
但是 progress 的调用时机又改了…遇到 object 类型的 props…直接把里面遍历一次刷 progress…
这导致 Vec3 直接分解成 xyz 3个 progress 了…完全没法区分轴向…

这下倒好了…单独写一个抛物线缓动没问题…可是镶不到 Tween 里啊…
想统一动画调用还是用 Tween 舒服啊…总得给个扩展时机啊…

1赞

tween的onupdate里手动更新位置吧,同时你还需要一个外部变量来保存上一次的ratio用来计算相对值变化,确实是很别扭的写法 :sweat:

论坛看见的。收藏了。
/**
* 二阶贝塞尔曲线 运动
* @param target
* @param {number} duration
* @param {} c1 起点坐标
* @param {} c2 控制点
* @param {Vec3} to 终点坐标
* @param opts
* @returns {any}
/
public static bezierTo(target: any, duration: number, c1: Vec2, c2: Vec2, to: Vec2, opts: any) {
opts = opts || Object.create(null);
/
*
* @desc 二阶贝塞尔
* @param {number} t 当前百分比
* @param {} p1 起点坐标
* @param {} cp 控制点
* @param {} p2 终点坐标
* @returns {any}
*/
let twoBezier = (t:number, p1: Vec2, cp: Vec2, p2: Vec2) => {
let x = (1 - t) * (1 - t) * p1.x + 2 * t * (1 - t) * cp.x + t * t * p2.x;
let y = (1 - t) * (1 - t) * p1.y + 2 * t * (1 - t) * cp.y + t * t * p2.y;
return new Vec3(x, y, 0);
};
opts.onUpdate = (arg: Vec3, ratio: number) => {
target.position = twoBezier(ratio, c1, c2, to);
};
return tween(target).to(duration, {}, opts).start();
}

我也刷到这个onUpdate的…这种太霸气了…完全打断了Tween设计的自由声明属性(比如世界坐标和相对坐标等)…还有连续动作之类的连贯性都没了…不予考虑哈…

做了个粗暴的方案…尽量维持Tween的调用方式…看看引擎组后面有没有其他计划吧…
(实现的不是贝塞尔…是基于y轴的抛物线…毕竟我的需求是这个…)

// 调用

tween(this.node)
    .by(0.5, { position: new Vec3(0, 50, 0) }, { easing: easing.backOut })
    .delay(0.5)
    .to(0.5, { worldPosition: Utils.jumpTo_v3(targetNode.worldPosition, 200) }, { easing: easing.sineInOut })
    .call(() => { console.log("[tween][end]"); })
    .start();

// 实现 Util.ts

import { math, Vec2, Vec3 } from "cc";

export class Utils {
    static jumpTo_v3(to: Vec3, height: number): Readonly<Vec3> {
        return { value: to, progress: this._jumpTo_progress(3, height) } as any as Vec3;
    }
    static jumpTo_v2(to: Vec3, height: number): Readonly<Vec2> {
        return { value: to, progress: this._jumpTo_progress(2, height) } as any as Vec2;
    }
    private static _jumpTo_progress(max: number, height: number): (from: number, to: number, cur: number, pcs: number) => number {
        let i = max;
        let heightSqrt = Math.sqrt(height);
        return (from: number, to: number, cur: number, pcs: number) => {

            // 使用序列耦合区分xyz轴: 1: x, 2: y, 3: z
            if (i >= max) i = 1;
            else i++;

            // let rsl = from + (to - from) * pcs; // lerp
            let rsl = math.lerp(from, to, pcs);

            if (i === 2) { // y轴的增量算法
                let du = Math.abs(1 - pcs * 2); // [0,1] > [1,0,1]
                rsl += height - Math.pow(heightSqrt * du, 2);
            }

            return rsl;
        };
    }
}

吐槽一下…3d版真的是摸一次坑一次…每次都是半天为单位 :upside_down_face:

1赞

其实也可以水平和垂直的tween分离 :thinking:

应该也可以…不过写起来就很遭罪了…链式调用又不好做外部函数扩展…

我这写了独立的tween用tween.parrallel就可以

我也写了个jump,非常hack,在node上塞了一些临时变量。。。

Node.prototype.qtJumpPosition = function(to: Vec3, jumpHeight: number, jumpNum: number, duration: number, opts?: ITweenOption) {
    const tweenPos = new Vec3();
    const jumpTween = tween(this);
    const totalNum = jumpNum * 2;

    this.jumpY = 0;
    let startPosY = 0;
    const yUpTween = tween().to(duration / totalNum, { jumpY: jumpHeight }, {
        onStart: (target: Node) => {
            startPosY = target.position.y;
            target.jumpY = 0;
        },
        onUpdate: (target: Node, ratio) => {
            tweenPos.set(target.position);
            tweenPos.y = startPosY + target.jumpY;
            target.position = tweenPos;
        },
        onComplete: (target: Node) => {
            target.jumpY = 0;
        }, easing: 'quadOut'
    }).to(duration / totalNum, { jumpY: jumpHeight }, {
        onStart: (target: Node) => {
            startPosY = target.position.y;
        },
        onUpdate: (target: Node, ratio) => {
            tweenPos.set(target.position);
            tweenPos.y = startPosY - target.jumpY;
            target.position = tweenPos;
        },
        onComplete: (target: Node) => {
            target.jumpY = 0;
        }, easing: 'quadIn',
    }).union().repeat(jumpNum);

    this.jumpOffsetY = 0;
    let offsetY = 0;
    const offsetYTween = tween().to(duration, { jumpOffsetY: to.y - this.position.y }, {
        onStart: (target: Node) => {
            offsetY = to.y - target.position.y;
            target.jumpOffsetY = 0;
        },
        onUpdate: (target: Node, ratio) => {
            const interpOffsetY = easing.quadOut(ratio) * offsetY;
            tweenPos.set(target.position);
            tweenPos.y += interpOffsetY;
            target.position = tweenPos;
        },
        onComplete: (target: Node) => {
            target.jumpOffsetY = 0;
        }, easing: 'quadOut'
    });

    this.jumpX = this.position.x;
    this.jumpZ = this.position.z;
    const xzTween = tween().to(duration, { jumpX: to.x, jumpZ: to.z }, {
        onStart: opts.onStart,
        onUpdate: (target: Node, ratio) => {
            tweenPos.set(target.position);
            tweenPos.x = target.jumpX;
            tweenPos.z = target.jumpZ;
            target.position = tweenPos;
            opts.onUpdate?.();
        },
        onComplete: (target: Node) => {
            // delete target.jumpX;
            // delete target.jumpY;
            // delete target.jumpZ;
            // delete target.jumpOffsetY;
            target.jumpX = target.position.x;
            target.jumpZ = target.position.z;
            opts.onComplete?.();
        }
    })

    jumpTween.parallel(yUpTween, offsetYTween, xzTween);
    return jumpTween;
}

https://github.com/gameall3d/QuickTween

tween(this.node.position)
x直线移动…
y加一个progress吗…?

rightAnim(mid:(target?:Node)=>void){
    let t = this.animDist;
    let lr = 0;
    return tween<Node>()
    .by(this.animDuration/2,{
    },{
        onStart(){
            lr = 0;
        },
        onUpdate(target:Node, ratio){
            ratio = sinIn(ratio);
            let p = target.position.clone();
            p.x += (ratio - lr)*t;
            target.setPosition(p);
            lr = ratio;
        },
        onComplete:mid,
    })
    .by(this.animDuration/2,{
    },{
        onStart(){
            lr = 0;
        },
        onUpdate(target:Node, ratio){
            ratio = sinOut(ratio);
            let p = target.position.clone();
            p.x -= (ratio - lr)*t;
            target.setPosition(p);
            lr = ratio;
        },
    });
}

我这边是2d用的不过大概就是这样…其他轴一起paraller就行。其实完全没利用tween控制属性只是借用流程…

哈哈…你这种也太粗暴了…临时变量也够多的…把 position 写到封装里也是我最介意的…

好吧好吧…你们全员都用 onUpdate 的 :joy:

该主题在最后一个回复创建后14天后自动关闭。不再允许新的回复。