> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# GDK 与 Steamworks 之间的概念差异

> 对比 GDK 的 C 风格 API 与 Steamworks 的接口单例在代码结构、状态管理以及作品运行方式上的差异,便于从 Steam 移植作品到 XBOX。

Steamworks 与 XBOX Game Development Kit(GDK)在结构、API 模式与用途上存在若干差异。本主题概述这些差异。

## XBOX Game Development Kit(GDK)中没有 API 单例

Steamworks 采用一种模式:每个 API 功能集被定义为一个接口(例如,所有用户统计函数都在 `ISteamUserStats` 接口中,远程存储在 `ISteamRemoteStorage` 中),API 被游戏代码初始化后,其实例即可使用。游戏启动时初始化的 Steamworks API 单例提供这些函数,并在应用整个生命周期内跟踪状态。

XBOX Game Development Kit(GDK)API 则不同,它倾向于 C 风格 API,大部分上下文与当前 API 数据状态必须由游戏自行保管,并传入各种 API 函数。例如,在 XBOX 服务中对用户进行身份验证后,游戏必须自行保留 XBOX 服务上下文句柄和用户句柄。用于获取这些信息的 API 不会像 Steamworks 那样在游戏的整个生命周期内“记住”它们。因此,你需要在游戏类中添加成员变量,或以其他方式在游戏中跟踪这些句柄。

## 异步函数与回调

Steamworks 中的异步函数会触发事件,你可以通过 `STEAM_CALLBACK` 宏或 `CCallResult` 变量订阅。当与指定事件结构体类型对应的事件被触发时,指定的方法会以一个事件结构体作为唯一参数被调用。该结构体包含 API 返回的结果,以及处理调用结果所需的上下文信息。例如,调用 `ISteamUserStats::DownloadLeaderboardEntries` 时,你在回调函数中需要一个 `SteamLeaderboard_t` 句柄以传给 `ISteamUserStats::DownloadLeaderboardEntries`,该句柄可以作为传入回调方法的 `LeaderboardScoresDownloaded_t` 结构体的成员获取。

XBOX Game Development Kit(GDK)中,所有异步操作遵循另一种模式:你创建一个 [`XAsyncBlock`](/reference/system/xasync/xasync_members) 结构体,可选地将其分配给任务队列,然后调用异步 API 方法。API 调用完成后,会触发 [`XAsyncBlock`](/reference/system/xasync/xasync_members) 中定义的回调函数,并以异步块指针作为唯一参数。如果需要在回调函数中访问信息,可以使用 async block 的 context 指针成员。与 Steam 不同,该 context 结构体中的信息不会自动填充,你需要自行构建。沿用之前的示例,如果你需要在回调函数中访问一组数据,可以按以下代码示例操作。

```cpp theme={null}
MyClass::SimpleContextExample()
{
    // ...
    struct MyContext 
    {
        MyHandle_t handle;
        std::shared_ptr<MyClass> instance;
    }

    // Assume myHandle is a variable containing the handle you'll need in your call function.
    auto contextPtr = std::unique_ptr<MyContext>(new MyContext{ myHandle, shared_from_this() });
    XAsyncBlock async = std::make_unique<XAsyncBlock>();
    async->context = contextPtr.get(); 
    async->callback = [](XAsyncBlock *async)
    {
        std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*        
        std::unique_ptr<MyContext> contextPtr{ static_cast<MyContext*>(asyncBlock->context) }; // Take over ownership of the context.        

        auto handle = contextPtr->handle;
        // You can now use your handle for other API calls.
    };

    HRESULT hr = SomeAsyncFunction(arg1, arg2, asyncBlock.get());
    if (SUCCEEDED(hr))
    {
        // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* and context* 
        // since the callback will take over ownership.
        // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* and context block.
        asyncBlock.release();
        contextPtr.release();
    }
}
```

<Note>
  上述示例通过 `shared_from_this()` 传入一个指向 `this` 的指针,使回调函数能够在需要时调用实例方法或访问成员变量。请谨慎传递原始指针,以避免生命周期问题。另请注意,如果你只需要在回调中访问一个变量,可以直接将 async block 的 `context` 指针设置为该变量的地址,然后在回调中转换为该变量的类型,而无需定义结构体。
</Note>

Steamworks 还要求游戏代码通过 `SteamAPI_RunCallbacks` 函数按一定间隔触发回调。XBOX Game Development Kit(GDK)不需要这样。如果使用线程池任务队列(默认情况),异步任务由其任务队列 dispatch 完成后,回调会被自动触发。

如需更多控制,你可以在使用手动任务队列时通过调用 `XTaskQueueDispatch` 手动将异步任务分派到指定线程。这种方式更复杂,具体细节超出本指南范围。有关 XBOX Game Development Kit(GDK)如何处理异步 API 操作的更多信息(含代码示例),参见 [异步编程模型](/build/core-features/common/async/async-programming-model)。

