- 向基于较早 API 版本(例如 v0)构建的应用程序添加 v4 功能,而无需一次性迁移整个应用程序。
- 调用针对较早版本 GameInput 构建的 SDK、中间件或引擎插件,即使应用程序的其余部分使用 v4。
支持的 API 版本
GameInput 运行时支持低于或等于其自身主版本号的每个 API 版本。例如,安装 GameInput 4.0 运行时后,每个 GameInput 对象都会实现 v0、v1、v2、v3 和 v4 接口,你的应用程序可以使用它们的任意组合。
如果你请求的 API 版本比已安装的运行时更新,
QueryInterface 和 GameInputInitialize 会失败并返回 E_NOINTERFACE。应用程序使用的最高 API 版本决定了它所需的最低运行时版本。
在 PC 上,已安装的运行时是 GameInput 可再发行组件或你随游戏部署的运行时。有关详细信息,请参阅 GameInput NuGet 包和 GameInput 旁加载。在 XBOX 上,运行时包含在 GDK 中,你也可以随游戏部署更新的运行时。有关详细信息,请参阅 XBOX 主机上的 GameInput。
设置项目
对于新的实现,请使用最新 Microsoft.GameInput NuGet 包中的头文件和GameInput.lib。在该包中,<GameInput.h> 是最新的头文件,每个较早的头文件都位于带版本号的文件夹中,例如 <v0/GameInput.h> 或 <v2/GameInput.h>。该库导出 GameInputInitialize,它会为你请求的任何 API 版本创建 GameInput。
较早版本中的库(例如 GDK 中的库)也仍可正常工作。有关详细信息,请参阅保留较早的库。
包含 v0 头文件
从 v1 开始,每个 GameInput 头文件都在带版本号的命名空间(例如GameInput::v4)中声明其类型。而 v0 头文件在全局命名空间中声明其类型。若要将 v0 头文件与最新头文件一起使用,请将以下样板代码放入你自己的头文件中,例如 GameInputV0.h:
- 它首先包含最新的
<GameInput.h>。这会在其自身的命名空间中声明最新的 API,并包含 v0 头文件所依赖的 Windows 和 COM 头文件,因此 v0 头文件不会在命名空间内再次包含它们。 - 前向声明使 v0 头文件中的 typedef 在
GameInput::v0中声明新接口,而不是引用最新头文件已声明的接口。 - 在命名空间内包含
<v0/GameInput.h>会将每个 v0 类型、枚举和常量移入GameInput::v0,例如GameInput::v0::IGameInput和GameInput::v0::GameInputKindGamepad。
GameInputV0.h 而不是 <GameInput.h>,并添加 using namespace GameInput::v0;,以便现有代码可以继续使用非限定名称。任何包含 GameInputV0.h 的源文件也可以通过完整名称使用最新的 API,例如 GameInput::v4::IGameInput。
示例:向 v0 应用程序添加 v4 功能
你无需重写 v0 代码即可开始使用 v4 功能。将 v0 代码保留在其自己的源文件中,并将新的 v4 代码放入单独的源文件中。在两者之间以IUnknown* 形式传递 GameInput 对象,并让每一方针对其所需的版本调用 QueryInterface。
现有的 v0 代码
以下 v0 代码创建 GameInput 并读取游戏手柄。与原始 v0 代码相比,只有三处更改:- 它包含
GameInputV0.h而不是<GameInput.h>。 - 它通过使用 v0 IID 调用
GameInputInitialize来创建 GameInput,因为最新的库不导出 v0GameInputCreate函数。 - 按下 A 按钮时,它会调用新的 v4 代码。
新的 v4 代码
新的源文件包含 v4 头文件,并对 v0 代码传入的设备调用QueryInterface。然后,它使用 v4 中新增的 IGameInputDevice::SetLightingColor 和 IGameInputDevice::ApplyLightingColors 更改控制器中心灯的颜色。如果设备没有中心灯,则会忽略该调用。
QueryInterface 失败,则表示已安装的运行时不支持 v4。你的 v0 代码会继续正常工作,应用程序可以跳过 v4 功能。
当所有代码都使用 v4 时,你可以删除 GameInputV0.h 和 QueryInterface 调用。
保留较早的库
如果你继续链接较早版本中的库(例如 GDK 中的库),则现有的 v0 代码完全无需更改。继续包含该版本中的 v0 头文件并调用GameInputCreate 即可。该调用会转发给已安装的运行时,因此当运行时为 4.0 或更高版本时,它返回的对象也会实现 v4 接口,前面所示的新 v4 代码无需更改即可正常工作。
较早的库不导出 GameInputInitialize。请仅通过调用 QueryInterface 获取 v4 接口,并且不要调用 v4 GameInputCreate,因为它会调用 GameInputInitialize。
在版本之间切换的规则
不同 API 版本中的接口具有相同的名称(例如IGameInput 和 IGameInputDevice),但它们是具有不同方法的不同类型。指向 v0 接口的指针并不是指向 v4 接口的指针,即使两者引用的是同一个对象。
强制转换与调用
QueryInterface 不同。始终使用 QueryInterface 获取其他 API 版本的接口。切勿使用 static_cast、reinterpret_cast 或 C 样式强制转换在版本之间进行转换。强制转换可以通过编译,但通过转换后的指针调用方法可能会调用错误的方法,从而导致未定义的行为。- 不要在 v0 头文件之后包含其他 GameInput 头文件。 v0 头文件将其错误代码(例如
GAMEINPUT_E_DEVICE_DISCONNECTED)定义为宏,这些宏会破坏在其之后包含的任何 GameInput 头文件中的声明。GameInputV0.h通过首先包含最新头文件来遵循此规则。 - 返回的对象使用你所调用的版本。 例如,对 v4 读取调用
GetDevice会返回 v4 设备,而通过 v4IGameInput注册的设备回调会收到 v4 设备。在将这些对象传递给使用其他版本的代码之前,请先调用QueryInterface。 - 每次调用
QueryInterface都会添加一个引用。 与任何其他 COM 接口指针一样,不再需要返回的指针时请将其释放。 - 检查结果。 当你请求较新的 API 版本时,失败意味着已安装的运行时不支持该版本。
GameInputV0.h中的 v0 类型是单独的 C++ 类型。GameInput::v0::IGameInput与单独使用<v0/GameInput.h>时的全局IGameInput描述的是同一个接口,但 C++ 将它们视为不同的类型。如果现有代码或其他库使用全局 v0 类型声明函数,请从仅包含<v0/GameInput.h>的源文件中调用这些函数。
调用使用较早 API 版本的 SDK
某些 SDK、中间件和引擎插件是针对特定 GameInput API 版本构建的,并要求使用该版本的 GameInput 接口。你的应用程序可以使用较新的版本,同时仍然调用这些组件。调用QueryInterface 获取组件所需的版本,然后传递该指针。
请将该调用放在包含与组件所用相同 GameInput 头文件的源文件中。在以下示例中,ThirdPartySdk.h 声明了 ThirdPartySdkInitialize(GameInput::v2::IGameInput*),应用程序的其余部分以 IUnknown* 形式传递其 GameInput 对象:
QueryInterface 以获取 v4 接口。
由于每个版本都由同一个对象实现,因此 SDK 和你的应用程序共享设备、读取和状态。SDK 的 API 版本也必须低于或等于已安装的运行时版本。
另请参阅
GameInput API 版本管理GameInput NuGet 包
GameInput 旁加载
GameInputInitialize
GameInput API 参考
