暂停与继续需协调逻辑、渲染、输入、音频多系统,核心是状态隔离;用全局isPaused标志控制主循环更新,输入分发需识别游戏状态,音频应记录播放位置而非粗暴暂停,禁用timeScale实现暂停,须监听系统事件兜底。

暂停与继续功能不是简单地停止计时器或冻结画面,而是要协调游戏逻辑、渲染、输入和音频多个子系统的一致性状态切换。核心在于“状态隔离”——让暂停时的更新不污染继续后的世界状态。
用布尔标志控制主循环更新逻辑
绝大多数游戏引擎或自研框架都依赖一个主循环(如 gameLoop 或 update()),暂停的本质是跳过逻辑更新,但保持渲染和输入采集(否则无法响应“继续”操作)。
-
isPaused必须是全局可读写的状态变量,且所有关键更新函数开头都要检查它 - 不要在
render()中加暂停判断——画面冻结靠的是不更新对象位置/动画帧,而非停渲染 - 物理引擎(如 Box2D、Matter.js)通常提供
setSleepingAllowed(false)或world.step()跳过调用,不能只靠标志位 - 示例片段:
function update() {
if (isPaused) return;
player.update();
enemies.forEach(e => e.update());
physicsWorld.step();
}
输入事件需区分“暂停中”与“游戏中”
用户在暂停界面按空格键,你得知道这是想继续,而不是触发跳跃——输入处理层必须感知当前游戏状态。
- 键盘监听应统一走一个
handleInput()入口,内部根据gameState(如'playing'/'paused'/'menu')分发逻辑 - 鼠标点击坐标需转换为 UI 坐标系再判断是否点中“继续按钮”,不能直接作用于游戏对象
- 手柄按键(如 Start 键)常被系统级捕获,某些平台(如 Switch WebApp)需手动启用
gamepad.connected监听并延迟注册
音频暂停要分层处理,避免音效错位
背景音乐可以整体暂停,但暂停瞬间正在播放的 UI 音效(如按钮点击声)必须允许播完,否则下次点击会漏响。
- 使用 Web Audio API 时:
audioContext.suspend()暂停整个上下文,但会中断所有正在播放的AudioBufferSourceNode - 更稳妥做法:对每个音效实例维护
isPlaying和currentTime,暂停时记录位置,继续时从该时间点重播(仅适用于循环类 BGM) - 商业引擎(Unity、Godot)建议用
AudioSource.Pause()而非AudioSource.Stop(),后者会丢失播放位置 - 特别注意:iOS Safari 对
audioContext的自动暂停策略极严,首次交互前必须由用户手势触发resume()
时间缩放(Time Scale)不是暂停的替代方案
把 timeScale = 0 当作暂停看似简洁,但它会破坏帧同步逻辑、插值计算和基于 delta-time 的物理模拟,尤其在跨平台或高帧率设备上极易出错。
-
deltaTime在暂停时应固定为 0,而非依赖timeScale * rawDelta计算——后者在浮点误差积累后可能产生微小非零值,导致对象缓慢漂移 - 动画系统若用
elapsedTime += deltaTime * timeScale,暂停时elapsedTime仍会因浮点精度缓慢增长 - 真正需要变速(如子弹时间)才用
timeScale;纯暂停请用分支逻辑硬控制
最易被忽略的是状态持久性:切出应用(如 Android 按 Home 键)、浏览器标签页失焦、甚至 iOS 后台音频限制,都会触发系统级暂停。这些场景下,仅靠游戏内 isPaused 标志不够,必须监听 document.hidden、pagehide、visibilitychange 等事件做兜底同步。