## 作品管理型 API 的“真源”

在 Steamworks 中,API 是几乎所有相关值的唯一真源。例如,用户统计从 API 获取,并基于 `ISteamUserStats::GetStat`/`ISteamUserStats::SetStat` 的值计算,你不需要自行在其他地方存储这些值。

XBOX Game Development Kit(GDK)中的成就与 stats/leaderboards API 都提供了 title-managed(作品管理)选项,便于开发者以更灵活、更简单的方式进行调用。顾名思义,作品管理型 API 的值由你的作品管理。这些值的唯一真源是你的游戏,可以将它们存储在任何位置 —— 存档文件、云存储或第三方后端。存储在 XBOX 网络(也称为 XBOX Live)服务器上的值可以作为一个快照,你偶尔更新它,但在运行时它不应作为你的唯一真源。

作品管理型 stats/成就的另一种选择是基于事件的 stats/成就。它们通过遥测事件更新用户的成就进度或重新计算 stats,并将 XBOX 服务视为真源。你可以在 Partner Center 中游戏的 **Gameplay Setting** 页面选择 stats 与成就使用哪种 API。

有关更多信息,参见比较 [基于事件与作品管理型 Stats](/services/xbox-services/player-data/stats-leaderboards/index) 与 [基于事件与作品管理型 Achievements](/services/xbox-services/player-data/achievements/index) 的主题。有时你可能会看到这些 API 使用它们此前的名称 *Stats/Achievements 2013(基于事件)* 与 *Stats/Achievements 2017(作品管理)*。

## XBOX Game Development Kit(GDK)是多平台的

使用 Steamworks API 的游戏可以假定它们总是通过 Steam 启动,因此 API 可以在初始化时注入一些上下文,指明当前的玩家是谁。使用 XBOX Game Development Kit(GDK)的游戏可以在 XBOX 主机上运行、通过 XBOX Gaming App 在 PC 上运行,或在任意设备与启动器上运行。

因此,Steam 自动提供的一些上下文,在 XBOX Game Development Kit(GDK)中可能需要手动初始化,例如用户身份。要获取这些信息,你可以按本指南中的 [初始化 GDK](/build/steam-porting-guide/initializing-the-gdk) 主题里的步骤进行,其中包含使用 Microsoft 账号/gamertag 对用户进行身份验证的说明。这也意味着 XBOX Game Development Kit(GDK)函数中存在一些为主机场景定制的模式,例如 `XUser` API 中对多用户登录的支持,这些在 PC 上并没有对应物。

## 打包

大多数 Steam 上的游戏只需在 Steamworks 管理门户中设置游戏信息、下载 SDK、导入所需文件并在代码中初始化 API,就可以使用 Steamworks API 函数。完成上述步骤后,你的游戏就能从任何启动位置与 Steamworks API 集成,例如游戏引擎编辑器。

要让 XBOX Game Development Kit(GDK)的 API 生效,必须首先打包你的游戏。要正确打包游戏,你需要编辑游戏的 `MicrosoftGame.config` 文件,并使用 XBOX Game Development Kit(GDK)的 `MakePkg` 工具为游戏创建 MSIXVC 包,然后就可以在启用了开发者模式的 PC 上以 Microsoft Store 应用的方式旁加载。

这也意味着与 Steamworks 游戏不同,XBOX Game Development Kit(GDK)的部分功能在未先打包游戏时,不能在游戏引擎编辑器或其他开发时环境中运行。

关于打包的更多信息,参见以下资源。

* [PC 打包入门](/build/core-features/common/packaging/overviews/packaging-getting-started-for-PC)
* [MicrosoftGame.config](/build/core-features/common/game-config/MicrosoftGameConfig-Overview)

## 开发与测试 Unity 游戏

使用 Unity 游戏引擎构建的作品可以使用 GDK Unity Plug-in 开发。此插件包含在 XBOX Game Development Kit(GDK)交付物中,也可作为独立附加组件在 [XBOX Developer Downloads](https://aka.ms/gdkdl) 门户获取。此插件包含 API 包装器,让你可以在游戏的 C# 代码中调用 XBOX Game Development Kit(GDK)函数。

有关 GDK Unity Plug-in 的更多信息,参见 [面向 PC 开发开始使用 Unity](/paths/unity/overview)。


## Related topics

- [Steam 移植指南概览](/zh-CN/build/steam-porting-guide/overview.md)
- [GDK 与 XDK 之间的 Web 请求差异](/zh-CN/build/console-features/networking/xdk-migration/xdk-migration-web-requests-networking.md)
- [从 Steam 移植](/zh-CN/paths/porting/from-steam.md)
- [概念](/zh-CN/services/xbox-services/multiplayer/invites/concepts/index.md)
- [游戏网状通信：GDK 与 XDK 的差异](/zh-CN/build/console-features/networking/xdk-migration/xdk-migration-game-mesh-networking.md)
