本指南将向您展示如何在 C++ 应用中使用存档游戏服务保存和加载玩家的游戏进度数据。您可以使用此服务在游戏过程中的任何时间点自动加载和保存玩家的游戏进度。此服务还可以使玩家触发用户界面来更新或恢复现有的存档游戏,或创建新的存档游戏。
开始之前
如果您尚未这样做,您可能会发现查看存档游戏游戏概念很有帮助。
在开始使用存档游戏 API 编写代码之前
数据格式和跨平台兼容性
保存到 Google 服务器的存档游戏数据必须采用std::vector<uint8_t>
格式。存档游戏服务负责对您的数据进行编码以实现跨平台兼容性;Android 应用可以将此相同的数据作为字节数组读取,而不会出现任何跨平台兼容性问题。
在为存档游戏数据选择数据格式时,避免使用特定于平台的格式。我们强烈建议您使用在多个平台上具有强大库支持的数据格式,例如 XML 或 JSON。
启用存档游戏服务
在您可以使用存档游戏服务之前,您必须首先启用对其的访问权限。为此,请在使用gpg::GameServices::Builder
创建服务时调用EnableSnapshots()
。这将在下一个身份验证事件中启用存档游戏所需的额外身份验证范围。
显示存档游戏
在您的游戏中,您可以提供一个选项,让玩家可以触发保存或恢复存档游戏。当玩家选择此选项时,您的游戏应显示一个屏幕,显示现有的存档槽,并允许玩家保存到或从其中一个槽加载,或创建新的存档游戏。使用以下方法执行此操作
SnapshotManager::ShowSelectUIOperation(...)
存档游戏选择 UI 允许玩家创建新的存档游戏、查看有关现有存档游戏的详细信息以及加载之前的存档游戏。
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);
}
以下示例说明了如何调出默认的存档游戏 UI 并处理玩家的 UI 选择
service_->Snapshots().ShowSelectUIOperation(
ALLOW_CREATE_SNAPSHOT,
ALLOW_DELETE_SNAPSHOT,
MAX_SNAPSHOTS,
SNAPSHOT_UI_TITLE,
[this](gpg::SnapshotManager::SnapshotSelectUIResponse const & response) {
…
}
如果在上面的示例中,ALLOW_CREATE_SNAPSHOT
为true
并且MAX_SNAPSHOTS
大于用户当前创建的快照的实际数量,则默认快照 UI 为玩家提供一个按钮来创建新的存档游戏,而不是选择现有的存档游戏。(显示时,按钮位于 UI 的底部。)当玩家点击此按钮时,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 |
指示存档游戏服务应选择基本存档游戏。 |
快照冲突策略::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()
方法提交保存的游戏更改。
以下示例显示了如何创建更改并提交保存的游戏。
首先,打开我们要编辑的快照,并确保通过选择基本版本来解决所有冲突。
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 } });
接下来,创建一个保存的游戏更改,其中包含用于封面图像的图像数据。
gpg::SnapshotMetadataChange::Builder builder; gpg::SnapshotMetadataChange metadata_change = builder.SetDescription("CollectAllTheStar savedata") .SetCoverImageFromPngData(pngData).Create();
最后,提交保存的游戏更改。
gpg::SnapshotManager::CommitResponse commitResponse = service_->Snapshots().CommitBlocking(metadata, metadata_change, SetupSnapshotData());
data 参数包含您正在存储的所有保存游戏数据。更改还包含其他保存游戏元数据,例如游戏时间和保存游戏的描述。
如果提交操作成功完成,玩家可以在“已保存的游戏”选择 UI 中看到保存的游戏。