将已保存的游戏添加到您的游戏中

鉴于 Google 登录 API 已弃用,我们将于 2026 年移除 games v1 SDK。2025 年 2 月之后,您将无法在 Google Play 上发布与 games v1 SDK 新集成的游戏。建议您改用 games v2 SDK。
虽然之前集成了 games v1 的现有游戏仍可在未来几年内继续运行,但建议您从 2025 年 6 月开始迁移到 v2
本指南适用于使用 Play 游戏服务 v1 SDK。Play 游戏服务 v2 的 C++ SDK 暂不可用。

本指南将向您展示如何在 C++ 应用中使用已保存的游戏服务来保存和加载玩家的游戏进度数据。您可以在游戏过程中的任何时间点使用此服务自动加载和保存玩家的游戏进度。此服务还允许玩家触发用户界面,以更新或恢复现有存档游戏,或者创建新存档游戏。

准备工作

如果您尚未这样做,建议您查阅已保存的游戏概念

在开始使用已保存的游戏 API 编写代码之前

数据格式和跨平台兼容性

您保存到 Google 服务器的已保存的游戏数据必须为 std::vector<uint8_t> 格式。已保存的游戏服务负责对您的数据进行编码以实现跨平台兼容性;Android 应用可以将这些数据读取为字节数组,而不会出现任何跨平台兼容性问题。

为您的已保存的游戏数据选择数据格式时,避免使用平台专用格式。我们强烈建议您使用 XML 或 JSON 等在多个平台上拥有强大库支持的数据格式。

启用已保存的游戏服务

在使用已保存的游戏服务之前,必须先启用对其的访问权限。为此,创建服务时使用 gpg::GameServices::Builder,然后调用 EnableSnapshots()。这将在下一次授权事件中启用已保存的游戏所需的额外授权范围。

显示已保存的游戏

在您的游戏中,您可以提供一个玩家可以触发的选项来保存或恢复存档游戏。当玩家选择此选项时,您的游戏应显示一个屏幕,其中显示现有的存档位,并允许玩家保存到或从其中一个位加载,或者创建新的存档游戏。为此,请使用以下方法

  SnapshotManager::ShowSelectUIOperation(...)

“已保存的游戏”选择界面允许玩家创建新的存档游戏,查看现有存档游戏的详细信息,以及加载以前的存档游戏。

  SnapshotManager::SnapshotSelectUIResponse response;
  if (IsSuccess(response.status)) {
  if (response.data.Valid()) {
    LogI("Description: %s", response.data.Description().c_str());
    LogI("FileName %s", response.data.FileName().c_str());
    //Opening the snapshot data
    
  } else {
    LogI("Creating new snapshot");
    
  }
} else {
  LogI("ShowSelectUIOperation returns an error %d", response.status);
}

