向您的游戏添加游戏存档

Google 登录 API 弃用后,我们将于 2026 年移除 Play 游戏服务 v1 SDK。自 2025 年 2 月起,您将无法在 Google Play 上发布新集成了 Games v1 SDK 的游戏。我们建议您改用游戏 v2 SDK。
虽然集成旧版游戏 v1 的现有游戏在未来几年内仍可正常运行,但我们建议您从 2025 年 6 月开始迁移到 v2
本指南适用于 Play 游戏服务 v1 SDK。适用于 Play 游戏服务 v2 的 C++ SDK 尚未推出。

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

准备工作

请阅读保存的游戏游戏概念(如果您尚未阅读)。

开始使用 Saved Games 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_SNAPSHOTtrueMAX_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());
    

    data 参数包含您存储的所有游戏存档数据。 该变更还包含其他游戏存档元数据,例如游戏时长和游戏存档说明。

如果提交操作成功完成,玩家可以在游戏存档选择界面中看到相应游戏存档。