DOTween 的大部分核心类(如DOTween、TweenManager、Tweener)都是纯 C# 静态类或普通 C# 类,并不继承自MonoBehaviour。DOTweenComponent作为场景中唯一且常驻的MonoBehaviour单例,负责将 DOTween 的轮询更新、协程等待、后台暂停处理以及 Editor 调试等功能接入 Unity 的原生生命周期。
一、 单例管理与初始化 (Singleton & Setup)
1. 静态工厂方法 (Create&DestroyInstance)
自动创建:当调用
DOTween.Init()或播放第一个 Tween 时,DOTween 会隐式调用DOTweenComponent.Create()。常驻不销毁:自动创建一个名为
[DOTween]的隐藏GameObject,并调用DontDestroyOnLoad,确保场景切换时动画驱动不中断。
2. 双重防重机制 (Awake&Start)
单例锁定:在
Awake中,如果DOTween.instance为空,则将当前组件设为全局单例,并初始化_unscaledTime。模块加载:通过 C# 反射寻找
DG.Tweening.DOTweenModuleUtils并执行其Init方法,用于动态加载 Unity 各种扩展模块(如 UGMA、Audio、Physics 等)。重复销毁:如果场景中重复合成了额外的
DOTweenComponent,Awake和Start会标记_duplicateToDestroy = true并直接销毁重复的GameObject,防止多重驱动导致动画加速。
二、 三大更新驱动管线 (Update Loop)
DOTween 将 Tween 划分为三种更新类型(UpdateType.Normal、UpdateType.Late、UpdateType.Fixed),分别在对应的生命周期函数中统一驱动:
Unity 帧周期 ├── Update() ──────► TweenManager.Update(UpdateType.Normal) ├── LateUpdate() ──────► TweenManager.Update(UpdateType.Late) └── FixedUpdate() ──────► TweenManager.Update(UpdateType.Fixed)1.Update():主更新循环
Unscaled Time 计算:手动计算当前帧的
_unscaledDeltaTime = realtimeSinceStartup - _unscaledTime,避免切后台或依赖 Unity API 带来的不确定性。平滑帧间隔(Smooth Delta Time):如果开启了
DOTween.useSmoothDeltaTime,会限制_unscaledDeltaTime不超过阈值(maxSmoothUnscaledTime),防止卡顿恢复时产生巨大的帧跳跃。驱动更新:调用
TweenManager.Update,传入受timeScale影响的常规时间差与不受影响的unscaledDeltaTime。编辑器统计:在 Unity Editor 模式下自增
inspectorUpdater用于刷新 Inspector 面板,同时记录峰值活跃 Tweener 与 Sequence 数量(用于性能分析)。
2.LateUpdate():延迟更新循环
专门处理跟随相机移动或需要等待
Update逻辑完成后再计算的动画。仅在TweenManager.hasActiveLateTweens为真时触发,减少不必要的开销。
3.FixedUpdate():物理更新循环
专门处理基于物理引擎(如
Rigidbody)的动画。暂停防护:增加
Time.timeScale <= 0.0的防范判断,确保物理暂停时无视物理时间的动画逻辑正常响应。
三、 协程等待指令 (Yield Instructions)
DOTween 允许使用yield return tween.WaitForCompletion()这种极其简洁的语法。源码中这些WaitForXXX方法全部是internal IEnumerator协程:
internal IEnumerator WaitForCompletion(Tween t) { while (t.active && !t.isComplete) yield return (object) null; }核心原理:
零 GC 轮询:每帧检查 Tween 的状态标志位(如
active、isComplete、playedOnce、position等)。自动释放:一旦 Tween 被销毁(
t.active == false)或达成指定条件,循环立即终止,协程继续向下执行,从源头避免了因对象已死而导致协程死锁的问题。
四、 应用状态与边界条件处理
1. 切后台平滑恢复 (OnApplicationPause)
public void OnApplicationPause(bool pauseStatus) { if (pauseStatus) { this._paused = true; this._pausedTime = Time.realtimeSinceStartup; } else { if (!this._paused) return; this._paused = false; // 关键:补齐切后台期间消耗的时间 this._unscaledTime += Time.realtimeSinceStartup - this._pausedTime; } }问题:在移动端,应用切到后台再切回时,
Time.realtimeSinceStartup会包含在后台经历的巨长时间。如果不做处理,取消暂停后的第一帧_unscaledDeltaTime会极大,导致所有 Unscaled 时间模式的 Tween 直接瞬间跳到终点。解决:记录切后台的时间差,并在切回时将
_unscaledTime对应后移,抵消切后台的时间跳变。
2. 游戏退出 (OnApplicationQuit)
将
DOTween.isQuitting标记为true,防止在场景销毁阶段因为触发某些 UI 回调而再次误建新的 Tween 或引发空指针异常。
3. 销毁与安全模式报告 (OnDestroy)
当DOTweenComponent被销毁(通常是退出游戏或关闭应用)时:
打印峰值峰值统计:如果开启了
showUnityEditorReport,输出整个运行期间同时激活的 Tween 数量峰值。安全模式汇总 (
Safe Mode Report):汇总并警告运行期间捕获的错误分类:totMissingTargetOrFieldErrors:目标对象或字段丢失(如动画播放中 GameObject 被提前 Destroy)。totStartupErrors:初始化/启动异常。totCallbackErrors:用户回调函数内部抛出的异常(OnComplete等)。totUnsetErrors:未归类的其他错误。
清理资源:调用
DOTween.Clear(true, this._isQuitting)清空所有动画内存池。
五、 容量设置 (Memory Capacity)
public IDOTweenInit SetCapacity(int tweenersCapacity, int sequencesCapacity) { TweenManager.SetCapacities(tweenersCapacity, sequencesCapacity); return (IDOTweenInit) this; }允许开发者手动指定
Tweener和Sequence的对象池初始容量,避免运行期间因为频繁动态扩容(Array.Resize)造成主线程 GC Alloc 和卡顿。
总结
DOTweenComponent表现为一个高可靠性的中央驱动器:
隐藏式管理:在后台默默提供生命周期驱动,无需开发者手动挂载脚本。
时间鲁棒性:自行维护
unscaledTime,对帧率波动、切后台、TimeScale 改变进行了极其严密的保护。安全与调试:集成了安全模式统计、Editor 预览刷新、Gizmos 绘制和内存池容量分配能力。