Unity MyFramework 用法说明(十九):使用 TimeManager 统一控制暂停、慢动作与时间缩放

📅 2026/7/29 2:56:46
Unity MyFramework 用法说明(十九):使用 TimeManager 统一控制暂停、慢动作与时间缩放
游戏暂停、技能慢动作、命中停顿等效果本质上都在修改游戏时间。如果业务代码到处直接设置Time.timeScale后续很容易出现暂停后无法恢复、UI 也被停止、多个效果互相覆盖等问题。MyFramework 使用TimeManager统一处理时间缩放并支持通过关键帧曲线平滑改变时间速度。项目地址https://github.com/ZHOURUIH/MyFramework一、为什么不应该到处修改 Time.timeScale最直接的写法是Time.timeScale 0.0f;恢复游戏Time.timeScale 1.0f;代码本身没有问题但当项目中多个功能都能修改时间时管理会逐渐混乱暂停界面将时间设置为0 技能将时间设置为0.2 受击效果将时间设置为0.05 剧情又将时间恢复为1每个系统都直接修改全局时间就很难确认当前时间缩放由谁控制。MyFramework 将这段操作集中封装为OT.TIME(0.0f);调用代码表达的是一次明确的“修改游戏时间”操作而不是直接修改 Unity 全局字段。二、立即修改时间速度暂停游戏OT.TIME(0.0f);恢复正常速度OT.TIME(1.0f);降低到一半速度OT.TIME(0.5f);加快到两倍速度OT.TIME(2.0f);OT.TIME(float scale)会立即应用目标值不执行渐变过程。例如暂停界面public class UIPause : LayoutScript { public override void onGameState() { base.onGameState(); OT.TIME(0.0f); } protected void onContinueClick() { OT.TIME(1.0f); LT.HIDEUIPause(); } }退出暂停界面时必须明确恢复时间避免游戏一直保持暂停状态。三、平滑进入慢动作有些效果不适合瞬间改变速度。例如角色释放大招时可以在短时间内从正常速度降低到0.2OT.TIME( 1.0f, 0.2f, 0.15f);三个参数分别表示起始时间缩放 目标时间缩放 变化所需时间恢复正常速度OT.TIME( 0.2f, 1.0f, 0.25f);默认使用KEY_CURVE.ZERO_ONE也就是按照从0到1的曲线在起始值和目标值之间插值。四、使用不同的关键帧曲线需要控制变化节奏时可以指定关键帧曲线OT.TIME( KEY_CURVE.QUAD_OUT, 1.0f, 0.1f, 0.2f);例如ZERO_ONE 线性变化 QUAD_IN 开始慢之后加快 QUAD_OUT 开始快之后减慢 SINE_IN_OUT 两端平缓框架中的时间缩放和移动、旋转、缩放等渐变操作使用同一套KeyFrameManager曲线。因此不需要为时间变化单独维护一套插值代码。五、监听变化过程和完成结果需要在变化过程中或完成后执行逻辑可以使用TIME_EXOT.TIME_EX( KEY_CURVE.QUAD_OUT, 1.0f, 0.1f, 0.2f, false, 0.0f, onTimeChanging, onTimeChanged);回调函数protected void onTimeChanging( ComponentKeyFrame component, bool isBreak) { // 时间缩放正在变化 } protected void onTimeChanged( ComponentKeyFrame component, bool isBreak) { if (isBreak) { return; } log(时间缩放完成); }isBreak表示本次变化是否被新的时间变化打断。例如慢动作尚未结束时又执行了OT.TIME(1.0f);原来的完成回调会收到isBreak true这样可以避免把“被打断”误认为“正常播放完成”。六、为什么时间缩放到 0 后仍然能够恢复如果时间已经停止OT.TIME(0.0f);普通使用缩放时间更新的对象将不再获得有效的elapsedTime。但TimeManager中负责时间变化的COMTimeScale会忽略时间缩放mCOMTimeScale.setIgnoreTimeScale(true);所以即使当前Time.timeScale 0仍然可以执行OT.TIME( 0.0f, 1.0f, 0.2f);时间缩放组件会使用未缩放时间完成恢复不会因为自己把时间停止而无法继续更新。七、暂停游戏时让 UI 继续运行暂停游戏后普通角色、特效和场景逻辑应该停止但暂停界面的按钮、动画和提示通常还需要继续工作。可以让整个布局忽略时间缩放public class UIPause : LayoutScript { public override void init() { base.init(); mLayout.setIgnoreTimeScale(true); } }这样布局更新时会使用未缩放时间。暂停状态下仍然可以正常执行按钮交互 界面动画 倒计时提示 加载图标旋转 界面渐入渐出只让某个 UI 对象忽略时间缩放mLoadingIcon.setIgnoreTimeScale(true);其他界面对象仍然跟随游戏时间。八、让角色或组件忽略时间缩放ComponentOwner和GameComponent也支持独立设置character.setIgnoreTimeScale(true);角色的组件会一同使用未缩放时间。只设置某个组件COMCharacterAnimation animation character.getComponentCOMCharacterAnimation(); animation?.setIgnoreTimeScale(true);适合以下场景暂停时仍然播放的展示角色 不受慢动作影响的界面特效 独立运行的摄像机动画 暂停期间仍要更新的系统组件不要为了图方便让所有对象都忽略时间缩放否则暂停和慢动作就失去了意义。九、一个简单的命中慢动作命中敌人时先快速进入慢动作public class CmdCharacterHitStop { public static void execute() { OT.TIME_EX( KEY_CURVE.QUAD_OUT, 1.0f, 0.1f, 0.08f, onSlowMotionFinished); } protected static void onSlowMotionFinished( ComponentKeyFrame component, bool isBreak) { if (isBreak) { return; } OT.TIME( 0.1f, 1.0f, 0.12f); } }执行CmdCharacterHitStop.execute();流程为正常速度 ↓ 快速降低到0.1 ↓ 降低完成 ↓ 逐渐恢复到1.0如果中途有其他逻辑重新设置时间旧回调会收到isBreak不会继续覆盖新的时间状态。十、使用时需要注意什么时间缩放是全局状态使用时主要注意三点。第一进入暂停、剧情或慢动作时要有明确的恢复入口OT.TIME(1.0f);第二需要在暂停时继续更新的界面或对象应设置setIgnoreTimeScale(true);第三多个系统都可能修改时间时应通过 Command 统一调用避免在各个类中直接设置Time.timeScale。例如CmdGamePause.execute(); CmdGameResume.execute(); CmdCharacterHitStop.execute();这样可以直接追踪每一种时间变化由什么业务触发。十一、总结MyFramework 的时间缩放主要通过以下接口完成OT.TIME(0.0f); OT.TIME(1.0f); OT.TIME(start, target, onceLength); OT.TIME(keyframe, start, target, onceLength); OT.TIME_EX(...);它将时间控制统一为立即修改时间速度 平滑进入或退出慢动作 使用关键帧曲线控制节奏 监听变化完成或被打断 让指定对象忽略时间缩放TimeManager不只是对Time.timeScale做一次简单包装而是把全局时间变化接入框架的关键帧、组件更新和未缩放时间体系让暂停和慢动作能够与角色、UI、特效及其他框架对象保持一致。