Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v3.8.5] Support difference targets in sequence and parallel chain #16979

Merged
merged 22 commits into from
May 21, 2024

Conversation

dumganhar
Copy link
Contributor

Re: #16978

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 dumganhar requested a review from minggo May 12, 2024 10:27
Copy link

github-actions bot commented May 12, 2024

Interface Check Report

! WARNING this pull request has changed these public interfaces:

@@ -54747,9 +54747,9 @@
          * 插入一个 tween 到队列中。
          * @method then
          * @param other @en The rear tween of this tween @zh 当前缓动的后置缓动
          */
-        then(other: Tween<T>): Tween<T>;
+        then<U extends object = any>(other: Tween<U>): Tween<T>;
         /**
          * @en
          * Sets tween target.
          * @zh
@@ -54854,18 +54854,18 @@
          * 添加一个队列 action。
          * @method sequence
          * @param args @en All tween that make up the sequence @zh 组成队列的所有缓动
          */
-        sequence(...args: Tween<T>[]): Tween<T>;
+        sequence(...args: Tween<any>[]): Tween<T>;
         /**
          * @en
          * Add a parallel action.
          * @zh
          * 添加一个并行 action。
          * @method parallel
          * @param args @en The tween parallel to this tween @zh 与当前缓动并行的缓动
          */
-        parallel(...args: Tween<T>[]): Tween<T>;
+        parallel(...args: Tween<any>[]): Tween<T>;
         /**
          * @en
          * Add a repeat action.
          * This action will integrate before actions to a sequence action as their parameters.
@@ -54947,9 +54947,9 @@
     }
     export class TweenAction<T> extends __private._cocos_tween_actions_action_interval__ActionInterval {
         constructor(duration: number, props: any, opts?: ITweenOption<T>);
         clone(): TweenAction<T>;
-        startWithTarget<U>(target: U | undefined): void;
+        startWithTarget<U>(target: U | null): void;
         update(t: number): void;
         progress(start: number, end: number, current: number, t: number): number;
     }
     /**
@@ -68875,10 +68875,57 @@
              * @static
              * @default -1
              */
             static TAG_INVALID: number;
+            /**
+             * The `originalTarget` and `target` are both assigned in `startWithTarget` method,
+             * and they get the same value normally. The difference between `originalTarget` and
+             * `target` is that `target` will be set to null after `stop` method is invoked
+             * but `originalTarget` will not. Therefore, ActionManager could remove a stopped action
+             * from hash map by searching action's `originalTarget`. You could refer to
+             * ActionManager.removeAction for the details.
+             */
             protected originalTarget: unknown;
             protected target: unknown;
