# PlayDrop 上的游戏生命周期：加载、就绪、暂停和音频

初始化 PlayDrop SDK，在第一个可玩帧时发出就绪信号，干净地暂停和恢复，遵循宿主的音频策略，并为原生应用和离线游玩预加载文件。

Source: https://www.playdrop.ai/zh-hans/create/learn/lifecycle

PlayDrop 会在你的游戏周围显示自己的加载画面、暂停菜单、静音控制和其他窗口。生命周期 API 让游戏与它们保持同步：PlayDrop 暂停游戏时游戏就停下，并且只在 PlayDrop 的音频策略允许时才播放声音。

## 先初始化，再发出就绪信号

加载 SDK，读取初始状态，订阅变化，准备好第一个画面，然后才调用 `ready()`。在此之前，PlayDrop 会一直显示它的加载画面。

```html
<script src="https://assets.playdrop.ai/sdk/playdrop.js"></script>
<script type="module">
  const sdk = await playdrop.init();
  let paused = sdk.host.isPaused;
  setAudio(sdk.host.audioAllowed);
  sdk.host.onAudioAllowedChange(setAudio);
  sdk.host.onPause(() => { paused = true; });
  sdk.host.onResume(() => { paused = false; last = performance.now(); });

  try {
    await loadFirstLevel();
  } catch (error) {
    sdk.host.error("Could not load the first level");
    throw error;
  }
  drawFirstFrame();
  sdk.host.ready();
</script>
```

等你设计的第一个画面已经显示、并且输入可用时再调用 `ready()`，不要在素材加载完成之前调用。`sdk.host.error(message)` 会告诉 PlayDrop 游戏无法启动。`setLoadingState` 和 `firstFrameReady` 是同一步骤的旧名称；请使用 `ready()`。

## 阶段：预览、游玩和编辑器

`sdk.host.phase` 为 `preview`、`play` 或 `editor`，`onPhaseChange` 会报告阶段切换。预览和录制会在没有真实会话的情况下展示游戏：在 `play` 之外，绝不能写入存档、分数、成就或购买。`sdk.host.mode` 通常为 `play`，在本地 Dev 框架中为 `dev`。

## 暂停和恢复

当玩家打开暂停菜单、隐藏标签页、登录、购买东西，或者广告或 PlayDrop 窗口遮住游戏时，会触发 `onPause(reason)` 和 `onResume(reason)`。`reason` 为 `tab_hidden`、`auth_prompt`、`purchase_flow`、`host_overlay`、`detail_hidden`、`route_change` 或 `native_background` 之一；`sdk.host.isPaused` 是当前状态。

暂停期间，停止模拟和计时器。恢复时，重置你的帧时钟，避免游戏突然向前跳：

```js
let last = performance.now();
function frame(now) {
  const dt = paused ? 0 : Math.min((now - last) / 1000, 0.05);
  last = now;
  if (!paused) update(dt);
  render();
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
```

Esc 键和控制器的菜单键属于 PlayDrop 的暂停菜单；不要在游戏中绑定它们。在联网游戏中，暂停期间也要保持房间连接：一位玩家暂停不能让其他玩家停下来。

## 音频

在 `init()` 之后读取一次 `sdk.host.audioAllowed`，之后遵循 `onAudioAllowedChange`。它综合了玩家的静音设置、阶段和暂停状态；在因隐藏标签页而暂停时仍为 true，这样音乐可以保持播放位置。如果你还需要知道原因或宿主音量，`onAudioPolicyChange` 会报告 `{ enabled, reason, volume }`。

```js
const audio = new AudioContext();
function setAudio(allowed) {
  (allowed ? audio.resume() : audio.suspend()).catch(() => {});
}
```

在宿主允许之前，音频一开始是静音的；而且浏览器仍需要玩家先操作一次才能播放声音。游戏内的音量设置可以作为额外的限制保留；宿主的静音始终优先。请捕获被拒绝的 `play()` 和 `resume()` promise。

## 在应用中预加载和离线游玩

原生应用可以在游戏启动前下载它的文件，并保留下来供离线游玩。请在 [catalogue.json](https://www.playdrop.ai/zh-hans/create/learn/catalogue-json) 中声明这些文件：

```json
{
  "prefetch": { "required": ["game.js", "assets/level-1.png"], "optional": ["assets/music.mp3"] },
  "offlineSupported": true
}
```

入口页面始终包含在内。`required` 文件会在游玩前下载；`optional` 文件在之后下载。`offlineSupported: true` 让应用在保存好必需文件后提供离线游玩。无效的值会被忽略，绝不会阻止上传。网站会照常加载游戏。对于[即玩](https://www.playdrop.ai/zh-hans/create/learn/instant-games)游戏，必需文件的大小就是启动大小。

在声明支持设备数据的宿主上，离线时产生的存档、分数和成就会保留在设备上，并在联网后同步；离线时排行榜只显示玩家自己的最佳成绩。浏览器中没有离线队列，所以请处理存档失败的情况。游戏从互联网加载的任何内容仍然需要网络连接。

## 触感反馈

在原生应用中，`sdk.haptics.selection()`、`impact("light" | "medium" | "heavy")` 和 `notification("success" | "warning" | "error")` 可以提供短暂的触感反馈；`sdk.haptics.supported` 表示宿主是否支持。它们在网页上不起作用，并且与静音无关。

## 相关指南

- [全屏和安全区域](https://www.playdrop.ai/zh-hans/create/learn/viewport): 让 PlayDrop 游戏铺满屏幕，借助 SDK 测得的 reservedCorners，让 HUD 留在 CSS 安全区域内并避开宿主的控件。
- [云存档](https://www.playdrop.ai/zh-hans/create/learn/cloud-saves): 用 sdk.me.appData 和 sdk.me.updateAppData 保存和读取玩家进度，适用于访客和已登录玩家，并了解写入、预览和离线游玩的规则。
- [即玩游戏](https://www.playdrop.ai/zh-hans/create/learn/instant-games): PlayDrop 游戏成为即玩游戏的条件：竖屏游玩、允许访客、启动文件最多 10 MiB 并在两秒内启动，以及它与普通游戏的区别。
- [客户端 SDK](https://www.playdrop.ai/zh-hans/create/learn/sdk): PlayDrop 浏览器 SDK 参考：初始化、宿主生命周期、玩家身份和存档、设备和控制器、社交、多人游戏、商店、广告、排行榜、成就、素材和 AI。
