diff --git a/cocos/tween/actions/action-interval.ts b/cocos/tween/actions/action-interval.ts
index c1a6b1fe93d..22d27fa1511 100644
--- a/cocos/tween/actions/action-interval.ts
+++ b/cocos/tween/actions/action-interval.ts
@@ -1,10 +1,7 @@
/*
- Copyright (c) 2008-2010 Ricardo Quesada
- Copyright (c) 2011-2012 cocos2d-x.org
- Copyright (c) 2013-2016 Chukong Technologies Inc.
- Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
+ Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
- http://www.cocos2d-x.org
+ https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -25,948 +22,139 @@
THE SOFTWARE.
*/
-import { FiniteTimeAction, Action } from './action';
-import { macro, logID, errorID } from '../../core';
-import { ActionInstant } from './action-instant';
+import { Vec2 } from '../../cocos/core/math/vec2';
+import { log } from '../../cocos/core/platform/debug';
+import { macro } from '../../cocos/core/platform/macro';
+import { Touch } from '../../cocos/input/types';
-/**
- * @en
- *
An interval action is an action that takes place within a certain period of time.
- * It has an start time, and a finish time. The finish time is the parameter
- * duration plus the start time.
- *
- * These CCActionInterval actions have some interesting properties, like:
- * - They can run normally (default)
- * - They can run reversed with the reverse method
- * - They can run with the time altered with the Accelerate, AccelDeccel and Speed actions.
- *
- * For example, you can simulate a Ping Pong effect running the action normally and
- * then running it again in Reverse mode.
- * @zh 时间间隔动作,这种动作在已定时间内完成,继承 FiniteTimeAction。
- * @class ActionInterval
- * @extends FiniteTimeAction
- * @param {Number} d duration in seconds
- */
-export class ActionInterval extends FiniteTimeAction {
- protected MAX_VALUE = 2;
- protected _elapsed = 0;
- protected _firstTick = false;
- protected _easeList: Function[] = [];
- protected _speed = 1;
- protected _repeatForever = false;
- _repeatMethod = false; // Compatible with repeat class, Discard after can be deleted
- protected _speedMethod = false; // Compatible with repeat class, Discard after can be deleted
-
- constructor (d?: number) {
- super();
- if (d !== undefined && !isNaN(d)) {
- this.initWithDuration(d);
- }
- }
-
- /*
- * How many seconds had elapsed since the actions started to run.
- * @return {Number}
- */
- getElapsed (): number {
- return this._elapsed;
- }
-
- /*
- * Initializes the action.
- * @param {Number} d duration in seconds
- * @return {Boolean}
- */
- initWithDuration (d: number): boolean {
- this._duration = (d === 0) ? macro.FLT_EPSILON : d;
- // prevent division by 0
- // This comparison could be in step:, but it might decrease the performance
- // by 3% in heavy based action games.
- this._elapsed = 0;
- this._firstTick = true;
- return true;
- }
-
- isDone (): boolean {
- return (this._elapsed >= this._duration);
- }
-
- _cloneDecoration (action: ActionInterval): void {
- action._repeatForever = this._repeatForever;
- action._speed = this._speed;
- action._timesForRepeat = this._timesForRepeat;
- action._easeList = this._easeList;
- action._speedMethod = this._speedMethod;
- action._repeatMethod = this._repeatMethod;
- }
-
- _reverseEaseList (action: ActionInterval): void {
- if (this._easeList) {
- action._easeList = [];
- for (let i = 0; i < this._easeList.length; i++) {
- action._easeList.push(this._easeList[i]);
- }
- }
- }
-
- clone (): ActionInterval {
- const action = new ActionInterval(this._duration);
- this._cloneDecoration(action);
- return action;
- }
+const tempVec2 = new Vec2();
+class TouchManager {
/**
- * @en Implementation of ease motion.
- * @zh 缓动运动。
- * @method easing
- * @param {Object} easeObj
- * @returns {ActionInterval}
- * @example
- * import { easeIn } from 'cc';
- * action.easing(easeIn(3.0));
+ * A map from touch ID to touch object.
*/
- easing (easeObj: any): ActionInterval {
- if (this._easeList) this._easeList.length = 0;
- else this._easeList = [];
- for (let i = 0; i < arguments.length; i++) this._easeList.push(arguments[i]);
- return this;
- }
-
- _computeEaseTime (dt: any): any {
- // var locList = this._easeList;
- // if ((!locList) || (locList.length === 0))
- // return dt;
- // for (var i = 0, n = locList.length; i < n; i++)
- // dt = locList[i].easing(dt);
- return dt;
- }
+ public _touchMap: Map;
+ private readonly _maxTouches = 8;
- step (dt: number): void {
- if (this._firstTick) {
- this._firstTick = false;
- this._elapsed = 0;
- } else this._elapsed += dt;
-
- // this.update((1 > (this._elapsed / this._duration)) ? this._elapsed / this._duration : 1);
- // this.update(Math.max(0, Math.min(1, this._elapsed / Math.max(this._duration, cc.macro.FLT_EPSILON))));
- let t = this._elapsed / (this._duration > 0.0000001192092896 ? this._duration : 0.0000001192092896);
- t = (t < 1 ? t : 1);
- this.update(t > 0 ? t : 0);
-
- // Compatible with repeat class, Discard after can be deleted (this._repeatMethod)
- if (this._repeatMethod && this._timesForRepeat > 1 && this.isDone()) {
- if (!this._repeatForever) {
- this._timesForRepeat--;
- }
- // var diff = locInnerAction.getElapsed() - locInnerAction._duration;
- this.startWithTarget(this.target);
- // to prevent jerk. issue #390 ,1247
- // this._innerAction.step(0);
- // this._innerAction.step(diff);
- this.step(this._elapsed - this._duration);
- }
- }
-
- startWithTarget (target: any): void {
- Action.prototype.startWithTarget.call(this, target);
- this._elapsed = 0;
- this._firstTick = true;
- }
-
- reverse (): ActionInterval {
- logID(1010);
- return this;
- }
-
- /*
- * Set amplitude rate.
- * @warning It should be overridden in subclass.
- * @param {Number} amp
- */
- setAmplitudeRate (amp: any): void {
- // Abstract class needs implementation
- logID(1011);
- }
-
- /*
- * Get amplitude rate.
- * @warning It should be overridden in subclass.
- * @return {Number} 0
- */
- getAmplitudeRate (): number {
- // Abstract class needs implementation
- logID(1012);
- return 0;
- }
-
- /**
- * @en
- * Changes the speed of an action, making it take longer (speed>1)
- * or less (speed<1) time.
- * Useful to simulate 'slow motion' or 'fast forward' effect.
- * @zh
- * 改变一个动作的速度,使它的执行使用更长的时间(speed > 1)
- * 或更少(speed < 1)可以有效得模拟“慢动作”或“快进”的效果。
- * @param {Number} speed
- * @returns {Action}
- */
- speed (speed: number): Action {
- if (speed <= 0) {
- logID(1013);
- return this;
- }
-
- this._speedMethod = true; // Compatible with repeat class, Discard after can be deleted
- this._speed *= speed;
- return this;
- }
-
- /**
- * @en
- * Get this action speed.
- * @zh
- * 返回此动作速度
- * @return {Number}
- */
- getSpeed (): number {
- return this._speed;
- }
-
- /**
- * @en
- * Set this action speed.
- * @zh
- * 设置此动作速度
- * @param {Number} speed
- * @returns {ActionInterval}
- */
- setSpeed (speed: number): ActionInterval {
- this._speed = speed;
- return this;
+ constructor () {
+ this._touchMap = new Map();
}
/**
- * @en
- * Repeats an action a number of times.
- * To repeat an action forever use the CCRepeatForever action.
- * @zh 重复动作可以按一定次数重复一个动作,使用 RepeatForever 动作来永远重复一个动作。
- * @method repeat
- * @param {Number} times
- * @returns {ActionInterval}
+ * The original touch object can't be modified, so we need to return the cloned touch object.
+ * @param touch
+ * @returns
*/
- repeat (times: number): ActionInterval {
- times = Math.round(times);
- if (isNaN(times) || times < 1) {
- logID(1014);
- return this;
- }
- this._repeatMethod = true; // Compatible with repeat class, Discard after can be deleted
- this._timesForRepeat *= times;
- return this;
+ public _cloneTouch (touch: Touch): Touch {
+ const touchID = touch.getID();
+ touch.getStartLocation(tempVec2);
+ const clonedTouch = new Touch(tempVec2.x, tempVec2.y, touchID);
+ touch.getLocation(tempVec2);
+ clonedTouch.setPoint(tempVec2.x, tempVec2.y);
+ touch.getPreviousLocation(tempVec2);
+ clonedTouch.setPrevPoint(tempVec2);
+ return clonedTouch;
}
/**
- * @en
- * Repeats an action for ever.
- * To repeat the an action for a limited number of times use the Repeat action.
- * @zh 永远地重复一个动作,有限次数内重复一个动作请使用 Repeat 动作。
- * @method repeatForever
- * @returns {ActionInterval}
- */
- repeatForever (): ActionInterval {
- this._repeatMethod = true; // Compatible with repeat class, Discard after can be deleted
- this._timesForRepeat = this.MAX_VALUE;
- this._repeatForever = true;
- return this;
+ * Create the touch object at the touch start event callback.
+ * we have some policy to create the touch object:
+ * - If the number of touches doesn't exceed the max count, we create a touch object.
+ * - If the number of touches exceeds the max count, we discard the timeout touch to create a new one.
+ * - If the number of touches exceeds the max count and there is no timeout touch, we can't create any touch object.
+ * @param touchID
+ * @param x
+ * @param y
+ * @returns
+ */
+ private _createTouch (touchID: number, x: number, y: number): Touch | undefined {
+ if (this._touchMap.has(touchID)) {
+ log('Cannot create the same touch object.');
+ return undefined;
+ }
+ const checkResult = this._checkTouchMapSizeMoreThanMax(touchID);
+ if (checkResult) {
+ log('The touches is more than MAX_TOUCHES.'); // TODO: logID 2300
+ return undefined;
+ }
+ const touch = new Touch(x, y, touchID);
+ this._touchMap.set(touchID, touch);
+ this._updateTouch(touch, x, y);
+ return touch;
}
-}
-
-/*
- * Runs actions sequentially, one after another.
- */
-export class Sequence extends ActionInterval {
- static _actionOneTwo = function (actionOne: ActionInterval, actionTwo: ActionInterval): Sequence {
- const sequence = new Sequence();
- sequence.initWithTwoActions(actionOne, actionTwo);
- return sequence;
- }
-
- private _actions: ActionInterval[] = [];
- private _split = 0;
- private _last = 0;
- private _reversed = false;
/**
- * @example
- * import { Sequence } from 'cc';
- *
- * // create sequence with actions
- * const seq = new Sequence(act1, act2);
- *
- * // create sequence with array
- * const seq = new Sequence(actArray);
+ * Release the touch object at the touch end or touch cancel event callback.
+ * @param touchID
+ * @returns
*/
- constructor (...actions: FiniteTimeAction[]);
- constructor (tempArray: any) {
- super();
-
- const paramArray = (tempArray instanceof Array) ? tempArray : arguments;
- if (paramArray.length === 1) {
- errorID(1019);
+ public releaseTouch (touchID: number): void {
+ if (!this._touchMap.has(touchID)) {
return;
}
- const last = paramArray.length - 1;
- if ((last >= 0) && (paramArray[last] == null)) logID(1015);
-
- if (last >= 0) {
- let prev = paramArray[0]; let action1: any;
- for (let i = 1; i < last; i++) {
- if (paramArray[i]) {
- action1 = prev;
- prev = Sequence._actionOneTwo(action1, paramArray[i]);
- }
- }
- this.initWithTwoActions(prev, paramArray[last]);
- }
- }
-
- /*
- * Initializes the action
- * @param {FiniteTimeAction} actionOne
- * @param {FiniteTimeAction} actionTwo
- * @return {Boolean}
- */
- initWithTwoActions (actionOne: any, actionTwo: any): boolean {
- if (!actionOne || !actionTwo) {
- errorID(1025);
- return false;
- }
-
- let durationOne = actionOne._duration; let durationTwo = actionTwo._duration;
- durationOne *= actionOne._repeatMethod ? actionOne._timesForRepeat : 1;
- durationTwo *= actionTwo._repeatMethod ? actionTwo._timesForRepeat : 1;
- const d = durationOne + durationTwo;
- this.initWithDuration(d);
-
- this._actions[0] = actionOne;
- this._actions[1] = actionTwo;
- return true;
- }
-
- clone (): any {
- const action = new Sequence();
- this._cloneDecoration(action as any);
- action.initWithTwoActions(this._actions[0].clone(), this._actions[1].clone());
- return action as any;
- }
-
- startWithTarget (target: any): void {
- ActionInterval.prototype.startWithTarget.call(this, target);
- this._split = this._actions[0]._duration / this._duration;
- this._split *= this._actions[0]._repeatMethod ? this._actions[0]._timesForRepeat : 1;
- this._last = -1;
- }
-
- stop (): void {
- // Issue #1305
- if (this._last !== -1) this._actions[this._last].stop();
- Action.prototype.stop.call(this);
+ this._touchMap.delete(touchID);
}
- update (dt: number): void {
- let new_t: number; let found = 0;
- const locSplit = this._split;
- const locActions = this._actions;
- const locLast = this._last;
- let actionFound: ActionInterval;
-
- dt = this._computeEaseTime(dt);
- if (dt < locSplit) {
- // action[0]
- new_t = (locSplit !== 0) ? dt / locSplit : 1;
-
- if (found === 0 && locLast === 1 && this._reversed) {
- // Reverse mode ?
- // XXX: Bug. this case doesn't contemplate when _last==-1, found=0 and in "reverse mode"
- // since it will require a hack to know if an action is on reverse mode or not.
- // "step" should be overriden, and the "reverseMode" value propagated to inner Sequences.
- locActions[1].update(0);
- locActions[1].stop();
- }
+ /**
+ * Get touch object by touch ID.
+ * @param touchID
+ * @returns
+ */
+ public getTouch (touchID: number, x: number, y: number, clone?: boolean): Touch | undefined {
+ let touch = this._touchMap.get(touchID);
+ if (!touch) {
+ touch = this._createTouch(touchID, x, y);
} else {
- // action[1]
- found = 1;
- new_t = (locSplit === 1) ? 1 : (dt - locSplit) / (1 - locSplit);
-
- if (locLast === -1) {
- // action[0] was skipped, execute it.
- locActions[0].startWithTarget(this.target);
- locActions[0].update(1);
- locActions[0].stop();
- }
- if (locLast === 0) {
- // switching to action 1. stop action 0.
- locActions[0].update(1);
- locActions[0].stop();
- }
- }
-
- actionFound = locActions[found];
- // Last action found and it is done.
- if (locLast === found && actionFound.isDone()) return;
-
- // Last action not found
- if (locLast !== found) actionFound.startWithTarget(this.target);
-
- new_t *= actionFound._timesForRepeat;
- actionFound.update(new_t > 1 ? new_t % 1 : new_t);
- this._last = found;
- }
-
- reverse (): any {
- const action = Sequence._actionOneTwo(this._actions[1].reverse(), this._actions[0].reverse());
- this._cloneDecoration(action);
- this._reverseEaseList(action);
- action._reversed = true;
- return action as any;
- }
-}
-
-/**
- * @en
- * Helper constructor to create an array of sequenceable actions
- * The created action will run actions sequentially, one after another.
- * @zh 顺序执行动作,创建的动作将按顺序依次运行。
- * @method sequence
- * @param {FiniteTimeAction|FiniteTimeAction[]} actionOrActionArray
- * @param {FiniteTimeAction} ...tempArray
- * @return {ActionInterval}
- * @example
- * import { sequence } from 'cc';
- *
- * // Create sequence with actions
- * const seq = sequence(act1, act2);
- *
- * // Create sequence with array
- * const seq = sequence(actArray);
- */
-// todo: It should be use new
-export function sequence (/* Multiple Arguments */tempArray: any): ActionInterval {
- const paramArray = (tempArray instanceof Array) ? tempArray : arguments;
- if (paramArray.length === 1) {
- errorID(1019);
- return null as any;
- }
- const last = paramArray.length - 1;
- if ((last >= 0) && (paramArray[last] == null)) logID(1015);
-
- let result: any = null;
- if (last >= 0) {
- result = paramArray[0];
- for (let i = 1; i <= last; i++) {
- if (paramArray[i]) {
- result = Sequence._actionOneTwo(result, paramArray[i]);
- }
- }
- }
-
- return result;
-}
-
-/*
- * Repeats an action a number of times.
- * To repeat an action forever use the CCRepeatForever action.
- * @class Repeat
- * @extends ActionInterval
- * @param {FiniteTimeAction} action
- * @param {Number} times
- * @example
- * import { Repeat, sequence } from 'cc';
- * const rep = new Repeat(sequence(jump2, jump1), 5);
- */
-export class Repeat extends ActionInterval {
- private _times = 0;
- private _total = 0;
- private _nextDt = 0;
- private _actionInstant = false;
- private _innerAction: FiniteTimeAction | null = null;
-
- constructor (action?: any, times?: any) {
- super();
- times !== undefined && this.initWithAction(action, times);
- }
-
- /*
- * @param {FiniteTimeAction} action
- * @param {Number} times
- * @return {Boolean}
- */
- initWithAction (action: FiniteTimeAction, times: number): boolean {
- const duration = action._duration * times;
-
- if (this.initWithDuration(duration)) {
- this._times = times;
- this._innerAction = action;
- if (action instanceof ActionInstant) {
- this._actionInstant = true;
- this._times -= 1;
- }
- this._total = 0;
- return true;
+ this._updateTouch(touch, x, y);
}
- return false;
- }
-
- clone (): Repeat {
- const action = new Repeat();
- this._cloneDecoration(action);
- action.initWithAction(this._innerAction!.clone(), this._times);
- return action;
- }
-
- startWithTarget (target: any): void {
- this._total = 0;
- this._nextDt = this._innerAction!._duration / this._duration;
- ActionInterval.prototype.startWithTarget.call(this, target);
- this._innerAction!.startWithTarget(target);
+ return touch ? clone === false ? touch : this._cloneTouch(touch) : undefined;
}
- stop (): void {
- this._innerAction!.stop();
- Action.prototype.stop.call(this);
- }
-
- update (dt: number): void {
- dt = this._computeEaseTime(dt);
- const locInnerAction = this._innerAction!;
- const locDuration = this._duration;
- const locTimes = this._times;
- let locNextDt = this._nextDt;
-
- if (dt >= locNextDt) {
- while (dt > locNextDt && this._total < locTimes) {
- locInnerAction.update(1);
- this._total++;
- locInnerAction.stop();
- locInnerAction.startWithTarget(this.target);
- locNextDt += locInnerAction._duration / locDuration;
- this._nextDt = locNextDt > 1 ? 1 : locNextDt;
- }
-
- // fix for issue #1288, incorrect end value of repeat
- if (dt >= 1.0 && this._total < locTimes) {
- // fix for cocos-creator/fireball/issues/4310
- locInnerAction.update(1);
- this._total++;
- }
-
- // don't set a instant action back or update it, it has no use because it has no duration
- if (!this._actionInstant) {
- if (this._total === locTimes) {
- locInnerAction.stop();
- } else {
- // issue #390 prevent jerk, use right update
- locInnerAction.update(dt - (locNextDt - locInnerAction._duration / locDuration));
- }
+ /**
+ * Get all the current touches objects.
+ * @returns
+ */
+ public getAllTouches (clone?: boolean): Touch[] {
+ const touches: Touch[] = [];
+ this._touchMap.forEach((touch) => {
+ if (touch) {
+ touches.push(clone === false ? touch : this._cloneTouch(touch));
}
- } else {
- locInnerAction.update((dt * locTimes) % 1.0);
- }
- }
-
- isDone (): boolean {
- return this._total === this._times;
+ });
+ return touches;
}
- reverse (): any {
- const action = new Repeat(this._innerAction!.reverse(), this._times);
- this._cloneDecoration(action);
- this._reverseEaseList(action);
- return action as any;
- }
-
- /*
- * Set inner Action.
- * @param {FiniteTimeAction} action
+ /**
+ * Update the location and previous location of current touch ID.
+ * @param touchID
+ * @param x The current location X
+ * @param y The current location Y
*/
- setInnerAction (action: any): void {
- if (this._innerAction !== action) {
- this._innerAction = action;
- }
+ private _updateTouch (touch: Touch, x: number, y: number): void {
+ touch.getLocation(tempVec2);
+ touch.setPrevPoint(tempVec2);
+ touch.setPoint(x, y);
}
- /*
- * Get inner Action.
- * @return {FiniteTimeAction}
- */
- getInnerAction (): FiniteTimeAction | null {
- return this._innerAction;
- }
-}
-
-/**
- * @en Creates a Repeat action. Times is an unsigned integer between 1 and pow(2,30)
- * @zh 重复动作,可以按一定次数重复一个动,如果想永远重复一个动作请使用 repeatForever 动作来完成。
- * @method repeat
- * @param {FiniteTimeAction} action
- * @param {Number} times
- * @return {Action}
- * @example
- * import { repeat, sequence } from 'cc';
- * const rep = repeat(sequence(jump2, jump1), 5);
- */
-export function repeat (action: any, times: any): Action {
- return new Repeat(action, times);
-}
-
-/*
- * Repeats an action for ever.
- * To repeat the an action for a limited number of times use the Repeat action.
- * @warning This action can't be Sequenceable because it is not an IntervalAction
- * @class RepeatForever
- * @extends ActionInterval
- * @param {ActionInterval} action
- * @example
- * import { sequence, RepeatForever } from 'cc';
- * const rep = new RepeatForever(sequence(jump2, jump1), 5);
- */
-export class RepeatForever extends ActionInterval {
- private _innerAction: ActionInterval | null = null;
-
- constructor (action?: ActionInterval) {
- super();
- action && this.initWithAction(action);
- }
-
- /*
- * @param {ActionInterval} action
- * @return {Boolean}
- */
- initWithAction (action: ActionInterval): boolean {
- if (!action) {
- errorID(1026);
+ private _checkTouchMapSizeMoreThanMax (touchID: number): boolean {
+ if (this._touchMap.has(touchID)) {
return false;
}
-
- this._innerAction = action;
- return true;
- }
-
- clone (): RepeatForever {
- const action = new RepeatForever();
- this._cloneDecoration(action);
- action.initWithAction(this._innerAction!.clone());
- return action;
- }
-
- startWithTarget (target: any): void {
- ActionInterval.prototype.startWithTarget.call(this, target);
- this._innerAction!.startWithTarget(target);
- }
-
- step (dt: any): void {
- const locInnerAction = this._innerAction!;
- locInnerAction.step(dt);
- if (locInnerAction.isDone()) {
- // var diff = locInnerAction.getElapsed() - locInnerAction._duration;
- locInnerAction.startWithTarget(this.target);
- // to prevent jerk. issue #390 ,1247
- // this._innerAction.step(0);
- // this._innerAction.step(diff);
- locInnerAction.step(locInnerAction.getElapsed() - locInnerAction._duration);
- }
- }
-
- isDone (): boolean {
- return false;
- }
-
- reverse (): any {
- const action = new RepeatForever(this._innerAction!.reverse());
- this._cloneDecoration(action);
- this._reverseEaseList(action);
- return action as any;
- }
-
- /*
- * Set inner action.
- * @param {ActionInterval} action
- */
- setInnerAction (action: any): void {
- if (this._innerAction !== action) {
- this._innerAction = action;
- }
- }
-
- /*
- * Get inner action.
- * @return {ActionInterval}
- */
- getInnerAction (): ActionInstant | null {
- return this._innerAction;
- }
-}
-
-/**
- * @en Create a acton which repeat forever, as it runs forever, it can't be added into `sequence` and `spawn`.
- * @zh 永远地重复一个动作,有限次数内重复一个动作请使用 repeat 动作,由于这个动作不会停止,所以不能被添加到 `sequence` 或 `spawn` 中。
- * @method repeatForever
- * @param {FiniteTimeAction} action
- * @return {ActionInterval}
- * @example
- * import { repeatForever, rotateBy } from 'cc';
- * var repeat = repeatForever(rotateBy(1.0, 360));
- */
-export function repeatForever (action?: ActionInterval): ActionInterval {
- return new RepeatForever(action);
-}
-
-/*
- * Spawn a new action immediately
- * @class Spawn
- * @extends ActionInterval
- */
-export class Spawn extends ActionInterval {
- static _actionOneTwo = function (action1: any, action2: any): Spawn {
- const pSpawn = new Spawn();
- pSpawn.initWithTwoActions(action1, action2);
- return pSpawn;
- }
-
- private _one: ActionInterval | null = null;
- private _two: ActionInterval | null = null;
-
- constructor (tempArray?: any) {
- super();
-
- const paramArray = (tempArray instanceof Array) ? tempArray : arguments;
- if (paramArray.length === 1) {
- errorID(1020);
- return;
- }
- const last = paramArray.length - 1;
- if ((last >= 0) && (paramArray[last] == null)) logID(1015);
-
- if (last >= 0) {
- let prev = paramArray[0]; let action1: any;
- for (let i = 1; i < last; i++) {
- if (paramArray[i]) {
- action1 = prev;
- prev = Spawn._actionOneTwo(action1, paramArray[i]);
- }
- }
- this.initWithTwoActions(prev, paramArray[last]);
- }
- }
-
- /* initializes the Spawn action with the 2 actions to spawn
- * @param {FiniteTimeAction} action1
- * @param {FiniteTimeAction} action2
- * @return {Boolean}
- */
- initWithTwoActions (action1: any, action2: any): boolean {
- if (!action1 || !action2) {
- errorID(1027);
+ const maxSize = macro.ENABLE_MULTI_TOUCH ? this._maxTouches : 1;
+ if (this._touchMap.size < maxSize) {
return false;
}
-
- let ret = false;
-
- const d1 = action1._duration;
- const d2 = action2._duration;
-
- if (this.initWithDuration(Math.max(d1, d2))) {
- this._one = action1;
- this._two = action2;
-
- if (d1 > d2) {
- this._two = Sequence._actionOneTwo(action2, delayTime(d1 - d2));
- } else if (d1 < d2) {
- this._one = Sequence._actionOneTwo(action1, delayTime(d2 - d1));
+ // Handle when exceed the max number of touches
+ const now = performance.now();
+ this._touchMap.forEach((touch) => {
+ if (now - touch.lastModified > macro.TOUCH_TIMEOUT) {
+ log(`The touches is more than MAX_TOUCHES, release touch id ${touch.getID()}.`);
+ // TODO: need to handle touch cancel event when exceed the max number of touches ?
+ this.releaseTouch(touch.getID());
}
-
- ret = true;
- }
- return ret;
- }
-
- clone (): Spawn {
- const action = new Spawn();
- this._cloneDecoration(action);
- action.initWithTwoActions(this._one!.clone(), this._two!.clone());
- return action;
- }
-
- startWithTarget (target: any): void {
- ActionInterval.prototype.startWithTarget.call(this, target);
- this._one!.startWithTarget(target);
- this._two!.startWithTarget(target);
- }
-
- stop (): void {
- this._one!.stop();
- this._two!.stop();
- Action.prototype.stop.call(this);
- }
-
- update (dt: any): void {
- dt = this._computeEaseTime(dt);
- if (this._one) this._one.update(dt);
- if (this._two) this._two.update(dt);
- }
-
- reverse (): any {
- const action = Spawn._actionOneTwo(this._one!.reverse(), this._two!.reverse());
- this._cloneDecoration(action);
- this._reverseEaseList(action);
- return action as any;
- }
-}
-
-/**
- * @en Create a spawn action which runs several actions in parallel.
- * @zh 同步执行动作,同步执行一组动作。
- * @method spawn
- * @param {FiniteTimeAction|FiniteTimeAction[]} actionOrActionArray
- * @param {FiniteTimeAction} ...tempArray
- * @return {FiniteTimeAction}
- * @example
- * import { spawn, jumpBy, rotateBy, Vec2 } from 'cc';
- * const action = spawn(jumpBy(2, new Vec2(300, 0), 50, 4), rotateBy(2, 720));
- * todo: It should be the direct use new
- */
-export function spawn (/* Multiple Arguments */tempArray: any): FiniteTimeAction {
- const paramArray = (tempArray instanceof Array) ? tempArray : arguments;
- if (paramArray.length === 1) {
- errorID(1020);
- return null as any;
- }
- if ((paramArray.length > 0) && (paramArray[paramArray.length - 1] == null)) logID(1015);
-
- let prev = paramArray[0];
- for (let i = 1; i < paramArray.length; i++) {
- if (paramArray[i] != null) prev = Spawn._actionOneTwo(prev, paramArray[i]);
- }
- return prev;
-}
-
-/* Delays the action a certain amount of seconds
- * @class DelayTime
- * @extends ActionInterval
- */
-class DelayTime extends ActionInterval {
- update (dt: any): void { }
-
- reverse (): any {
- const action = new DelayTime(this._duration);
- this._cloneDecoration(action);
- this._reverseEaseList(action);
- return action as any;
- }
-
- clone (): DelayTime {
- const action = new DelayTime();
- this._cloneDecoration(action);
- action.initWithDuration(this._duration);
- return action;
- }
-}
-
-/**
- * @en Delays the action a certain amount of seconds.
- * @zh 延迟指定的时间量。
- * @method delayTime
- * @param {Number} d duration in seconds
- * @return {ActionInterval}
- * @example
- * import { delayTime } from 'cc';
- * const delay = delayTime(1);
- */
-export function delayTime (d: number): ActionInterval {
- return new DelayTime(d);
-}
-
-/**
- *
- * Executes an action in reverse order, from time=duration to time=0
- * @warning Use this action carefully. This action is not sequenceable.
- * Use it as the default "reversed" method of your own actions, but using it outside the "reversed"
- * scope is not recommended.
- *
- * @class ReverseTime
- * @extends ActionInterval
- * @param {FiniteTimeAction} action
- * @example
- * import ReverseTime from 'cc';
- * var reverse = new ReverseTime(this);
- */
-export class ReverseTime extends ActionInterval {
- private _other: ActionInterval | null = null;
-
- constructor (action?: any) {
- super();
- action && this.initWithAction(action);
- }
-
- /*
- * @param {FiniteTimeAction} action
- * @return {Boolean}
- */
- initWithAction (action: ActionInterval): boolean {
- if (!action) {
- errorID(1028);
- return false;
- }
- if (action === this._other) {
- errorID(1029);
- return false;
- }
-
- if (ActionInterval.prototype.initWithDuration.call(this, action._duration)) {
- // Don't leak if action is reused
- this._other = action;
- return true;
- }
- return false;
- }
-
- clone (): ReverseTime {
- const action = new ReverseTime();
- this._cloneDecoration(action);
- action.initWithAction(this._other!.clone());
- return action;
- }
-
- startWithTarget (target: any): void {
- ActionInterval.prototype.startWithTarget.call(this, target);
- this._other!.startWithTarget(target);
- }
-
- update (dt: number): void {
- dt = this._computeEaseTime(dt);
- if (this._other) this._other.update(1 - dt);
- }
-
- reverse (): any {
- return this._other!.clone() as any;
- }
-
- stop (): void {
- this._other!.stop();
- Action.prototype.stop.call(this);
+ });
+ return maxSize >= this._touchMap.size;
}
}
-/**
- * @en Executes an action in reverse order, from time=duration to time=0.
- * @zh 反转目标动作的时间轴。
- * @method reverseTime
- * @param {FiniteTimeAction} action
- * @return {ActionInterval}
- * @example
- * import { reverseTime } from 'cc';
- * const reverse = reverseTime(this);
- */
-export function reverseTime (action: any): ActionInterval {
- return new ReverseTime(action);
-}
+export const touchManager = new TouchManager();
diff --git a/cocos/tween/tween.ts b/cocos/tween/tween.ts
index 3beb6574466..9dd3ad7409c 100644
--- a/cocos/tween/tween.ts
+++ b/cocos/tween/tween.ts
@@ -22,15 +22,15 @@
THE SOFTWARE.
*/
-import { TweenSystem } from './tween-system';
import { warn } from '../core';
-import { ActionInterval, sequence, repeat, repeatForever, reverseTime, delayTime, spawn } from './actions/action-interval';
-import { removeSelf, show, hide, callFunc } from './actions/action-instant';
+import { legacyCC } from '../core/global-exports';
import { Action, FiniteTimeAction } from './actions/action';
+import { callFunc, hide, removeSelf, show } from './actions/action-instant';
+import { ActionInterval, delayTime, repeat, repeatForever, reverseTime, sequence, spawn } from './actions/action-interval';
import { ITweenOption } from './export-api';
-import { TweenAction } from './tween-action';
import { SetAction } from './set-action';
-import { legacyCC } from '../core/global-exports';
+import { TweenAction } from './tween-action';
+import { TweenSystem } from './tween-system';
// https://medium.com/dailyjs/typescript-create-a-condition-based-subset-types-9d902cea5b8c
type FlagExcludedType = { [Key in keyof Base]: Base[Key] extends Type ? never : Key };
@@ -60,6 +60,31 @@ export class Tween {
private _target: T | null = null;
private _tag = Action.TAG_INVALID;
+ // for time scale
+ private _timeScale = 1;
+
+ /**
+ * !#en set/get time scale for tween
+ * !#zh 设置/读取 tween 的 time scale (时间缩放参数)
+ * @property timeScale
+ * @type {Number}
+ * @default 1
+ */
+ get timeScale (): number {
+ return this._timeScale;
+ }
+
+ set timeScale (value: number) {
+ if (value < 0) {
+ warn('`timeScale` cannot be less than 0.');
+ value = 1;
+ }
+ this._timeScale = value;
+ if (this._finalAction) {
+ (this._finalAction as ActionInterval).timeScale = value;
+ }
+ }
+
constructor (target?: T | null) {
this._target = target === undefined ? null : target;
}
@@ -138,6 +163,38 @@ export class Tween {
return this;
}
+ /**
+ * !#en
+ * Pause this tween
+ * !#zh
+ * 暂停当前 tween
+ * @method pause
+ * @return {Tween}
+ * @typescript pause(): Tween
+ */
+ pause (): Tween {
+ if (this._finalAction) {
+ (this._finalAction as ActionInterval).paused = true;
+ }
+ return this;
+ }
+
+ /**
+ * !#en
+ * Resume this tween
+ * !#zh
+ * 从暂停状态恢复当前 tween
+ * @method resume
+ * @return {Tween}
+ * @typescript resume(): Tween
+ */
+ resume (): Tween {
+ if (this._finalAction) {
+ (this._finalAction as ActionInterval).paused = false;
+ }
+ return this;
+ }
+
/**
* @en
* Clone a tween.
@@ -261,7 +318,7 @@ export class Tween {
*/
sequence (...args: Tween[]): Tween {
const action = Tween._wrappedSequence(...args);
- this._actions.push(action);
+ this._actions.push(action as Action);
return this;
}
@@ -275,7 +332,7 @@ export class Tween {
*/
parallel (...args: Tween[]): Tween {
const action = Tween._wrappedParallel(...args);
- this._actions.push(action);
+ this._actions.push(action as Action);
return this;
}
@@ -437,7 +494,7 @@ export class Tween {
if (actions.length === 1) {
action = actions[0];
} else {
- action = sequence(actions);
+ action = sequence(actions) as Action;
}
return action;
@@ -449,7 +506,7 @@ export class Tween {
private static readonly _tmp_args: Tween[] | Action[] = [];
- private static _wrappedSequence (...args: Action[] | Tween[]): ActionInterval {
+ private static _wrappedSequence (...args: Action[] | Tween[]): ActionInterval | null {
const tmp_args = Tween._tmp_args;
tmp_args.length = 0;
for (let l = args.length, i = 0; i < l; i++) {
@@ -459,10 +516,10 @@ export class Tween {
}
}
- return sequence.apply(sequence, tmp_args as any);
+ return sequence(tmp_args as Action[]);
}
- private static _wrappedParallel (...args: Action[] | Tween[]): FiniteTimeAction {
+ private static _wrappedParallel (...args: Action[] | Tween[]): FiniteTimeAction | null {
const tmp_args = Tween._tmp_args;
tmp_args.length = 0;
for (let l = args.length, i = 0; i < l; i++) {
diff --git a/tests/tween/tween.test.ts b/tests/tween/tween.test.ts
index 32e90d0048a..4b3146c42aa 100644
--- a/tests/tween/tween.test.ts
+++ b/tests/tween/tween.test.ts
@@ -4,6 +4,36 @@ import { Node, Scene } from "../../cocos/scene-graph";
import { Component } from "../../cocos/scene-graph/component";
import { game, director } from "../../cocos/game";
+test('Start pause and resume a tween action', function () {
+ const scene = new Scene('test-play');
+ const node = new Node();
+ scene.addChild(node);
+
+ const sys = new TweenSystem();
+ (TweenSystem.instance as any) = sys;
+ director.registerSystem(TweenSystem.ID, sys, System.Priority.MEDIUM);
+ director.runSceneImmediate(scene);
+
+ let action = tween(node).to(4, { scale : new Vec3(5, 5, 5) }).start();
+ director.tick(0);
+ director.tick(1);
+ expect(node.scale.equals3f(2, 2, 2)).toBeTruthy();
+
+ action.pause();
+ director.tick(2);
+ expect(node.scale.equals3f(2, 2, 2)).toBeTruthy();
+ action.resume();
+ director.tick(1);
+ expect(node.scale.equals3f(3, 3, 3)).toBeTruthy();
+
+ action.timeScale = 0.5;
+ director.tick(1);
+ expect(node.scale.equals3f(3.5, 3.5, 3.5)).toBeTruthy();
+ action.stop();
+ director.tick(2);
+ expect(node.scale.equals3f(3.5, 3.5, 3.5)).toBeTruthy();
+});
+
test('remove actions by tag', function () {
const scene = new Scene('test-tags');
const node = new Node();