> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Game Saves 快速入門

> PlayFab Game Saves 快速入門會逐步引導您完成初始設定、SDK 呼叫以及讀寫雲端存檔資料，讓您能快速整合存檔。

# Game Saves 快速入門

PlayFab Game Saves 可透過將存檔資料同步至雲端，讓玩家在各裝置之間順暢地繼續其進度。本快速入門指南會逐步引導您為 XBOX 和 Windows 平台實作完整的遊戲存檔解決方案。

## 先決條件

開始之前，請確定您已：

* [上線](/zh-TW/services/playfab/player-progression/game-saves/onboarding)至 Game Saves
* 檢閱[概觀](/zh-TW/services/playfab/player-progression/game-saves/overview)一節中的實作需求
* 完成下列需求
* (選用) 複製或檢閱 GitHub 上適用於 Windows 的端對端 **Game Saves 範例**：[PlayFabGameSaveSample-Windows](https://github.com/PlayFab/PlayFab-Samples/tree/master/Samples/All/PlayFabGameSaveSample-Windows)。此範例示範本快速入門中所參考的初始化、同步、衝突處理和上傳流程。

## 您將學到的內容

在本指南中，您將學習如何：

* 初始化 Game Saves 系統
* 從雲端下載現有的存檔資料
* 將本機存檔資料上傳至雲端
* 處理衝突和 UI 回呼
* 管理作用中裝置案例

## 開發需求

### 軟體需求

* [PlayFab 開發人員帳戶](https://developer.playfab.com)
* 建議使用 Visual Studio 2019 或 Visual Studio 2022 進行 Gaming Runtime 開發。如需詳細資料，請參閱 [https://learn.microsoft.com/en-us/gaming/gdk/docs/gdk-dev/get-started/overviews/sdk-and-tools#install-visual-studio。](https://learn.microsoft.com/en-us/gaming/gdk/docs/gdk-dev/get-started/overviews/sdk-and-tools#install-visual-studio。)
* 存取最新的 [Microsoft 遊戲開發套件 (GDK)](https://learn.microsoft.com/gaming/gdk/)

## Game Saves 流程概觀

Game Saves 系統遵循一個在各裝置之間順暢運作的簡單模式：

### 初始設定 (每個遊戲工作階段一次)

1. **初始化服務**：設定 PlayFab Core 和 Game Saves 模組
2. **驗證使用者**：使用 XBOX 驗證讓玩家登入
3. **下載現有存檔**：將其他裝置的存檔資料同步至本機裝置
4. **取得存檔位置**：取得遊戲應寫入存檔檔案的本機存檔根資料夾

### 遊戲期間

5. **寫入存檔檔案**：您的遊戲照常將存檔資料寫入本機存檔根資料夾
6. **上傳變更**：定期將修改過的存檔檔案上傳至雲端
7. **繼續遊玩**：在遊戲工作階段期間視需要重複步驟 5-6

### 工作階段結束

8. **最終上傳**：在玩家結束之前上傳任何最終變更
9. **背景同步**：在 XBOX/Windows 上，系統會在遊戲關閉時自動處理最終上傳

### 主要優點

* **離線支援**：玩家即使沒有網際網路連線也能開始遊玩
* **自動衝突解決**：內建 UI 可處理裝置之間的存檔衝突
* **增量上傳**：只上傳變更的檔案，以改善效能
* **跨裝置連續性**：在裝置之間切換時提供順暢的體驗

## 實作詳細資料

下列各節為每個步驟提供詳細的程式碼範例：

## 步驟 1：初始化 Game Saves

Game Saves 的設計可同時在線上和離線運作，這使其與其他 PlayFab API 不同。它會維護一個永續性的本機使用者身分識別，即使裝置以離線狀態啟動也能運作。

### 重要概念

* **PFLocalUserHandle**：可離線運作的永續性使用者識別碼
* **PFServiceConfigHandle**：您 PlayFab 遊戲的設定
* **離線優先設計**：即使沒有網際網路連線，系統也能立即運作

### 先決條件

初始化 Game Saves 之前，請確定您已：

* 呼叫 `XGameRuntimeInitialize()` 以初始化 XBOX 執行階段
* 呼叫 `XUserAddAsync()` 以讓使用者登入並取得 `XUserHandle`
* 從 Game Manager 取得您的 PlayFab 遊戲識別碼

### 實作

```cpp theme={null}
// Step 1: Initialize PlayFab Core
HRESULT hr = PFInitialize(nullptr);
if (FAILED(hr))
{
    // Handle initialization failure - log error and exit gracefully
    return hr;
}

// Step 2: Create service config handle with your title information
PFServiceConfigHandle serviceConfigHandle{ nullptr };
hr = PFServiceConfigCreateHandle(
    "https://<titleId>.playfabapi.com",    // Replace <titleId> with your actual PlayFab Title ID
    "<titleId>",                           // Replace <titleId> with your actual PlayFab Title ID
    &serviceConfigHandle);
if (FAILED(hr))
{
    // Handle service config creation failure
    return hr;
}

// Step 3: Initialize the Game Saves module
PFGameSaveInitArgs args = {};
// Set args.saveFolder here if you are targetting platforms such as Steam
// where you need to provide root of where the game saves are
hr = PFGameSaveFilesInitialize(&args);
if (FAILED(hr))
{
    // Handle Game Saves initialization failure
    return hr;
}

// Step 4: Create a local user handle
// NOTE: Assumes you have already obtained 'xuserHandle' from XUserAddAsync
PFLocalUserHandle localUserHandle;
hr = PFLocalUserCreateHandleWithXboxUser(serviceConfigHandle, xuserHandle, nullptr, &localUserHandle);
if (FAILED(hr))
{
    // Handle local user creation failure
    return hr;
}

// Success! The Game Saves system is now initialized and ready to use
```

<Info>
  將 `<titleId>` 取代為您從 Game Manager 取得的實際 PlayFab 遊戲識別碼。`xuserHandle` 必須從成功呼叫 `XUserAddAsync` 取得。
</Info>

### 替代平台

對於沒有 XBOX 驗證且不支援離線的平台，請改用 `PFLocalUserCreateHandle` 或 `PFLocalUserCreateHandleWithPersistedLocalId` 的其他版本。如需實作詳細資料，請參閱平台特定文件。

例如：

```cpp theme={null}
PFLocalUserHandle localUserHandle;
hr = PFLocalUserCreateHandleWithSteamUser(serviceConfigHandle, nullptr, &localUserHandle);
if (FAILED(hr))
{
    // Handle local user creation failure
    return hr;
}
```

## 步驟 2：從雲端同步存檔資料

初始化後，將使用者新增至 Game Saves 系統，以同步來自其他裝置的任何現有存檔資料。此步驟也會設定您的遊戲讀寫存檔檔案的本機存檔根資料夾。

### 何時呼叫

* 每個遊戲工作階段一次，在使用者驗證之後
* 當使用者返回遊戲主功能表時
* 從暫停/背景繼續之後

### 此步驟的作用

1. 從其他裝置**下載現有存檔** (只下載新的或變更的檔案)
2. 盡可能**保留檔案時間戳記**，以進行適當的版本控制
3. 透過內建 UI 自動**處理衝突**
4. 為此使用者**將裝置設定為作用中**
5. **提供存檔資料夾路徑**，供遊戲寫入檔案

### 重要限制

* 每個 Game Saves 工作階段只能**成功呼叫一次**
* 需要重新初始化 Game Saves 系統才能再次呼叫
* 會針對衝突、儲存空間問題和裝置爭用觸發 UI 提示

### 實作

```cpp theme={null}
// Add user to Game Saves system and sync from cloud
HRESULT hr;
XAsyncBlock async{};
hr = PFGameSaveFilesAddUserWithUiAsync(localUserHandle, PFGameSaveFilesAddUserOptions::None, &async);
if (FAILED(hr))
{
    // Handle API call failure
    return hr;
}

// Wait for the operation to complete
// For production code, consider using a callback instead of blocking
hr = XAsyncGetStatus(&async, true); 
if (FAILED(hr))
{
    // Handle async operation failure (network issues, user cancellation, etc.)
    return hr;
}

hr = PFGameSaveFilesAddUserWithUiResult(&async);
if (FAILED(hr))
{
    // Handle specific operation failures (conflicts, storage issues, etc.)
    return hr;
}

// Get the local save root folder path for your game
char saveFolder[1024] = { 0 };
hr = PFGameSaveFilesGetFolder(localUserHandle, 1024, saveFolder, nullptr);
if (FAILED(hr))
{
    // Handle folder retrieval failure
    return hr;
}

// Check remaining cloud storage quota
int64_t remainingQuota{ 0 };
hr = PFGameSaveFilesGetRemainingQuota(localUserHandle, &remainingQuota);
if (FAILED(hr))
{
    // Handle quota retrieval failure
    return hr;
}

// Success! You can now read/write save files in the saveFolder directory
printf("Save folder: %s\n", saveFolder);
printf("Remaining quota: %lld bytes\n", remainingQuota);
```

### 後續步驟

此呼叫成功完成後：

* 您的遊戲可以從 `saveFolder` 目錄讀取現有的存檔檔案
* 視需要寫入新的存檔檔案並建立子目錄
* 此裝置現在被視為此使用者的「作用中」裝置
* 如果使用者嘗試在其他裝置上同步，這些裝置會顯示警告

## 步驟 3：將存檔資料上傳至雲端

在您的遊戲將存檔檔案和子資料夾寫入本機存檔根資料夾後，請使用此步驟將變更上傳至雲端。系統會自動偵測並只上傳自上次上傳以來變更的檔案和子資料夾。檔案和資料夾的刪除也會自動同步至雲端。

### 建議的上傳時機

* **重大進度之後**：當玩家到達檢查點或完成關卡時
* **功能表轉換之前**：返回主功能表或切換遊戲模式時
* **結束遊戲時**：在玩家結束遊戲之前
* **定期存檔**：在長時間遊戲工作階段期間每隔幾分鐘

### 上傳選項

* **`KeepDeviceActive`**：裝置保持作用中，允許稍後進行其他上傳
* **`ReleaseDeviceAsActive`**：釋放裝置的作用中狀態，允許在其他裝置上順暢同步

### 平台行為

* **XBOX/Windows**：遊戲關閉後，上傳會在背景繼續
* **其他平台** (Steam Deck 等)：上傳必須在遊戲結束之前完成，否則存檔資料將無法到達雲端

### 實作

```cpp theme={null}
// Upload save files to cloud
XAsyncBlock async{};
HRESULT hr = PFGameSaveFilesUploadWithUiAsync(
    localUserHandle, 
    PFGameSaveFilesUploadOption::KeepDeviceActive,  // Use ReleaseDeviceAsActive when quitting
    &async);
if (FAILED(hr))
{
    // Handle API call failure
    return hr;
}

// Wait for upload to complete
// Consider using callbacks for better user experience
hr = XAsyncGetStatus(&async, true); 
if (FAILED(hr))
{
    // Handle async operation failure (network issues, storage full, etc.)
    return hr;
}

hr = PFGameSaveFilesUploadWithUiResult(&async);
if (FAILED(hr))
{
    // Handle upload failure
    return hr;
}

// Success! Save data is now safely stored in the cloud
```

### 我何時可以再次寫入存檔資料夾？

在上傳期間，系統會先讀取並壓縮您的本機存檔檔案，然後再上傳。一旦同步狀態轉換為 `Uploading` (透過 `PFGameSaveFilesUiProgressCallback` 回報)，表示系統已完成讀取您的檔案，可以安全地再次寫入存檔資料夾。您不需要等待完整的上傳完成，即可繼續存檔。

如果您未使用進度回呼，請等待 `XAsyncBlock` 完成後再寫入新的存檔資料。

### 最佳做法

1. **正常處理失敗**：網路問題不應讓遊戲當機
2. **使用適當的選項**：
   * 在遊戲期間使用 `KeepDeviceActive` 進行其他上傳
   * 當玩家結束或返回功能表時使用 `ReleaseDeviceAsActive`
3. **在非 XBOX 平台上警告使用者**：讓玩家知道不要在上傳期間結束

### 頻率考量

* 支援每個工作階段多次上傳，且效率良好
* 只上傳變更的檔案，將頻寬使用量降至最低
* 如需特定配額和限制，請參閱[限制文件](/zh-TW/services/playfab/player-progression/game-saves/limits)

## 步驟 4：處理 UI 回呼 (選用)

Game Saves 為 XBOX 和 Windows 平台提供內建 UI。在其他平台上 (例如 Steam Deck)，您的遊戲必須透過處理回呼來提供自己的 UI。

UI 回呼會在 `PFGameSaveFilesAddUserWithUiAsync` 和 `PFGameSaveFilesUploadWithUiAsync` 期間引發。每個回呼都會暫停非同步作業，直到您的遊戲回應為止——在所有 UI 回呼都解決之前，`XAsyncBlock` 回呼不會引發。

```cpp theme={null}
// Set up custom UI callbacks (call this before AddUser or Upload operations)
// See sample for detailed examples of these callbacks.
PFGameSaveUICallbacks callbacks{};
callbacks.progressCallback = MyProgressCallback;
callbacks.syncFailedCallback = MySyncFailedCallback;
callbacks.activeDeviceContentionCallback = MyActiveDeviceContentionCallback;
callbacks.conflictCallback = MyConflictCallback;
callbacks.outOfStorageCallback = MyOutOfStorageCallback;

HRESULT hr = PFGameSaveFilesSetUiCallbacks(&callbacks);
```

如需回呼類型、回應 API、使用者動作的完整清單，以及狀態機運作方式的詳細資料，請參閱 [Game Saves UI 回呼](/zh-TW/services/playfab/player-progression/game-saves/ui-callbacks)。

## 了解存檔衝突

當相同的遊戲資料在多部裝置上被修改時，就會發生存檔衝突。Game Saves 會將每個根層級子資料夾視為衝突解決的不可部分完成單位，發生衝突時，玩家可以選擇保留本機或雲端資料。

如需詳細的衝突處理案例和最佳做法，請參閱 [Game Saves 衝突](/zh-TW/services/playfab/player-progression/game-saves/conflicts)。

## 了解 Game Saves 離線模式

Game Saves 可同時在線上和離線運作。連線至雲端時，所有 API 都能正常運作。離線或中斷連線時，本機存檔會繼續運作，但雲端作業會傳回 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD`。

使用 `PFGameSaveFilesIsConnectedToCloud()` 檢查連線狀態，並實作同步失敗回呼以正常處理網路問題。

如需詳細的離線行為和最佳做法，請參閱 [Game Saves 離線模式](/zh-TW/services/playfab/player-progression/game-saves/offline)。

## 了解 Game Saves 作用中裝置變更

當玩家在工作階段中途切換裝置時，請務必防止他們因同時在多部裝置上遊玩而意外遺失進度。

如果您的遊戲只使用 XBOX 的**單一存在點 (SPOP)** 功能登入，則會自動防止此案例。SPOP 可確保使用者一次只能在一部 XBOX 裝置上登入。否則，您也應實作作用中裝置變更回呼，以處理玩家在工作階段中途切換裝置的案例

如需詳細的行為和最佳做法，請參閱 [Game Saves 作用中裝置變更](/zh-TW/services/playfab/player-progression/game-saves/activedevicechanges)。

## 偵錯

查看結果並對 SDK 中的任何呼叫進行偵錯的最簡單方式，是啟用[偵錯追蹤](/zh-TW/services/playfab/sdks/c/tracing)。啟用偵錯追蹤可讓您在偵錯工具輸出視窗中查看結果，並將結果連結至遊戲自己的記錄中。


## Related topics

- [快速入門](/zh-TW/services/playfab/economy-monetization/economy-v2/quickstart.md)
- [GDK 快速入門](/zh-TW/services/playfab/sdks/c/quickstart-gdk.md)
- [Event Sampling 快速入門](/zh-TW/services/playfab/data-analytics/export-data/event-sampling-quickstart.md)
- [Data Connections 快速入門](/zh-TW/services/playfab/data-analytics/export-data/data-connection-quickstart.md)
- [Linux 快速入門](/zh-TW/services/playfab/sdks/c/quickstart-linux.md)