以下示例说明了如何显示默认的“已保存的游戏”界面并处理玩家的界面选择

  service_->Snapshots().ShowSelectUIOperation(
  ALLOW_CREATE_SNAPSHOT,
  ALLOW_DELETE_SNAPSHOT,
  MAX_SNAPSHOTS,
  SNAPSHOT_UI_TITLE,
  [this](gpg::SnapshotManager::SnapshotSelectUIResponse const & response) {
  
      }

在上面的示例中,如果 ALLOW_CREATE_SNAPSHOTtrue 并且 MAX_SNAPSHOTS 大于用户当前创建的实际快照数量,默认的“快照”界面会为玩家提供一个按钮,用于创建新存档游戏,而不是选择现有存档游戏。(显示时,按钮位于界面的底部。)当玩家点击此按钮时,SnapshotSelectUIResponse 响应有效,但没有数据。

打开和读取已保存的游戏

要访问已保存的游戏并读取或修改其内容,首先打开表示该已保存游戏的 SnapshotMetadata 对象。接下来,调用 SnapshotManager::Read*() 方法。

以下示例展示了如何打开已保存的游戏

  LogI("Opening file");
  service_->Snapshots()
  .Open(current_snapshot_.FileName(),
               gpg::SnapshotConflictPolicy::BASE_WINS,
        [this](gpg::SnapshotManager::OpenResponse const & response) {
           LogI("Reading file");
           gpg::SnapshotManager::ReadResponse responseRead =
           service_->Snapshots().ReadBlocking(response.data);
          
        }

检测并解决数据冲突

当您打开 SnapshotMetadata 对象时,已保存的游戏服务会检测是否存在冲突的已保存游戏。当存储在玩家本地设备上的存档游戏与存储在 Google 服务器上的远程版本不同步时,可能会发生数据冲突。

打开已保存的游戏时指定的冲突政策会告知“已保存的游戏”服务如何自动解决数据冲突。政策可以是以下任一项

冲突政策 说明
SnapshotConflictPolicy::MANUAL 指示已保存的游戏服务不应执行任何解决操作。相反,您的游戏将执行自定义合并
SnapshotConflictPolicy::LONGEST_PLAYTIME 指示“已保存的游戏”服务应选择游玩时间值最大的已保存游戏。
SnapshotConflictPolicy::BASE_WINS 指示“已保存的游戏”服务应选择基础已保存游戏。
SnapshotConflictPolicy::REMOTE_WINS 指示“已保存的游戏”服务应选择远程已保存游戏。远程版本是已保存的游戏版本,它在玩家的一台设备上检测到,并且时间戳比基础版本更新。

如果您指定的冲突政策不是 GPGSnapshotConflictPolicyManual,“已保存的游戏”服务将合并已保存的游戏并通过生成的 SnapshotManager::OpenResponse 值返回更新后的版本。您的游戏可以打开已保存的游戏,向其中写入数据,然后调用 SnapshotManager::Commit(...) 方法将已保存的游戏提交到 Google 服务器。

执行自定义合并

如果您将 SnapshotConflictPolicy::MANUAL 指定为冲突政策,您的游戏必须先解决检测到的任何数据冲突,然后才能对已保存的游戏执行进一步的读写操作。

在这种情况下,当检测到数据冲突时,服务会通过 SnapshotManager::OpenResponse 返回以下参数

  • 一个 conflict_id,用于唯一标识此冲突(提交最终版本的已保存游戏时将使用此值);
  • 冲突的已保存游戏的基础版本;以及,
  • 冲突的已保存游戏的远程版本。

您的游戏必须决定要保存哪些数据,然后调用 SnapshotManager::ResolveConflictBlocking() 方法将最终版本提交/解决到 Google 的服务器。

    //Resolve conflict
    gpg::SnapshotManager::OpenResponse resolveResponse =
        manager.ResolveConflictBlocking(openResponse.conflict_base, metadata_change,
                                  openResponse.conflict_id);

写入已保存的游戏

要写入已保存的游戏,首先打开表示该已保存游戏的 SnapshotMetadata 对象,解决检测到的任何数据冲突,然后调用 SnapshotManager::Commit() 方法提交已保存的游戏更改。

以下示例展示了如何创建更改并提交已保存的游戏。

  1. 首先,打开要编辑的快照,并通过选择基础快照来确保所有冲突都已解决。

    service_->Snapshots().Open(
          file_name,
          gpg::SnapshotConflictPolicy::BASE_WINS,
          [this](gpg::SnapshotManager::OpenResponse const &response) {
            if (IsSuccess(response.status)) {
              // metadata : gpg::SnapshotMetadata
              metadata = response.data;
            } else {
              // Handle snapshot open error here
            }
          });
    
  2. 接下来,创建包含用于封面图片的图像数据的已保存游戏更改

    gpg::SnapshotMetadataChange::Builder builder;
    gpg::SnapshotMetadataChange metadata_change =
        builder.SetDescription("CollectAllTheStar savedata")
                 .SetCoverImageFromPngData(pngData).Create();
    
  3. 最后,提交已保存的游戏更改。

    gpg::SnapshotManager::CommitResponse commitResponse =
        service_->Snapshots().CommitBlocking(metadata, metadata_change, SetupSnapshotData());
    

    数据参数包含您存储的所有存档游戏数据。更改还包含其他已保存的游戏元数据,例如游玩时间和已保存游戏的描述。

如果提交操作成功完成,玩家可在“已保存的游戏”选择界面中看到已保存的游戏。