GameInput 读取
从每个设备接收的原始输入数据包封装在“读取”对象中。 读取包含最初的原始数据包数据,并且(通常)还包含这些原始数据转换为更高级格式的一个或多个转换。 除了其作为数据容器的角色之外,读取还充当在输入流内引用某一特定位置的标识符。
获取读取
GameInput API 提供两种方法来获取读取。 最常见的方法是通过 IGameInput 接口上的方法直接从输入流访问它们。
HRESULT GetCurrentReading(
_In_ GameInputKind inputKind,
_In_opt_ IGameInputDevice * device,
_COM_Outptr_ IGameInputReading ** reading);
调用 GetCurrentReading 将从输入流检索最新的读取。 可传递一个可选 GameInputKind 筛选器,将返回的读取限制为特定类型的输入,如游戏手柄或键盘。 还可以传递一个可选 IGameInputDevice 筛选器,将返回的读取限制为仅由指定设备生成的读取。 这些筛选器可单独应用,也可以一起应用。
IGameInputReading 实例是引用计数的单一实例。 检索读取是一种非常快速且轻量级的操作 - 不会执行内存分配或复制,并且 API 调用是无锁的,且没有内核模式转换。 由于读取是单一实例,因此应用程序可以比较读取指针的相等性,以判断 GetCurrentReading 的两个调用是否返回了相同读取(这意味着没有生成新的输入)。
具有较为简单的输入需要的游戏可能只是每个帧轮询新输入一次,并且比较两个读取中存储的状态的差异(如果它们并非相同读取)。 但是,具有较为复杂的输入需要的游戏可能想要访问输入流,以便获取自上一个帧后已发生的所有输入状态变化的全貌。 这是通过 GetNextReading 和 GetPreviousReading 方法实现的,允许使用与 GetCurrentReading 相同的筛选器。 输入流在其缓冲区中维护上半秒的历史读取。
HRESULT GetNextReading(
_In_ IGameInputReading * referenceReading,
_In_ GameInputKind inputKind,
_In_opt_ IGameInputDevice * device,
_COM_Outptr_ IGameInputReading ** reading);
HRESULT GetPreviousReading(
_In_ IGameInputReading * referenceReading,
_In_ GameInputKind inputKind,
_In_opt_ IGameInputDevice * device,
_COM_Outptr_ IGameInputReading ** reading);
或者,应用程序可以注册只要生成了输入就调用的回调。 与上述同步方法相似,可应用若干筛选器以便控制返回的读取类型以及从哪些设备返回。 有关详细信息,请参阅“高级 GameInput 主题”部分中的 GameInput 回调。
从读数获取数据
尽管每个读取都包含来自设备的原始输入数据包数据,但读取通常还包含这些数据的一个或多个高级转换。 例如,从游戏板接收的输入还分析为具有已知按钮和摇杆标识符的标准固定格式结构。 读取通常包含相同原始输入数据的不同表示形式,允许应用程序选择最适合其需要的格式。
应用程序可以通过调用其 GetInputKind 方法,查询读取中包含的数据类型。 这样将从 GameInputKind 枚举返回一个或多个标志值。
typedef enum GameInputKind
{
GameInputKindUnknown = 0x00000000,
GameInputKindRawDeviceReport = 0x00000001,
GameInputKindController = 0x00000002,
GameInputKindKeyboard = 0x00000004,
GameInputKindMouse = 0x00000008,
GameInputKindTouch = 0x00000100,
GameInputKindMotion = 0x00001000,
GameInputKindArcadeStick = 0x00010000,
GameInputKindFlightStick = 0x00020000,
GameInputKindGamepad = 0x00040000,
GameInputKindRacingWheel = 0x00080000,
GameInputKindUiNavigation = 0x01000000
} GameInputKind;
可用于读取的数据类型取决于输入设备及其物理属性。 例如,来自标准键盘的读取可能仅包含键盘数据,而来自具有集成的轨迹球的键盘的读取可能包含键盘数据和鼠标数据。
几乎所有游戏控制器都会生成包含一般“控制器”数据的读取,这只是匿名轴和按钮状态的集合。 这为具有输入映射 UI 的应用程序实现了广泛的设备支持。 但是,许多游戏控制器(如游戏板)还在其读取中公开熟悉的固定格式状态,这极大方便了典型游戏的使用。
typedef struct GameInputGamepadState
{
GameInputGamepadButtons buttons;
float leftTrigger;
float rightTrigger;
float leftThumbstickX;
float leftThumbstickY;
float rightThumbstickX;
float rightThumbstickY;
} GameInputGamepadState;
IGameInputReading 接口包含用于以读取所支持的任何格式检索状态的方法。 可用于读取的所有不同的表示形式都是预先计算的,因此,这些方法只是复制少量数据字节并返回。
简单游戏板输入循环
以下示例代码是一个充分发挥功能的游戏板输入循环的示例。 与此示例有关的一个注意事项没有显式设备枚举。 IGameInputDevice 的唯一用途是作为设备标识符。 未曾调用其任何方法。 这阐释了 GameInput API 的以输入为中心的特性,以及它是如何简化针对常用输入情形的代码的。
IGameInput* g_gameInput = nullptr;
IGameInputDevice* g_gamepad = nullptr;
HRESULT InitializeInput()
{
return GameInputCreate(&g_gameInput);
}
void ShutdownInput()
{
if (g_gamepad) g_gamepad->Release();
if (g_gameInput) g_gameInput->Release();
}
void PollGamepadInput()
{
// Ask for the latest reading from devices that provide fixed-format
// gamepad state. If a device has been assigned to g_gamepad, filter
// readings to just the ones coming from that device. Otherwise, if
// g_gamepad is null, it will allow readings from any device.
IGameInputReading * reading;
if (SUCCEEDED(g_gameInput->GetCurrentReading(GameInputKindGamepad, g_gamepad, &reading)))
{
// If no device has been assigned to g_gamepad yet, set it
// to the first device we receive input from. (This must be
// the one the player is using because it's generating input.)
if (!g_gamepad) reading->GetDevice(&g_gamepad);
// Retrieve the fixed-format gamepad state from the reading.
GameInputGamepadState state;
reading->GetGamepadState(&state);
reading->Release();
// Application-specific code to process the gamepad state goes here.
}
// If an error is returned from GetCurrentReading(), it means the
// gamepad we were reading from has disconnected. Reset the
// device pointer, and go back to looking for an active gamepad.
else if (g_gamepad)
{
g_gamepad->Release();
g_gamepad = nullptr;
}
}