cocos-engine icon indicating copy to clipboard operation
cocos-engine copied to clipboard

fixed #17207: Add Tween.updateUntil low-level tween API

Open dumganhar opened this issue 1 year ago • 15 comments

Re: #17207

Suppose there is an object (A) who wants to trace another object (B), when A gets close to B, A plays an animation to scale itself to 2 in 0.25s and reverse to scale 1 later. We could add an 'updateUntil' action to make A trace B, if A gets close to B, the callback function returns true, which means that 'updateUnitl' should be finished now, otherwise, the callback will be invoked every frame and the actions after it will not be executed 'until' its callback returns true.

I wrote the following code to demostrate.

import { _decorator, Component, Node, tween, v3, Vec3 } from 'cc';
const { ccclass, property } = _decorator;

const positionTmp = new Vec3();
const positionTmp2 = new Vec3();

@ccclass('TweenTest')
export class TweenTest extends Component {

    @property(Node)
    targetNode: Node | null = null;

    start() {
        tween(this.node)
            .updateUntil((curNode: Node, dt: number)=>{
                const d = Vec3.copy(positionTmp2, this.targetNode!.position).subtract(curNode.position);
                const length = d.length();
                if (length < 10) {
                    return true;
                }
                
                const newPos = Vec3.copy(positionTmp, curNode.position).add(d.normalize().multiplyScalar(length / 10 * dt * 10));
                curNode.setPosition(newPos);
                return false;
            })
            .by(0.25, { scale: v3(1, 1, 0) }, { easing: 'cubicInOut' }).id(1)
            .reverse(1)
            .call((curNode?: Node)=>{
                const newPos = v3((Math.random() - 0.5) * 400, (Math.random() - 0.5) * 400, 0);
                if (newPos.y < 100 && newPos.y > 0) newPos.y = 100;
                if (newPos.y > -100 && newPos.y < 0) newPos.y = -100;

                if (newPos.x < 100 && newPos.x > 0) newPos.x = 100;
                if (newPos.x > -100 && newPos.x < 0) newPos.x = -100;
                curNode?.setPosition(newPos);
            })
            .union()
            .repeatForever()
            .start();

        tween(this.node)
            .by(1, { angle: 360 })
            .repeatForever()
            .start();

    }
}

And I recorded a video for that:

https://github.com/cocos/cocos-engine/assets/493372/4c7df413-7eff-4137-aafa-bc37df732102

Changelog


Continuous Integration

This pull request:

  • [ ] needs automatic test cases check.

    Manual trigger with @cocos-robot run test cases afterward.

  • [ ] does not change any runtime related code or build configuration

    If any reviewer thinks the CI checks are needed, please uncheck this option, then close and reopen the issue.


Compatibility Check

This pull request:

  • [ ] changes public API, and have ensured backward compatibility with deprecated features.
  • [ ] affects platform compatibility, e.g. system version, browser version, platform sdk version, platform toolchain, language version, hardware compatibility etc.
  • [ ] affects file structure of the build package or build configuration which requires user project upgrade.
  • [ ] introduces breaking changes, please list all changes, affected features and the scope of violation.

dumganhar avatar Jun 29 '24 11:06 dumganhar

@smallmain could review this now.

dumganhar avatar Jun 29 '24 11:06 dumganhar

@cocos-robot run test cases

dumganhar avatar Jun 29 '24 11:06 dumganhar

Interface Check Report

! WARNING this pull request has changed these public interfaces:

@@ -54842,8 +54842,9 @@
         startWithTarget<U>(target: U | null): void;
         stop(): void;
         update(t: number): void;
         progress(start: number, end: number, current: number, t: number): number;
+        isUnknownDuration(): boolean;
     }
     export namespace tweenProgress {
         export function bezier(...knots: ReadonlyArray<math.Vec3>): ITweenCustomProperty<math.Vec3>;
         export function catmullRom(...knots: ReadonlyArray<math.Vec3>): ITweenCustomProperty<math.Vec3>;
@@ -54893,8 +54894,9 @@
         onStop?: () => void;
         onComplete?: () => void;
     }
     export type TweenUpdateCallback<T extends object, Args extends any[]> = (target: T, ratio: number, ...args: Args) => void;
