Time.timeScale=0时如何保持UI动画继续播放
解读
国内项目里“暂停”需求非常普遍:战斗暂停、剧情暂停、打开设置面板时冻结世界逻辑,但UI 必须继续动——按钮呼吸、红点闪烁、倒计时数字滚动、Loading 圆圈旋转,一旦卡住玩家立刻感知。
面试官问这道题,不是考你会不会“把动画搬到 UI 线程”,而是看你是否吃透 Unity 的时间体系与更新回路,能否在 timeScale=0 的极端条件下,既保证 UI 动画流畅,又不破坏暂停逻辑、不引入新的性能坑,还能让美术继续用 Animator/UGUI 原有工作流。答不到“Unscaled Time”这一层只能拿 60 分;能把动画更新回路、性能、热更新兼容、设备差异都闭环,才能拿到 Offer。
知识点
- Unity 时间体系
- Time.time、Time.deltaTime、Time.fixedDeltaTime 受 timeScale 影响
- Time.unscaledTime、Time.unscaledDeltaTime、Time.realtimeSinceStartup 不受 timeScale 影响
- UGUI/Animator 更新回路
- Canvas 的 Update 在 PlayerLoop 的 Update 阶段,与 timeScale 无关;但动画系统默认用 scaled time
- Animator 的 Update Mode:Normal / Unscaled Time / Animate Physics
- DOTween 的 SetUpdate(UpdateType.Normal | UpdateType.Unscaled | UpdateType.Late)
- 粒子、Spine、FairyGUI、AssetBundle 热更框架 对暂停的兼容策略
- 移动端省电策略:pause 后 CPU 降频,realtimeSinceStartup 仍递增但帧率可能骤降,需做 maxDelta 截断防穿模
- 多人协作规范:策划配表、美术挂 Animator,程序不能要求他们改流程 → 用 SOD 或 ScriptableObject 配置统一换 Update Mode
答案
分三层回答,先给结论再给落地细节,体现工程思维。
-
UGUI 自带动画
把 Graphic 组件的 CanvasGroup.UpdateMode 或 Image.CrossFadeAlpha 的 ignoreTimeScale 参数设为 true;若用 Animator 做 UI 动画,把 Animator 的 Update Mode 改成 Unscaled Time,一条代码都不用写,美术无感知。 -
代码补间(DOTween 最常见)
所有 UI 动画统一走 DOTween.To(...).SetUpdate(UpdateType.Unscaled),封装成 UIManager.DoTween(this, target, endValue, duration),强制走 Unscaled,防止新人漏写。 -
粒子/Spine/自定义 Shader 动画
- ParticleSystem 把 Simulation Space 设为 Unscaled,或在 Pause() 后手动调用 ParticleSystem.Simulate(Time.unscaledDeltaTime, true, false)
- Spine 的 SkeletonAnimation.timeScale 独立于 Time.timeScale,直接赋 1 即可
- Shader 里用 _TimeParameters.y(即 unscaled time)做 UV 滚动,避免用 _Time.y
-
框架级防呆
在 GamePause.SetTimeScale(0) 里统一遍历所有 Canvas.GetComponentsInChildren<Animator>(true),把 updateMode = AnimatorUpdateMode.UnscaledTime;暂停恢复时再还原,避免策划新加 UI 时漏设置。 -
性能与兼容性
- 暂停后帧率可能掉到 15 fps(国产安卓省电),unscaledDeltaTime 可能 >0.1 s,动画位移要做 maxDelta 0.05 s 截断防穿模
- 热更新 Lua 层用 Time.unscaledTime 代替 os.time(),避免 TimeManager 被 tolua 导出后仍读到 scaled 时间
一句话总结:“让 UI 动画链路里所有时间采样都改拿 unscaled 时间,就能在 timeScale=0 的世界中独善其身。”
拓展思考
-
可暂停的 UI 与不可暂停的 UI 分层
把世界 UI(血条、伤害数字)放进 WorldSpace Canvas,设置 updateMode = UnscaledTime;纯逻辑 UI(背包、设置)保持默认。这样即使以后策划要求“只暂停世界,不暂停背包”,也能直接复用同一套规则。 -
网络帧同步项目
帧同步层用 custom timeScale 驱动逻辑帧,渲染层仍用 Unity 的 timeScale=0 做表现暂停,但 UI 动画继续跑。此时需要双时钟:一个 scaled(逻辑)一个 unscaled(表现),并在 LockStepService 里把 UI 事件插到渲染队列,避免逻辑帧卡住导致 UI 点击无响应。 -
WebGL 与 iOS 后台
WebGL 在标签页切后台时 Time.unscaledTime 也会停,需用 jslib 插件 记录 performance.now() 做补偿;iOS 切后台进入 background 状态,Unity 完全停止,回到前台要手动刷新一次 Animator.Play(state, 0, offset),防止 UI 动画回跳。 -
面试反向提问
答完后可以反问面试官:“咱们项目暂停时是否需要保持粒子特效?如果后期做 GPU Driven Particle,unscaled 时间是否走 Compute Shader 的 CustomTime?” 既展示深度,也探出团队技术栈,加分项。