# PlayDrop 的遊戲生命週期：載入、就緒、暫停與音訊

初始化 PlayDrop SDK，在第一個可遊玩的畫面發出就緒訊號，順暢地暫停與恢復，遵循主機端的音訊政策，並為原生 App 與離線遊玩預先載入檔案。

Source: https://www.playdrop.ai/zh-hant/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。

## 在 App 中預先載入與離線遊玩

原生 App 可以在遊戲開始前下載它的檔案，並保留下來供離線遊玩。請在 [catalogue.json](https://www.playdrop.ai/zh-hant/create/learn/catalogue-json) 中宣告這些檔案：

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

入口頁面一定會包含在內。`required` 檔案在遊玩前下載；`optional` 檔案則在之後下載。`offlineSupported: true` 讓 App 在必要檔案儲存完成後，就能離線提供遊戲。無效的值會被忽略，絕不會阻擋上傳。網站則照常載入遊戲。對[即玩](https://www.playdrop.ai/zh-hant/create/learn/instant-games)遊戲來說，必要檔案就是啟動大小。

在宣告支援裝置資料的主機端上，離線時產生的存檔、分數與成就會留在裝置上，並在連線後同步；離線時排行榜只會顯示玩家自己的最佳成績。瀏覽器中沒有離線佇列，所以請處理存檔失敗的情況。遊戲從網際網路載入的任何內容仍需要連線。

## 觸覺回饋

在原生 App 中，`sdk.haptics.selection()`、`impact("light" | "medium" | "heavy")` 和 `notification("success" | "warning" | "error")` 會提供短暫的回饋；`sdk.haptics.supported` 表示主機端是否支援。它們在網頁上沒有作用，也與靜音無關。

## 相關指南

- [全螢幕與安全區域](https://www.playdrop.ai/zh-hant/create/learn/viewport): 讓 PlayDrop 遊戲填滿螢幕，並用 SDK 實際測量的 reservedCorners，讓 HUD 保持在 CSS 安全區域內、避開主機端的控制項。
- [雲端存檔](https://www.playdrop.ai/zh-hant/create/learn/cloud-saves): 用 sdk.me.appData 與 sdk.me.updateAppData 儲存及載入玩家進度，適用於訪客與已登入玩家，並說明寫入、預覽與離線遊玩的規則。
- [即玩遊戲](https://www.playdrop.ai/zh-hant/create/learn/instant-games): PlayDrop 遊戲成為即玩遊戲的條件：直向遊玩、訪客可玩、啟動檔案最多 10 MiB、兩秒內開始，以及它與一般遊戲的差異。
- [用戶端 SDK](https://www.playdrop.ai/zh-hant/create/learn/sdk): PlayDrop 瀏覽器 SDK 參考：初始化、主機端生命週期、玩家身分與存檔、裝置與控制器、社群、多人連線、商店、廣告、排行榜、成就、素材與 AI。