+    export type TweenUpdateUntilCallback<T extends object, Args extends any[]> = (target: T, dt: number, ...args: Args) => boolean;
     /**
      * @en
      * Tween provide a simple and flexible way to action, It's transplanted from cocos creator。
      * @zh
@@ -55057,17 +55059,25 @@
          * @return @en The instance itself for easier chaining. @zh 返回该实例本身,以便于链式调用。
          */
         by(duration: number, props: __private._cocos_tween_tween__ConstructorType<T>, opts?: ITweenOption<T>): Tween<T>;
         /**
-         * @en Add an custom action.
-         * @zh 添加一个自定义动作。
+         * @en Add a custom action with constant duration.
+         * @zh 添加一个固定时长的自定义动作。
          * @param duration @en The tween time in seconds. @zh 缓动时间,单位为秒。
          * @param cb @en The callback of the current action. @zh 动作回调函数。
          * @param args @en The arguments passed to the callback function. @zh 传递给动作回调函数的参数。
          * @return @en The instance itself for easier chaining. @zh 返回该实例本身,以便于链式调用。
          */
         update<Args extends any[]>(duration: number, cb: TweenUpdateCallback<T, Args>, ...args: Args): Tween<T>;
         /**
+         * @en Add a custom action with unknown duration. If the callback returns true means this action is finished.
+         * @zh 添加一个不确定时长的自定义动作。如果回调函数返回 true,表示当前动作结束。
+         * @param cb @en The callback of the current action. @zh 动作回调函数。如果回调函数返回 true,表示当前动作结束。
+         * @param args @en The arguments passed to the callback function. @zh 传递给动作回调函数的参数。
+         * @return @en The instance itself for easier chaining. @zh 返回该实例本身,以便于链式调用。
+         */
+        updateUntil<Args extends any[]>(cb: TweenUpdateUntilCallback<T, Args>, ...args: Args): Tween<T>;
+        /**
          * @en
          * Directly set target properties.
          * @zh
          * 直接设置 target 的属性。
@@ -70514,8 +70524,9 @@
              * @return {FiniteTimeAction}
              */
             abstract clone(): _cocos_tween_actions_action__FiniteTimeAction;
             abstract reverse(): _cocos_tween_actions_action__FiniteTimeAction;
+            abstract isUnknownDuration(): boolean;
         }
         /**
          * @en
          * <p> An interval action is an action that takes place within a certain period of time. <br/>

github-actions[bot] avatar Jun 29 '24 11:06 github-actions[bot]

TODO: Update the updateUntil API documentation.

dumganhar avatar Jun 29 '24 11:06 dumganhar

amazing!

smallmain avatar Jun 29 '24 15:06 smallmain

@cocos-robot run test cases

dumganhar avatar Jun 30 '24 14:06 dumganhar

@cocos-robot run test cases

dumganhar avatar Jul 01 '24 05:07 dumganhar

@cocos-robot run test cases

dumganhar avatar Jul 01 '24 05:07 dumganhar

@cocos-robot run test cases

dumganhar avatar Jul 01 '24 08:07 dumganhar

@cocos-robot run test cases

sushanhong avatar Jul 01 '24 08:07 sushanhong

@cocos-robot run test cases

sushanhong avatar Jul 01 '24 08:07 sushanhong

@cocos-robot run test cases

sushanhong avatar Jul 01 '24 09:07 sushanhong

@cocos-robot run test cases

sushanhong avatar Jul 01 '24 09:07 sushanhong

@cocos-robot run test cases

sushanhong avatar Jul 01 '24 09:07 sushanhong

@cocos-robot run test cases

dumganhar avatar Jul 01 '24 11:07 dumganhar

@cocos-robot run test cases

dumganhar avatar Jul 02 '24 02:07 dumganhar

@cocos-robot run test cases

shaoqiangcai avatar Jul 02 '24 02:07 shaoqiangcai