向您的游戏添加游戏存档

本指南介绍了如何在 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_SNAPSHOTtrue,并且 MAX_SNAPSHOTS 大于用户当前创建的实际快照数量,则默认的 Snapshot 界面会为玩家提供一个用于创建新游戏存档的按钮,而不是选择现有游戏存档。(显示时,该按钮位于界面底部。)当玩家点击此按钮时,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 参数包含您要存储的所有游戏存档数据。这项更改还包含其他游戏存档元数据,例如游戏时间和游戏存档说明。

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