-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
[v3.8.5] Support difference targets in sequence and parallel chain #16979
Conversation
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/>
|
TODO:
|
What's the usage with different targets? |
Could see the new code I added in the test case. Or refer to https://forum.cocos.org/t/topic/157814/7?u=dumganhar |
cocos/tween/actions/action.ts
Outdated
@@ -45,6 +45,7 @@ export class Action { | |||
|
|||
protected originalTarget: Node | null = null; | |||
protected target: Node | null = null; | |||
public workerTarget: Node | null = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does workerTarget
mean?
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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()
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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
@cocos-robot run test cases |
@cocos-robot run test cases |
…85-tween-different-targets
@cocos-robot run test cases |
@@ -389,6 +394,19 @@ export class Sequence extends ActionInterval { | |||
action._reversed = true; | |||
return action; | |||
} | |||
|
|||
updateWorkerTarget<T> (workerTarget: T): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why need this function?
There was a problem hiding this comment.
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.
@dumganhar, Please check the result of
Task Details |
@dumganhar, Please check the result of
Task Details
|
Fix a bug about union here: #17020 |
Re: #16978
Changelog
Continuous Integration
This pull request:
Compatibility Check
This pull request: