时间系统一混,暂停就会出怪问题
Godot 里有 Timer、delta、SceneTree pause、Engine time_scale、Tween、AnimationPlayer。它们都和时间有关。项目早期随便用,到了暂停菜单、慢动作、技能冷却、UI 倒计时、联网同步时,就会出现奇怪问题:暂停后 Buff 还在掉,UI 动画不动,慢动作影响了下载进度,技能冷却和服务器时间不一致。
客户端需要区分几种时间:游戏模拟时间、UI 时间、真实墙钟时间、服务器时间、特效表现时间。Godot 提供工具,但你要先明确每个系统用哪种时间。
flowchart TD
A[真实时间/墙钟] --> B[UI倒计时/网络超时]
C[游戏模拟时间] --> D[技能/Buff/AI]
E[表现时间] --> F[动画/特效/慢动作]
G[服务器时间] --> H[活动/领奖/赛季]
I[PauseManager] --> C
I --> E
I --> B
B --> J[不同 process mode]
SceneTree pause 不是万能暂停
Godot 可以设置 get_tree().paused = true,配合节点 process mode 控制哪些节点继续运行。它很方便,但如果项目没有规则,会让某些节点意外停或不停。暂停菜单打开后,游戏世界应暂停,UI 菜单应继续,网络请求和下载应继续,某些背景音乐可能降低音量但不完全停止。
每个系统要明确 process mode。Gameplay 节点跟随暂停,UI 节点继续,NetworkService 继续,AudioService 按策略处理。不要让某个重要服务因为挂在暂停的节点下而停止。
暂停入口也要统一。不要每个页面直接改 get_tree().paused。用 PauseManager 管理暂停原因:菜单暂停、剧情暂停、系统弹窗、后台暂停。多个原因叠加时,只有全部释放才恢复。
time_scale 适合慢动作,不适合业务倒计时
Engine time_scale 可以做慢动作,让物理和动画变慢。它不应该影响活动倒计时、网络超时、登录 token 过期、商城刷新。这些用真实时间或服务器时间。否则玩家开慢动作,活动时间也变慢,逻辑就错了。
技能冷却要看游戏设计。单机动作游戏的技能冷却可能随游戏时间暂停;联网游戏的冷却通常由服务器时间或战斗时间决定。客户端要明确,不要简单用 Timer 默认行为。
慢动作还要分层。有些 UI 动画不应慢,伤害数字可能慢,菜单不慢,音乐可能不变或进入特殊混音。time_scale 一刀切时,需要节点 process mode 和自定义时间源配合。
Timer 要按用途封装
Godot Timer 节点适合场景内定时,但项目里最好封装几类计时:GameplayTimer、UITimer、RealtimeTimer、ServerCountdown。不同计时使用不同时间源和暂停规则。
GameplayTimer 受暂停和 time_scale 影响,用于技能前摇、AI 决策、Buff tick。UITimer 不受游戏暂停影响,用于菜单动画和 Toast。RealtimeTimer 用于网络超时和诊断。ServerCountdown 显示服务器目标时间,使用服务器时间估算。
封装后,开发者选择计时器时就会思考用途,而不是随手加一个 Timer。调试面板也能列出当前活跃计时器和时间源。
冷却和倒计时要可恢复
技能冷却、道具倒计时、活动剩余时间都需要在场景切换或重连后恢复。不要只依赖 Timer 节点剩余时间。应保存开始时间、持续时间、时间源,恢复时重新计算剩余。
比如技能冷却保存 battle_time_start 和 cooldown_duration;活动倒计时保存 server_end_timestamp;UI Toast 可以只用本地剩余时间。不同用途恢复策略不同。
如果页面关闭,UI 倒计时节点销毁,再打开时从模型读取目标时间。UI 不应成为时间事实来源。
暂停和后台生命周期
移动端进入后台时,游戏通常暂停,但真实时间继续。回来后,要计算离开多久。单机游戏可以选择是否推进离线收益,战斗可能直接暂停不推进,活动时间按服务器继续。PauseManager 和生命周期服务要协作。
桌面游戏失焦是否暂停,也要有设置。窗口失焦暂停时,网络和下载仍应继续。音频可能降低或静音。
小结
Godot 时间系统要先拆概念。SceneTree pause 管节点运行,time_scale 管慢动作,Timer 按用途封装,服务器时间管活动,真实时间管网络和诊断。暂停原因集中管理,倒计时从模型恢复。这样暂停、慢动作、后台和联网倒计时才不会互相污染。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
我会在调试面板里显示当前 pause reasons、time_scale、活跃计时器和各自时间源。每次出现“暂停后还在动”或“倒计时不走”,这张面板能直接告诉你是哪类时间用错了。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。