DOTween源码学习笔记
前言
最近抽时间学习了一下DOTween的源码,发现实现方式非常的巧妙。
在此记录一下学习笔记。
核心思想
为什么DOTween看起来那么复杂呢?
因为DOTween提供的功能特别多,包括变化队列Sequence、运动轨迹Path、链式编程等等。非常强大的插件。但本质上的核心变化思想就一点:
每帧根据时间位置来算出对应ease上的实际变化状态(或者叫变化完成度,范围从0到1),根据这个变化状态设置变化对象的属性(包括位置、颜色、旋转、数值等)。
比较
曾经有人做过各种Tween插件的对比,这是链接。
总的来说:
性能:LeanTween ≈ DOTween > iTween > HOTween
使用性:DOTween > LeanTween > iTween > HOTween
普及率:iTween > LeanTween > HOTween > DOTween
这里主要看DOTween和iTween性能比较,为什么DOTween的性能会相较而言更好呢?
具体原因需要待我看完iTween源码后再分析。(挖坑)
但就Update一点来说,DOTween是一个MonoBehavior统一管理所有变化,而iTween是每个变化都创建一个MonoBehavior自己管理自己。
TweenManager
Update源码加注释:
internal static void Update(UpdateType updateType, float deltaTime, float independentTime)
{
if (TweenManager._requiresActiveReorganization) // 如果需要重新组织激活的变化
TweenManager.ReorganizeActiveTweens(); // 把缓存激活变化的数组进行调整, 清除里面的null对象
TweenManager.isUpdateLoop = true;
bool flag1 = false;
int num1 = TweenManager._maxActiveLookupId + 1; // 最大激活的查找ID
for (int index = 0; index < num1; ++index)
{
Tween activeTween = TweenManager._activeTweens[index]; // 遍历每一个激活的变化
if (activeTween != null && activeTween.updateType == updateType) // 判空并且是这种update类型
{
if (TweenManager._totTweenLinks > 0) // 如果这个变化有连接obj的行为, 比如: 在obj OnDisable的时候暂停、OnEnable的时候开始
TweenManager.EvaluateTweenLink(activeTween); // 根据obj的情况调用变化开始、暂停等方法
if (!activeTween.active) // 如果这个变化没激活
{
flag1 = true; // 暂记
TweenManager.MarkForKilling(activeTween); // 则说明这个变化需要被关闭了, 这里变化被标记, 等待被Kill
}
else if (activeTween.isPlaying) // 如果这个变化正在播放
{
activeTween.creationLocked = true;
float num2 = (activeTween.isIndependentUpdate ? independentTime : deltaTime) * activeTween.timeScale;
// 计算时间增量
// 如果需要则独立计算时间增量, 则不用deltaTime
if ((double) num2 >= 9.99999997475243E-07 || (double) num2 <= -9.99999997475243E-07) // 如果这个时间增量很大或很小???
{
// --- 用于检测是否需要调用OnPlay回调 ---
if (!activeTween.delayComplete) // 如果这个变化没有延迟完成
{
num2 = activeTween.UpdateDelay(activeTween.elapsedDelay + num2); // 修改时间增量???
if ((double) num2 <= -1.0) // 如果时间增量小于等于-1
{
flag1 = true; // 暂记
TweenManager.MarkForKilling(activeTween); // 则说明这个变化需要被关闭了, 这里变化被标记, 等待被Kill
continue;
}
if ((double) num2 > 0.0) // 如果时间增量大于0
{
if (activeTween.playedOnce && activeTween.onPlay != null) // 如果这个变化只播放一次并且有OnPlay回调
Tween.OnTweenCallback(activeTween.onPlay); // 调用OnPlay回调
}
else // 如果时间增量在-1~0之间
continue;
}
// -----------------------------------
if (!activeTween.startupDone && !activeTween.Startup()) // 如果这个变化还没有开始
{
flag1 = true; // 暂记
TweenManager.MarkForKilling(activeTween); // 则说明这个变化需要被关闭了, 这里变化被标记, 等待被Kill
}
else
{
float position = activeTween.position; // 这一圈的时间位置
bool flag2 = (double) position >= (double) activeTween.duration; // 一开始时间位置是否大于等于持续时间
int toCompletedLoops = activeTween.completedLoops; // 完成圈数
float toPosition;
if ((double) activeTween.duration <= 0.0) // 如果这个变化没有持续时间
{
toPosition = 0.0f; // 设置下一帧的时间位置为0
toCompletedLoops = activeTween.loops == -1 ? activeTween.completedLoops + 1 : activeTween.loops; // 设置下一帧的完成圈数
}
else // 如果这个变化有持续时间
{
if (activeTween.isBackwards) // 如果这个变化是倒放
{
// 初始设置下一帧的时间位置 = 这一圈的时间位置 - 时间增量
// 循环设置下一帧的时间位置 = 下一帧的时间位置 + 变化的持续时间
// 循环设置下一帧的圈数 = 下一帧的圈数 - 1
// 直到下一帧的时间位置大于等于0或者下一帧的完成圈数小于0为止
for (toPosition = position - num2; (double) toPosition < 0.0 && toCompletedLoops > -1; --toCompletedLoops)
toPosition += activeTween.duration;
if (toCompletedLoops < 0 || flag2 && toCompletedLoops < 1) // 如果下一帧完成圈数小于0, 或者一开始时间位置是否大于等于持续时间并且一帧完成圈数小于等于0
{
toPosition = 0.0f; // 设置下一帧的时间位置为0
toCompletedLoops = flag2 ? 1 : 0; // 设置下一帧的完成圈数
}
}
else // 如果这个变化是正放
{
// 初始设置下一帧的时间位置 = 这一圈的时间位置 + 时间增量
// 循环设置下一帧的时间位置 = 下一帧的时间位置 - 变化的持续时间
// 循环设置下一帧的圈数 = 下一帧的圈数 + 1
// 直到下一帧的时间位置小于持续时间或者下一帧的完成圈数大于等于目标圈数为止
for (toPosition = position + num2; (double) toPosition >= (double) activeTween.duration && (activeTween.loops == -1 || toCompletedLoops < activeTween.loops); ++toCompletedLoops)
toPosition -= activeTween.duration;
}
if (flag2) // 如果一开始时间位置是否大于等于持续时间
--toCompletedLoops; // 下一帧的圈数 - 1
if (activeTween.loops != -1 && toCompletedLoops >= activeTween.loops) // 如果下一帧的完成圈数大于等于目标圈数
toPosition = activeTween.duration; // 设置下一帧的时间位置 = 持续时间
}
if (Tween.DoGoto(activeTween, toPosition, toCompletedLoops, UpdateMode.Update)) // 把上述参数传入, 进行调整变化
{
// 如果调整变化失败
flag1 = true; // 暂记
TweenManager.MarkForKilling(activeTween); // 则说明这个变化需要被关闭了, 这里变化被标记, 等待被Kill
}
}
}
}
}
}
if (flag1) // 如果有某个变化需要被Kill
{
if (TweenManager._despawnAllCalledFromUpdateLoopCallback)
TweenManager._despawnAllCalledFromUpdateLoopCallback = false;
else
TweenManager.DespawnActiveTweens(TweenManager._KillList);
TweenManager._KillList.Clear();
}
TweenManager.isUpdateLoop = false;
}
ABSSequentiable
一个变化的基本抽象类。
Tween
继承ABSSequentiable,是一个变化的基本类。
customEase——委托,根据这个来算出实际的变化信息。
DoGoto源码加注释:
internal static bool DoGoto(
Tween t,
float toPosition, // 下一帧时间位置
int toCompletedLoops, // 下一帧完成圈数
UpdateMode updateMode)
{
if (!t.startupDone && !t.Startup()) // 如果没开始
return true; // 有问题, 需要被kill
if (!t.playedOnce && updateMode == UpdateMode.Update) // 如果不止播放一次并且是Update类型的
{
t.playedOnce = true; // 改为播放一次
if (t.onStart != null) // 如果有开始回调
{
Tween.OnTweenCallback(t.onStart); // 调用开始回调
if (!t.active) // 如果没有激活
return true; // 有问题, 需要被kill
}
if (t.onPlay != null) // 如果有播放回调
{
Tween.OnTweenCallback(t.onPlay); // 调用播放回调
if (!t.active) // 如果没有激活
return true; // 有问题, 需要被kill
}
}
float position = t.position; // 记录当前时间位置
int completedLoops = t.completedLoops; // 记录当前完成圈数
t.completedLoops = toCompletedLoops; // 设置完成圈数 = 下一帧完成圈数
bool flag = (double) t.position <= 0.0 && completedLoops <= 0; // 记录是否当前时间小于等于0并且当前完成圈数小于等于0
bool isComplete = t.isComplete; // 记录是否已经完成
if (t.loops != -1) // 如果目标圈数不为-1
t.isComplete = t.completedLoops == t.loops; // 判断当前是否已经达成圈数目标, 并设置完成情况
int newCompletedSteps = 0; // 记录完成阶段
if (updateMode == UpdateMode.Update) // 如果是Update类型
{
if (t.isBackwards) // 如果是倒放
{
newCompletedSteps = t.completedLoops < completedLoops ? completedLoops - t.completedLoops : ((double) toPosition > 0.0 || flag ? 0 : 1); // 设置完成阶段 = 这一帧完成圈数和下一帧完成圈数之差
if (isComplete) // 如果已经完成目标圈数
--newCompletedSteps; // 完成步数 - 1
}
else // 如果是正放
newCompletedSteps = t.completedLoops > completedLoops ? t.completedLoops - completedLoops : 0; // 设置完成阶段 = 这一帧完成圈数和下一帧完成圈数之差
}
else if (t.tweenType == TweenType.Sequence) // 如果是Sequence类型
{
newCompletedSteps = completedLoops - toCompletedLoops; // 完成阶段 = 当前完成圈数 - 下一帧完成圈数
if (newCompletedSteps < 0)
newCompletedSteps = -newCompletedSteps; // 完成阶段非负
}
t.position = toPosition; // 设置时间位置 = 下一帧时间位置
if ((double) t.position > (double) t.duration) // 如果时间位置超过了持续时间
t.position = t.duration; // 设置时间位置 = 持续时间
else if ((double) t.position <= 0.0) // 如果时间位置小于等于0
t.position = t.completedLoops > 0 || t.isComplete ? t.duration : 0.0f; // 设置时间位置 = 持续时间或0
bool isPlaying = t.isPlaying; // 记录是否正在播放
if (t.isPlaying) // 如果正在播放
t.isPlaying = t.isBackwards ? t.completedLoops != 0 || (double) t.position > 0.0 : !t.isComplete; // 判断是否还需要播放
bool useInversePosition = t.loopType == LoopType.Yoyo && ((double) t.position < (double) t.duration ? (uint) (t.completedLoops % 2) > 0U : t.completedLoops % 2 == 0); // 记录是否用逆时间位置
UpdateNotice updateNotice = (flag ? 0 : (t.loopType != LoopType.Restart || t.completedLoops == completedLoops || t.loops != -1 && t.completedLoops >= t.loops ? ((double) t.position > 0.0 ? 0 : (t.completedLoops <= 0 ? 1 : 0)) : 1)) != 0 ? UpdateNotice.RewindStep : UpdateNotice.None; // 设置UpdateNotice
if (t.ApplyTween(position, completedLoops, newCompletedSteps, useInversePosition, updateMode, updateNotice)) // *** 进行变化! ***
return true; // 有问题, 需要被kill
if (t.onUpdate != null && updateMode != UpdateMode.IgnoreOnUpdate) // 如果有更新回调
Tween.OnTweenCallback(t.onUpdate); // 调用更新回调
if ((double) t.position <= 0.0 && t.completedLoops <= 0 && (!flag && t.onRewind != null)) // 如果有倒带回调
Tween.OnTweenCallback(t.onRewind); // 调用倒带回调
if (newCompletedSteps > 0 && updateMode == UpdateMode.Update && t.onStepComplete != null) // 如果有阶段完成回调
{
for (int index = 0; index < newCompletedSteps; ++index)
Tween.OnTweenCallback(t.onStepComplete); // 调用阶段完成回调
}
if (t.isComplete && !isComplete && (updateMode != UpdateMode.IgnoreOnComplete && t.onComplete != null)) // 如果有完成回调
Tween.OnTweenCallback(t.onComplete); // 调用完成回调
if (!t.isPlaying & isPlaying && (!t.isComplete || !t.autoKill) && t.onPause != null) // 如果有暂停回调
Tween.OnTweenCallback(t.onPause); // 调用暂停回调
return t.autoKill && t.isComplete; // 判断是否需要被kill
}
Tweener
继承Tween。
TweenerCore
继承Tweener,是变换的核心。
tweenPlugin——是什么变换,最终会调用里面的接口
ApplyTween源码加注释:
internal override bool ApplyTween(
float prevPosition, // 变化前时间位置
int prevCompletedLoops, // 变化前完成圈数
int newCompletedSteps, // 新完成阶段数
bool useInversePosition, // 是否用逆时间位置
UpdateMode updateMode, // 更新模式
UpdateNotice updateNotice)
{
float elapsed = useInversePosition ? this.duration - this.position : this.position; // 计算逝去时间, 用它来通过ease计算实际的数据
if (DOTween.useSafeMode) // 安全模式使用try-catch
{
try
{
this.tweenPlugin.EvaluateAndApply(this.plugOptions, (Tween) this, this.isRelative, this.getter, this.setter, elapsed, this.startValue, this.changeValue, this.duration, useInversePosition, updateNotice);
}
catch
{
return true;
}
}
else // 不安全模式直接调
this.tweenPlugin.EvaluateAndApply(this.plugOptions, (Tween) this, this.isRelative, this.getter, this.setter, elapsed, this.startValue, this.changeValue, this.duration, useInversePosition, updateNotice);
return false;
}
ABSTweenPlugin
EvaluateAndApply() ——最终改变物体的函数,这一层是abstract的。
Color2Plugin/RectPlugin/DoublePlugin/QuaternionPlugin…
继承ABSTweenPlugin。
EvaluateAndApply() ——最终改变物体的函数,里面根据EaseManager.Evaluate()方法计算当前时间位置所对应的数值,从而改变物体。
EaseManager
Evaluate源码加注释:
public static float Evaluate(
Ease easeType, // 官方的ease类型
EaseFunction customEase, // 自定义ease类型的计算方法
float time, // 当前时间/逝去时间
float duration, // 持续时间
float overshootOrAmplitude,
float period)
{
switch (easeType) // 具体类型自行百度
{
case Ease.Linear: // 线性
return time / duration;
case Ease.InSine:
return (float) (-Math.Cos((double) time / (double) duration * 1.57079637050629) + 1.0);
case Ease.OutSine:
return (float) Math.Sin((double) time / (double) duration * 1.57079637050629);
case Ease.InOutSine:
return (float) (-0.5 * (Math.Cos(3.14159274101257 * (double) time / (double) duration) - 1.0));
case Ease.InQuad:
return (time /= duration) * time;
case Ease.OutQuad:
return (float) (-(double) (time /= duration) * ((double) time - 2.0));
case Ease.InOutQuad:
return (double) (time /= duration * 0.5f) < 1.0 ? 0.5f * time * time : (float) (-0.5 * ((double) --time * ((double) time - 2.0) - 1.0));
case Ease.InCubic:
return (time /= duration) * time * time;
case Ease.OutCubic:
return (float) ((double) (time = (float) ((double) time / (double) duration - 1.0)) * (double) time * (double) time + 1.0);
case Ease.InOutCubic:
return (double) (time /= duration * 0.5f) < 1.0 ? 0.5f * time * time * time : (float) (0.5 * ((double) (time -= 2f) * (double) time * (double) time + 2.0));
case Ease.InQuart:
return (time /= duration) * time * time * time;
case Ease.OutQuart:
return (float) -((double) (time = (float) ((double) time / (double) duration - 1.0)) * (double) time * (double) time * (double) time - 1.0);
case Ease.InOutQuart:
return (double) (time /= duration * 0.5f) < 1.0 ? 0.5f * time * time * time * time : (float) (-0.5 * ((double) (time -= 2f) * (double) time * (double) time * (double) time - 2.0));
case Ease.InQuint:
return (time /= duration) * time * time * time * time;
case Ease.OutQuint:
return (float) ((double) (time = (float) ((double) time / (double) duration - 1.0)) * (double) time * (double) time * (double) time * (double) time + 1.0);
case Ease.InOutQuint:
return (double) (time /= duration * 0.5f) < 1.0 ? 0.5f * time * time * time * time * time : (float) (0.5 * ((double) (time -= 2f) * (double) time * (double) time * (double) time * (double) time + 2.0));
case Ease.InExpo:
return (double) time != 0.0 ? (float) Math.Pow(2.0, 10.0 * ((double) time / (double) duration - 1.0)) : 0.0f;
case Ease.OutExpo:
return (double) time == (double) duration ? 1f : (float) (-Math.Pow(2.0, -10.0 * (double) time / (double) duration) + 1.0);
case Ease.InOutExpo:
if ((double) time == 0.0)
return 0.0f;
if ((double) time == (double) duration)
return 1f;
return (double) (time /= duration * 0.5f) < 1.0 ? 0.5f * (float) Math.Pow(2.0, 10.0 * ((double) time - 1.0)) : (float) (0.5 * (-Math.Pow(2.0, -10.0 * (double) --time) + 2.0));
case Ease.InCirc:
return (float) -(Math.Sqrt(1.0 - (double) (time /= duration) * (double) time) - 1.0);
case Ease.OutCirc:
return (float) Math.Sqrt(1.0 - (double) (time = (float) ((double) time / (double) duration - 1.0)) * (double) time);
case Ease.InOutCirc:
return (double) (time /= duration * 0.5f) < 1.0 ? (float) (-0.5 * (Math.Sqrt(1.0 - (double) time * (double) time) - 1.0)) : (float) (0.5 * (Math.Sqrt(1.0 - (double) (time -= 2f) * (double) time) + 1.0));
case Ease.InElastic:
if ((double) time == 0.0)
return 0.0f;
if ((double) (time /= duration) == 1.0)
return 1f;
if ((double) period == 0.0)
period = duration * 0.3f;
float num1;
if ((double) overshootOrAmplitude < 1.0)
{
overshootOrAmplitude = 1f;
num1 = period / 4f;
}
else
num1 = period / 6.283185f * (float) Math.Asin(1.0 / (double) overshootOrAmplitude);
return (float) -((double) overshootOrAmplitude * Math.Pow(2.0, 10.0 * (double) --time) * Math.Sin(((double) time * (double) duration - (double) num1) * 6.28318548202515 / (double) period));
case Ease.OutElastic:
if ((double) time == 0.0)
return 0.0f;
if ((double) (time /= duration) == 1.0)
return 1f;
if ((double) period == 0.0)
period = duration * 0.3f;
float num2;
if ((double) overshootOrAmplitude < 1.0)
{
overshootOrAmplitude = 1f;
num2 = period / 4f;
}
else
num2 = period / 6.283185f * (float) Math.Asin(1.0 / (double) overshootOrAmplitude);
return (float) ((double) overshootOrAmplitude * Math.Pow(2.0, -10.0 * (double) time) * Math.Sin(((double) time * (double) duration - (double) num2) * 6.28318548202515 / (double) period) + 1.0);
case Ease.InOutElastic:
if ((double) time == 0.0)
return 0.0f;
if ((double) (time /= duration * 0.5f) == 2.0)
return 1f;
if ((double) period == 0.0)
period = duration * 0.45f;
float num3;
if ((double) overshootOrAmplitude < 1.0)
{
overshootOrAmplitude = 1f;
num3 = period / 4f;
}
else
num3 = period / 6.283185f * (float) Math.Asin(1.0 / (double) overshootOrAmplitude);
return (double) time < 1.0 ? (float) (-0.5 * ((double) overshootOrAmplitude * Math.Pow(2.0, 10.0 * (double) --time) * Math.Sin(((double) time * (double) duration - (double) num3) * 6.28318548202515 / (double) period))) : (float) ((double) overshootOrAmplitude * Math.Pow(2.0, -10.0 * (double) --time) * Math.Sin(((double) time * (double) duration - (double) num3) * 6.28318548202515 / (double) period) * 0.5 + 1.0);
case Ease.InBack:
return (float) ((double) (time /= duration) * (double) time * (((double) overshootOrAmplitude + 1.0) * (double) time - (double) overshootOrAmplitude));
case Ease.OutBack:
return (float) ((double) (time = (float) ((double) time / (double) duration - 1.0)) * (double) time * (((double) overshootOrAmplitude + 1.0) * (double) time + (double) overshootOrAmplitude) + 1.0);
case Ease.InOutBack:
return (double) (time /= duration * 0.5f) < 1.0 ? (float) (0.5 * ((double) time * (double) time * (((double) (overshootOrAmplitude *= 1.525f) + 1.0) * (double) time - (double) overshootOrAmplitude))) : (float) (0.5 * ((double) (time -= 2f) * (double) time * (((double) (overshootOrAmplitude *= 1.525f) + 1.0) * (double) time + (double) overshootOrAmplitude) + 2.0));
case Ease.InBounce:
return Bounce.EaseIn(time, duration, overshootOrAmplitude, period);
case Ease.OutBounce:
return Bounce.EaseOut(time, duration, overshootOrAmplitude, period);
case Ease.InOutBounce:
return Bounce.EaseInOut(time, duration, overshootOrAmplitude, period);
case Ease.Flash:
return Flash.Ease(time, duration, overshootOrAmplitude, period);
case Ease.InFlash:
return Flash.EaseIn(time, duration, overshootOrAmplitude, period);
case Ease.OutFlash:
return Flash.EaseOut(time, duration, overshootOrAmplitude, period);
case Ease.InOutFlash:
return Flash.EaseInOut(time, duration, overshootOrAmplitude, period);
case Ease.INTERNAL_Zero:
return 1f;
case Ease.INTERNAL_Custom: // 自定义
return customEase(time, duration, overshootOrAmplitude, period);
default:
return (float) (-(double) (time /= duration) * ((double) time - 2.0));
}
}
Sequence
继承Tween,是一串变化的基本类。
ApplyTween源码加注释:
internal override bool ApplyTween(
float prevPosition,
int prevCompletedLoops,
int newCompletedSteps,
bool useInversePosition,
UpdateMode updateMode,
UpdateNotice updateNotice)
{
return Sequence.DoApplyTween(this, prevPosition, prevCompletedLoops, newCompletedSteps, useInversePosition, updateMode); // 和TweenerCore不一样的是, Sequence需要对一串Tween进行管理, TweenerCore只需要对自己一个Tween进行管理
}
DoApplyTween源码加注释:
internal static bool DoApplyTween(
Sequence s,
float prevPosition, // 变化前时间位置
int prevCompletedLoops, // 变化前完成圈数
int newCompletedSteps, // 新完成阶段
bool useInversePosition, // 是否用逆时间位置, 即是否倒放
UpdateMode updateMode)
{
float time1 = prevPosition; // 记录变化前时间位置
float time2 = s.position; // 记录变化后时间位置
if (s.easeType != Ease.Linear) // 如果非线性的ease
{
// Sequence的ease是用于计算一串Tween的时间位置的, 而不是计算每一个Tween具体变化的
// 这里通过ease计算出实际时间位置
time1 = s.duration * EaseManager.Evaluate(s.easeType, s.customEase, time1, s.duration, s.easeOvershootOrAmplitude, s.easePeriod); // 完成前实际时间位置
time2 = s.duration * EaseManager.Evaluate(s.easeType, s.customEase, time2, s.duration, s.easeOvershootOrAmplitude, s.easePeriod); // 完成后实际时间位置
}
float toPos = 0.0f;
bool prevPosIsInverse = s.loopType == LoopType.Yoyo && ((double) time1 < (double) s.duration ? (uint) (prevCompletedLoops % 2) > 0U : prevCompletedLoops % 2 == 0); // 判断时间位置是否是逆的
if (s.isBackwards) // 如果是倒放
prevPosIsInverse = !prevPosIsInverse;
if (newCompletedSteps > 0) // 如果完成阶段大于0
{
int completedLoops = s.completedLoops; // 记录变化后完成圈数
float position = s.position; // 记录变化后时间位置
int num1 = newCompletedSteps; // 记录新完成阶段数
int num2 = 0;
float fromPos = time1; // 记录完成前实际时间位置
if (updateMode == UpdateMode.Update)
{
while (num2 < num1) // 循环"新完成阶段数"次
{
if (num2 > 0)
fromPos = toPos;
else if (prevPosIsInverse && !s.isBackwards)
fromPos = s.duration - fromPos;
toPos = prevPosIsInverse ? 0.0f : s.duration;
if (Sequence.ApplyInternalCycle(s, fromPos, toPos, updateMode, useInversePosition, prevPosIsInverse, true))
return true; // 需要被kill
++num2;
if (s.loopType == LoopType.Yoyo)
prevPosIsInverse = !prevPosIsInverse;
}
if (completedLoops != s.completedLoops || (double) Math.Abs(position - s.position) > 1.40129846432482E-45)
return !s.active;
}
else
{
if (s.loopType == LoopType.Yoyo && newCompletedSteps % 2 != 0)
{
prevPosIsInverse = !prevPosIsInverse;
time1 = s.duration - time1;
}
newCompletedSteps = 0;
}
}
if (newCompletedSteps == 1 && s.isComplete) // 如果完成阶段等于1并且已经完成了
return false; // 不需要被kill
float fromPos1;
if (newCompletedSteps > 0 && !s.isComplete) // 如果完成阶段大于1并且还没完成
{
fromPos1 = useInversePosition ? s.duration : 0.0f;
if (s.loopType == LoopType.Restart && (double) toPos > 0.0)
Sequence.ApplyInternalCycle(s, s.duration, 0.0f, UpdateMode.Goto, false, false, false);
}
else
fromPos1 = useInversePosition ? s.duration - time1 : time1; // 返回变化实际完成前的实际位置
return Sequence.ApplyInternalCycle(s, fromPos1, useInversePosition ? s.duration - time2 : time2, updateMode, useInversePosition, prevPosIsInverse, false);
}
ApplyInternalCycle源码加注释:
private static bool ApplyInternalCycle(
Sequence s,
float fromPos, // 变化前位置
float toPos, // 变化后位置
UpdateMode updateMode,
bool useInverse, // 是否为倒放
bool prevPosIsInverse,
bool multiCycleStep = false)
{
if ((double) toPos < (double) fromPos) // 如果变化前位置大于变化后位置
{
int num = s._sequencedObjs.Count - 1;
for (int index = num; index > -1; --index)
{
if (!s.active)
return true;
ABSSequentiable sequencedObj = s._sequencedObjs[index];
if ((double) sequencedObj.sequencedEndPosition >= (double) toPos && (double) sequencedObj.sequencedPosition <= (double) fromPos)
{
if (sequencedObj.tweenType == TweenType.Callback)
{
if (updateMode == UpdateMode.Update & prevPosIsInverse)
Tween.OnTweenCallback(sequencedObj.onStart);
}
else
{
float to = toPos - sequencedObj.sequencedPosition;
if ((double) to < 0.0)
to = 0.0f;
Tween t = (Tween) sequencedObj;
if (t.startupDone)
{
t.isBackwards = true;
if (TweenManager.Goto(t, to, false, updateMode))
{
if (DOTween.nestedTweenFailureBehaviour == NestedTweenFailureBehaviour.KillWholeSequence || s.sequencedTweens.Count == 1 && s._sequencedObjs.Count == 1 && !Sequence.IsAnyCallbackSet(s))
return true;
TweenManager.Despawn(t, false);
s._sequencedObjs.RemoveAt(index);
s.sequencedTweens.Remove(t);
--index;
--num;
}
else if (multiCycleStep && t.tweenType == TweenType.Sequence)
{
if ((double) s.position <= 0.0 && s.completedLoops == 0)
{
t.position = 0.0f;
}
else
{
bool flag = s.completedLoops == 0 || s.isBackwards && (s.completedLoops < s.loops || s.loops == -1);
if (t.isBackwards)
flag = !flag;
if (useInverse)
flag = !flag;
if (s.isBackwards && !useInverse && !prevPosIsInverse)
flag = !flag;
t.position = flag ? 0.0f : t.duration;
}
}
}
}
}
}
}
else // 如果变化后位置大于变化前位置
{
int count = s._sequencedObjs.Count; // 记录Sequence中的Tween数量
for (int index = 0; index < count; ++index) // 对于每一个Tween
{
if (!s.active) // 如果这个Sequence没激活
return true; // 直接返回
ABSSequentiable sequencedObj = s._sequencedObjs[index]; // 获得这个Tween
if ((double) sequencedObj.sequencedPosition <= (double) toPos && ((double) sequencedObj.sequencedPosition <= 0.0 || (double) sequencedObj.sequencedEndPosition > (double) fromPos) && ((double) sequencedObj.sequencedPosition > 0.0 || (double) sequencedObj.sequencedEndPosition >= (double) fromPos))
{
// 这个Tween的sequence时间位置小于等于变化后位置并且这个Tween的sequence时间位置小于等于0 或者 这个Tween的sequence结束时间位置大于变化前位置并且这个Tween的sequence时间位置大于0 或者 这个Tween的sequence结束时间位置大于等于变化前位置
// 基本上包括了所有的正常情况
if (sequencedObj.tweenType == TweenType.Callback) // 如果是callback类型的Tween
{
if (updateMode == UpdateMode.Update && (s.isBackwards || useInverse || prevPosIsInverse ? (!(s.isBackwards & useInverse) ? 0 : (!prevPosIsInverse ? 1 : 0)) : 1) != 0)
Tween.OnTweenCallback(sequencedObj.onStart);
}
else // 如果是非callback类型的Tween
{
float to = toPos - sequencedObj.sequencedPosition;
if ((double) to < 0.0)
to = 0.0f;
Tween t = (Tween) sequencedObj;
if ((double) toPos >= (double) sequencedObj.sequencedEndPosition) // 如果变化完时间大于等于这个Tween的sequence结束时间
{
if (!t.startupDone)
TweenManager.ForceInit(t, true);
if ((double) to < (double) t.fullDuration)
to = t.fullDuration;
}
t.isBackwards = false; // 设置没有倒放
if (TweenManager.Goto(t, to, false, updateMode)) // ** 变化这个Tween **
{ // 如果变化完成
if (DOTween.nestedTweenFailureBehaviour == NestedTweenFailureBehaviour.KillWholeSequence || s.sequencedTweens.Count == 1 && s._sequencedObjs.Count == 1 && !Sequence.IsAnyCallbackSet(s))
return true;
TweenManager.Despawn(t, false);
s._sequencedObjs.RemoveAt(index); // 移除改Tween
s.sequencedTweens.Remove(t); // 移除改Tween
--index;
--count;
}
else if (multiCycleStep && t.tweenType == TweenType.Sequence) // 如果变化没完成
{
if ((double) s.position <= 0.0 && s.completedLoops == 0)
{
t.position = 0.0f;
}
else
{
bool flag = s.completedLoops == 0 || !s.isBackwards && (s.completedLoops < s.loops || s.loops == -1);
if (t.isBackwards)
flag = !flag;
if (useInverse)
flag = !flag;
if (s.isBackwards && !useInverse && !prevPosIsInverse)
flag = !flag;
t.position = flag ? 0.0f : t.duration;
}
}
}
}
}
}
return false;
}
PluginsManager
缓存各种类型的Plugin,并提供接口。
TweenSettingsExtensions
Tween的扩展方法。
ShortcutExtensions
Unity组件的扩展方法。