+            /**
+             * The `workerTarget` was added from Cocos Creator 3.8.5 and it's used for nest `Tween` functionality.
+             * It stores the target of sub-tween and its value may be different from `target`.
+             *
+             * Example 1:
+             * ```ts
+             *   tween(node).to(1, { scale: new Vec3(2, 2, 2) }).start();
+             *   // target and original target are both `node`, workerTarget is `null`.
+             * ```
+             *
+             * Example 2:
+             * ```ts
+             *   tween(node).parallel(                                        // ----- Root tween
+             *       tween(node).to(1, { scale: new Vec3(2, 2, 2) }),         // ----- Sub tween 1
+             *       tween(node).to(1, { position: new Vec3(10, 10, 10) })    // ----- Sub Tween 2
+             *   ).start();
+             *   // Note that only root tween is started here. We call tweens in `parallel`/`sequence` sub tweens.
+             *   // The `target` and `originalTarget` of all internal actions are `node`.
+             *   // Actions in root tween: workerTarget = null
+             *   // Actions in sub tween 1: workerTarget = node
+             *   // Actions in sub tween 2: workerTarget = node
+             * ```
+             *
+             * Example 3:
+             * ```ts
+             *   tween(node).parallel(                                        // ----- Root tween
+             *       tween(node).to(1, { scale: new Vec3(2, 2, 2) }),         // ----- Sub tween 1
+             *       tween(node.getComponent(UITransform)).to(1, {            // ----- Sub Tween 2
+             *           contentSize: new Size(10, 10)
+             *       })
+             *   ).start();
+             *   // Note that only root tween is started here. We call tweens in `parallel`/`sequence` sub tweens.
+             *   // The `target` and `originalTarget` of all internal actions are `node`.
+             *   // Actions in root tween: workerTarget = null
+             *   // Actions in sub tween 1: workerTarget = node
+             *   // Actions in sub tween 2: workerTarget = node's UITransform component
+             * ```
+             */
+            workerTarget: unknown;
             protected tag: number;
             /**
              * @en
              * to copy object with deep copy.
@@ -69194,9 +69241,9 @@
             protected _elapsed: number;
             protected _firstTick: boolean;
             protected _speed: number;
             protected _repeatForever: boolean;
-            _repeatMethod: boolean;
+            protected _repeatMethod: boolean;
             protected _speedMethod: boolean;
             constructor(d?: number);
             getElapsed(): number;
             initWithDuration(d: number): boolean;
@@ -69205,10 +69252,8 @@
             abstract clone(): _cocos_tween_actions_action_interval__ActionInterval;
             step(dt: number): void;
             startWithTarget<T>(target: T | null): void;
             reverse(): _cocos_tween_actions_action_interval__ActionInterval;
-            setAmplitudeRate(amp: number): void;
-            getAmplitudeRate(): number;
             /**
              * @en
              * Changes the speed of an action, making it take longer (speed>1)
              * or less (speed<1) time. <br/>

@dumganhar dumganhar marked this pull request as draft May 12, 2024 14:33
@dumganhar
Copy link
Contributor Author

dumganhar commented May 12, 2024

TODO:

  • handle .then
  • nest tween in deep depth
  • nest sequence and parallel
  • re-targetting
  • clone with target

@minggo
Copy link
Contributor

minggo commented May 13, 2024

What's the usage with different targets?

@dumganhar
Copy link
Contributor Author

Could see the new code I added in the test case. Or refer to https://forum.cocos.org/t/topic/157814/7?u=dumganhar

@@ -45,6 +45,7 @@ export class Action {

protected originalTarget: Node | null = null;
protected target: Node | null = null;
public workerTarget: Node | null = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does workerTarget mean?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And what's the difference between workerTarget and target?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

workerTarget is the actual target tween action runs on when the target initialized in sequence, parallel or then is differernt from the root target. For example:

tween(this.node).parallel( // this.node is the root target, start method will assign this.node as `target`
  tween(this.node.getComponent(UITransform))).to(XXXXX), // UITransform target is different from `this.node`, so it's the actual target this tween wants to run on. I call it `workerTarget`
  tween(this.node.getComponent(UIOpacity))).to(XXXXX)
)
.start()

When start is inovked, only the root target this.node will be set to every internal actions in the old code, so any other targets in sequence, parallel and then will be ignored.

I added a new property workerTarget to support this feature. But yes, there is probably a better name for it. Maybe proxyTarget or just subTarget.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or to make it clearer, targetToApply?
If developer apply the same root target for different sub tweens, then targetToApply is the root target.

const targetToApply =  this.targetToApply ?? this.target; // targetToApply may be null if all sub tweens have the same targets.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will happen if workerTarget is not a component, but a node, for example

tween(this.node).parallel( // this.node is the root target, start method will assign this.node as `target`
  tween(this.node).to(XXXXX),
  tween(this.node.getComponent(UIOpacity))).to(XXXXX)
)
.start()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this example,

tween(this.node).parallel(   // this.node is the target for root tween, target = this.node, workerTarget = null
  tween(this.node).to(XXXXX), // target = this.node, workerTarget = this.node
  tween(this.node.getComponent(UIOpacity))).to(XXXXX)  // target = this.node, workerTarget = this.node
)
.start()

In fact, each tween owns a target, the targets in sub-tween of parallel/sequence will be lost since the parallel/sequence only pick up the internal actions of tween stores.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And since the target type is a generic T, it could be any object types, not only Node/Component.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the declaration type is Node | null.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's unknown type now. T for Tween will contain generic type, while passing to Action, it's an unknown type.

Construct Tween with T or Tween.clone/target with .

class Tween<T extends object = any> {
    constructor(target?: T | null) {...}
    clone<U extends object = any> (target: U): Tween<U> { ... }
    target<U extends object = any> (target: U): Tween<U> { ... }
}

When we start a tween, target with T will be passed to ActionManager:

addAction<T> (action: Action | null, target: T, paused: boolean): void {
    ...
    action.startWithTarget(target); // It will call action's startWithTarget<T>(target)
}

startWithTarget<T> (target: T | null): void {
        this.originalTarget = target;
        this.target = target;  // We use unknown type to store target
    }

    protected originalTarget: unknown = null;
    protected target: unknown = null;
    public workerTarget: unknown = null;

In tween-actions.ts, since we need to recover the T for the callback ( onStart/onUpdate/onComplete) , we convert unknown to T.

    startWithTarget<U> (target: U | null): void {
        const isEqual: TypeEquality<T, U> = true;
        if (!isEqual) return;
        super.startWithTarget(target);

        const workerTarget = (this.workerTarget ?? this.target) as T;
        if (!workerTarget) return;
        ......
        if (this._opts!.onStart) { this._opts!.onStart(workerTarget); }
update (t: number): void {
        const workerTarget = this.workerTarget ?? this.target;
        if (!workerTarget) return;
        ......
        if (opts.onUpdate) { opts.onUpdate(workerTarget as T, t); }
        if (t === 1 && opts.onComplete) { opts.onComplete(workerTarget as T); }

…85-tween-different-targets

# Conflicts:
#	cocos/tween/actions/action-instant.ts
#	cocos/tween/actions/action.ts
#	cocos/tween/tween-action.ts
#	cocos/tween/tween.ts
@dumganhar dumganhar marked this pull request as ready for review May 16, 2024 11:03
@dumganhar dumganhar requested a review from minggo May 16, 2024 11:03
@dumganhar
Copy link
Contributor Author

@cocos-robot run test cases

@dumganhar
Copy link
Contributor Author

@cocos-robot run test cases

@dumganhar
Copy link
Contributor Author

@cocos-robot run test cases

@@ -389,6 +394,19 @@ export class Sequence extends ActionInterval {
action._reversed = true;
return action;
}

updateWorkerTarget<T> (workerTarget: T): void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why need this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used for update the workerTarget (subTarget or call it runningTarget) in Sequence/Spawn actions.
Sequence and Spawn actions are containers for storing some other actions ( child actions) . Child actions may have different target to apply to, so we need a method to update the target.

Copy link

@dumganhar, Please check the result of run test cases:

Task Details

Copy link

@dumganhar, Please check the result of run test cases:

Task Details

Platform build boot runned crashScene FailScene
web-mobile PASS PASS PASS
windows PASS PASS FAIL capture_to_web,cameraUseRenderTex,use-render-texture-asset
android PASS PASS PASS

@dumganhar dumganhar merged commit 0a7b117 into cocos:v3.8.5 May 21, 2024
9 checks passed
@dumganhar
Copy link
Contributor Author

Fix a bug about union here: #17020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants