为您的游戏添加游戏存档

本指南介绍了如何使用 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_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 参数包含您要存储的所有游戏存档数据。 这项变更还包含其他游戏存档元数据,例如游戏时间及游戏存档的说明。

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