本主题概括介绍有关如何使用 XGameSave
API 的几个最佳做法。
何时获取存储空间
一般情况下,当用户登录并指示要玩游戏后,游戏应该立即尝试获取 XGameSave
存储空间。 游戏通过调用 XGameSaveInitializeProvider 或 XGameSaveInitializeProviderAsync 获取存储空间。
获取用户的连接存储空间的执行时间可能有所不同。 游戏应在主执行期间执行此操作,而不是在响应用户开始注销或在响应接收来自系统的暂停通知时执行。 你还应该考虑将连接存储空间的获取与数据加载的长序列保持一致,以便操作可以并行执行。
当游戏请求访问 XGameSave
存储空间时,系统会执行同步过程,使用户保存的数据在各 Xbox One 主机中状态一致,并使其数据可在脱机游戏时使用。 由于同步花费的时间可能各有不同,且可能需要用户做决策,系统可能会此过程的不同阶段向用户显示 UI。
用户可以通过随时按下 Xbox 按钮导航离开应用,即使同步 UI 处于活动状态。 离开后,系统会隐藏 UI,但会在没有用户交互的情况下尽可能地继续同步。 当用户回到应用后,UI 将重新显示,除非同步已完成。 当 UI 隐藏时,系统绝不会假定用户已做出选择。
存储空间的生命周期
一般情况下,最佳做法是在游戏有了给定存储空间的 XGameSaveProviderHandle
后,它将在该游戏会话的生命周期内保留该句柄。 保留句柄对游戏没有不利的性能影响。 在释放某一句柄后尝试重新获取该句柄可能需要一些时间。
何时使用“按需同步”
大多数游戏不应该使用“按需同步”。 游戏可以在获取存储空间时选择使用“按需同步”,将 true
传递给 XGameSaveInitializeProvider 或 XGameSaveInitializeProviderAsync 的 syncOnDemand
参数。
“按需同步”在游戏同步保存内容时更改。
- 在未使用“按需同步”时,所有同步将在获取了存储空间时发生。 用户可能会看到 UI 在游戏中出现,具体取决于保存游戏的大小和其他条件。
- 当使用“按需同步”时,仅当游戏尝试通过调用 XGameSaveReadBlobData 或 XGameSaveReadBlobDataAsync 从存储容器读取数据时,才会进行同步。 如果需要同步,可能会向用户显示 UI。
“按需同步”应仅在满足以下所有条件时使用。
- 游戏具有大量容器,且每个容器都很大。
- 当首次需要从存储容器读取时,游戏并未处于活动游戏中。请记住,首次读取可能会提示需要同步整个容器。
- 游戏会话只需要访问有限数量的容器。
防止意外覆盖
同步已保存的游戏可能需要一些时间。 用户可以选择取消同步保存的游戏。 当用户选择取消时,存储空间及其容器可能不在本地设备上,或可能只部分完成了同步。 游戏需要自行保护,以确保它们不会对可能已经下载的内容做出错误的假设。
当你对设备上已创建的容器调用 XGameSaveCreateContainer 时,它将打开该容器,让游戏可以对该容器进行更新。
当你调用 XGameSaveCreateContainer 并且设备上不存在该容器时(由于尚未同步或游戏有意创建新容器),它将创建一个新容器。 如果目的是要进行一些新尝试,你会达到预期目的。 如果发生这种情况是因为有些内容未同步,当新(和无意)创建的容器与云中的哪怕一个容器冲突时,就有可能丢失数据。
要避免这种情况发生很简单:始终在调用 create
之前枚举你感兴趣的容器。 游戏可以通过调用 XGameSaveEnumerateContainerInfo 或 XGameSaveEnumerateContainerInfoByName 枚举容器。
何时保存
只要游戏收到暂停通知,游戏就应至少保存相关数据,使系统能够返回到用户的上下文适当的状态。
如果游戏设计使用定期的、自动的或由用户启动的保存,则 XGameSave
的调用频率比接收到暂停通知时的调用频率更高。 这样做可以很好地降低由于断电或故障而丢失数据的风险。
当用户注销时,该用户的 XUser
对象会继续有效,前提是游戏获取了注销延迟。 通过该延迟,游戏可以使用 XGameSave
API 执行最终的保存操作。
一般指南
请不要跨容器存储依赖数据
请不要跨多个容器存储具有依赖关系的数据。 容器可能会因为同步不完整、断电或其他错误条件而分隔。 存储在单个容器中的数据必须保持自足和本身一致。
请不要阻止用户关闭主机或导航离开
游戏不应阻止用户在保存时关闭主机或导航离开应用。 在 Xbox One 上,你的游戏会收到暂停事件,并且有 1 秒钟的时间使用 XGameSave
API 来保存状态。 在完全关闭或进入低功耗状态之前,系统会确保数据已正确提交到硬盘。
如果用户弹出游戏的磁盘并运行其他磁盘,会发生同样的暂停过程。
请注意,最好使用 XGameSave
API 的异步版本。 这将允许游戏在系统写出游戏保存数据时继续其暂停代码的其余部分。 有关如何完成此操作的示例,请参阅 XGameSaveSubmitUpdateAsync。
在游戏尝试加载数据时通知用户
当你的游戏正在等待 XGameSaveInitializeProvider 调用完成时,以可视方式向用户指示应用正在尝试加载数据。 虽然系统会在应用以全屏显示模式运行时,在同步过程中提供 UI,但是,如果用户导航到“主页”屏幕,此 UI 将被隐藏。 如果系统正在为请求的 XGameSave
存储空间同步大量数据,可能会发生这种情况。
保留 XGameSave 存储空间
保留 XGameSaveProviderHandle
,而不是在每次发生读取或写入事件时尝试获取这些对象。 长时间保留句柄不会对性能或稳定性造成负面影响。
注意
如果在任何时间有任何 XGameSave
API 返回了 E_GS_HANDLE_EXPIRED
,游戏应该丢弃所有 XGameSave
对象,然后重新获取 XGameSaveProviderHandle
。
将数据大小维持在较小的值
将已保存数据大小维持在较小的值。 当主机处于联机状态时,存储空间中的所有用户数据都会上传到云中。 优化数据格式,确保将延迟和带宽使用量降到最低。
即使超出保存数据的物理大小,也应考虑容器内的 blob 数量。 即使容器仅有 1 MB,如果该容器有 1,000 个 blob,每个 1KB,它的同步时间也会比 blob 数量更少但大很多的容器要长。
验证用户是否不介意未保存
检查返回自 XGameSaveInitializeProvider 和 XGameSaveSubmitUpdate 的 E_GS_OUT_OF_LOCAL_STORAGE
错误。 询问用户,了解他们是否确实要在不保存的情况下玩游戏。 如果用户指示想要保存游戏,请重试此操作。
检查用户的配额并提示清除空间
检查是否有 XGameSaveSubmitUpdate 返回的 E_GS_QUOTA_EXCEEDED
错误。
如果你的游戏收到此消息,则通知用户,告知他们需释放一些空间才可保存更多的数据。 向他们提供可用于执行此操作的 UI。 对于每个游戏,每个用户将获取 256 MB 数据。
保存菜单状态以便以后还原
除保存核心游戏数据外,还应保存菜单状态和其他游戏设置。 如果用户运行其他游戏,然后返回到您的游戏,请将其还原为上下文适当的菜单状态。
响应已登录用户的更改
当游戏暂停时,用户可以注销。 当游戏继续时,它应确定已登录的用户组是否发生更改。 发生这种情况时,请考虑导航到游戏中的适当位置(例如菜单)。
在电脑上的存档不安全
在电脑上保存数据不受保护且不安全。 玩家可以找到这些存档并篡改它们。 游戏不应依赖于存储在设备本地的存档中的关键数据。 需要更高级别保护的游戏可以为他们写入的数据实现自己的哈希解决方案,然后在读取这些数据时验证这些哈希。