Files
esengine/source/src/Tween/Tween.ts
2021-07-03 12:27:21 +08:00

319 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
module es {
export enum LoopType {
none,
restartFromBeginning,
pingpong
}
export enum TweenState {
running,
paused,
complete
}
export abstract class Tween<T> implements ITweenable, ITween<T> {
protected _target: ITweenTarget<T>;
protected _isFromValueOverridden: boolean;
protected _fromValue: T;
protected _toValue: T;
protected _easeType: EaseType;
protected _shouldRecycleTween: boolean = true;
protected _isRelative: boolean;
protected _completionHandler: (tween: ITween<T>) => void;
protected _loopCompleteHandler: (tween: ITween<T>) => void;
protected _nextTween: ITweenable;
protected _tweenState: TweenState = TweenState.complete;
private _isTimeScaleIndependent: boolean;
protected _delay: number;
protected _duration: number;
protected _timeScale: number = 1;
protected _elapsedTime: number;
protected _loopType: LoopType;
protected _loops: number;
protected _delayBetweenLoops: number;
private _isRunningInReverse: boolean;
public context: any;
public setEaseType(easeType: EaseType): ITween<T> {
this._easeType = easeType;
return this;
}
public setDelay(delay: number): ITween<T> {
this._delay = delay;
this._elapsedTime = -this._delay;
return this;
}
public setDuration(duration: number): ITween<T> {
this._duration = duration;
return this;
}
public setTimeScale(timeSclae: number): ITween<T> {
this._timeScale = timeSclae;
return this;
}
public setIsTimeScaleIndependent(): ITween<T> {
this._isTimeScaleIndependent = true;
return this;
}
public setCompletionHandler(completeHandler: (tween: ITween<T>) => void): ITween<T> {
this._completionHandler = completeHandler;
return this;
}
public setLoops(loopType: LoopType, loops: number = 1, delayBetweenLoops: number = 0): ITween<T> {
this._loopType = loopType;
this._delayBetweenLoops = delayBetweenLoops;
if (loops < 0)
loops = -1;
if (loopType == LoopType.pingpong)
loops = loops * 2;
this._loops = loops;
return this;
}
public setLoopCompletionHanlder(loopCompleteHandler: (tween: ITween<T>) => void): ITween<T> {
this._loopCompleteHandler = loopCompleteHandler;
return this;
}
public setFrom(from: T): ITween<T> {
this._isFromValueOverridden = true;
this._fromValue = from;
return this;
}
public prepareForReuse(from: T, to: T, duration: number): ITween<T> {
this.initialize(this._target, to, duration);
return this;
}
public setRecycleTween(shouldRecycleTween: boolean): ITween<T> {
this._shouldRecycleTween = shouldRecycleTween;
return this;
}
public abstract setIsRelative(): ITween<T>;
public setContext(context): ITween<T> {
this.context = context;
return this;
}
public setNextTween(nextTween: ITweenable): ITween<T> {
this._nextTween = nextTween;
return this;
}
public tick(): boolean {
if (this._tweenState == TweenState.paused)
return false;
// 当我们进行循环时我们会在0和持续时间之间限制数值
let elapsedTimeExcess = 0;
if (!this._isRunningInReverse && this._elapsedTime >= this._duration) {
elapsedTimeExcess = this._elapsedTime - this._duration;
this._elapsedTime = this._duration;
this._tweenState = TweenState.complete;
} else if (this._isRunningInReverse && this._elapsedTime <= 0) {
elapsedTimeExcess = 0 - this._elapsedTime;
this._elapsedTime = 0;
this._tweenState = TweenState.complete;
}
// 当我们延迟开始tween的时候经过的时间会是负数所以不要更新这个值。
if (this._elapsedTime >= 0 && this._elapsedTime <= this._duration) {
this.updateValue();
}
// 如果我们有一个loopType并且我们是Complete意味着我们达到了0或持续时间处理循环。
// handleLooping将采取任何多余的elapsedTime并将其因子化并在必要时调用udpateValue来保持tween的完美准确性
if (this._loopType != LoopType.none && this._tweenState == TweenState.complete && this._loops != 0) {
this.handleLooping(elapsedTimeExcess);
}
let deltaTime = this._isTimeScaleIndependent ? Time.unscaledDeltaTime : Time.deltaTime;
deltaTime *= this._timeScale;
// 我们需要减去deltaTime
if (this._isRunningInReverse)
this._elapsedTime -= deltaTime;
else
this._elapsedTime += deltaTime;
if (this._tweenState == TweenState.complete) {
this._completionHandler && this._completionHandler(this);
// 如果我们有一个nextTween把它添加到TweenManager中这样它就可以开始运行了
if (this._nextTween != null) {
this._nextTween.start();
this._nextTween = null;
}
return true;
}
return false;
}
public recycleSelf() {
if (this._shouldRecycleTween) {
this._target = null;
this._nextTween = null;
}
}
public isRunning(): boolean {
return this._tweenState == TweenState.running;
}
public start() {
if (!this._isFromValueOverridden)
this._fromValue = this._target.getTargetObject();
if (this._tweenState == TweenState.complete) {
this._tweenState = TweenState.running;
TweenManager.addTween(this);
}
}
public pause() {
this._tweenState = TweenState.paused;
}
public resume() {
this._tweenState = TweenState.running;
}
public stop(bringToCompletion: boolean = false) {
this._tweenState = TweenState.complete;
if (bringToCompletion) {
// 如果我们逆向运行我们在0处结束否则我们进入持续时间
this._elapsedTime = this._isRunningInReverse ? 0 : this._duration;
this._loopType = LoopType.none;
this._loops = 0;
// TweenManager将在下一个tick上进行删除处理
} else {
TweenManager.removeTween(this);
}
}
public jumpToElapsedTime(elapsedTime) {
this._elapsedTime = MathHelper.clamp(elapsedTime, 0, this._duration);
this.updateValue();
}
/**
* 反转当前的tween如果是向前走就会向后走反之亦然
*/
public reverseTween() {
this._isRunningInReverse = !this._isRunningInReverse;
}
/**
* 当通过StartCoroutine调用时这将一直持续到tween完成
*/
public * waitForCompletion() {
while (this._tweenState != TweenState.complete)
yield null;
}
public getTargetObject() {
return this._target.getTargetObject();
}
private resetState() {
this.context = null;
this._completionHandler = this._loopCompleteHandler = null;
this._isFromValueOverridden = false;
this._isTimeScaleIndependent = false;
this._tweenState = TweenState.complete;
// TODO: 我认为在没有得到用户同意的情况下我们绝对不应该从_shouldRecycleTween=false。需要研究和思考
// this._shouldRecycleTween = true;
this._isRelative = false;
this._easeType = TweenManager.defaultEaseType;
if (this._nextTween != null) {
this._nextTween.recycleSelf();
this._nextTween = null;
}
this._delay = 0;
this._duration = 0;
this._timeScale = 1;
this._elapsedTime = 0;
this._loopType = LoopType.none;
this._delayBetweenLoops = 0;
this._loops = 0;
this._isRunningInReverse = false;
}
/**
* 将所有状态重置为默认值,并根据传入的参数设置初始状态。
* 这个方法作为一个切入点这样Tween子类就可以调用它这样tweens就可以被回收。
* 当回收时,构造函数不会再被调用,所以这个方法封装了构造函数要做的事情
* @param target
* @param to
* @param duration
*/
public initialize(target: ITweenTarget<T>, to: T, duration: number) {
// 重置状态,以防我们被回收
this.resetState();
this._target = target;
this._toValue = to;
this._duration = duration;
}
/**
* 处理循环逻辑
* @param elapsedTimeExcess
*/
private handleLooping(elapsedTimeExcess: number) {
this._loops--;
if (this._loopType == LoopType.pingpong) {
this.reverseTween();
}
if (this._loopType == LoopType.restartFromBeginning || this._loops % 2 == 0) {
this._loopCompleteHandler && this._completionHandler(this);
}
// 如果我们还有循环要处理就把我们的状态重置为Running这样我们就可以继续处理它们了
if (this._loops != 0) {
this._tweenState = TweenState.running;
// 现在我们需要设置我们的经过时间并考虑到我们的elapsedTimeExcess
if (this._loopType == LoopType.restartFromBeginning) {
this._elapsedTime = elapsedTimeExcess - this._delayBetweenLoops;
} else {
if (this._isRunningInReverse)
this._elapsedTime += this._delayBetweenLoops - elapsedTimeExcess;
else
this._elapsedTime = elapsedTimeExcess - this._delayBetweenLoops;
}
// 如果我们有一个elapsedTimeExcess并且没有delayBetweenLoops则更新该值
if (this._delayBetweenLoops == 0 && elapsedTimeExcess > 0) {
this.updateValue();
}
}
}
protected abstract updateValue();
}
}