GameInput の読み取り値
すべてのデバイスから受信した未加工入力パケットは、"読み取り値" オブジェクトにカプセル化されます。 読み取り値には、元の未加工パケット データと、(通常は) 未加工データの上位レベルの形式への変換が 1 つ以上含まれます。 読み取り値は、データ コンテナーとしての役割に加えて、入力ストリーム内の特定の位置を参照する識別子としても機能します。
読み取り値の取得
GameInput API では、2 つの方法で読み取り値を取得できます。 最も一般的な方法は、IGameInput インターフェイスのメソッドを使用して、入力ストリームに含まれる読み取り値に直接アクセスすることです。
HRESULT GetCurrentReading(
_In_ GameInputKind inputKind,
_In_opt_ IGameInputDevice * device,
_COM_Outptr_ IGameInputReading ** reading);
GetCurrentReading を呼び出して、入力ストリームから最新の読み取り値を取得します。 返される読み取り値を特定の種類の入力 (ゲームパッドやキーボードなど) に制限するには、オプションの GameInputKind フィルターを渡します。 さらに、オプションの IGameInputDevice フィルターを渡して、返される読み取り値を指定のデバイスで生成されたものだけに制限することもできます。 これらのフィルターは、個別または一緒に適用できます。
IGameInputReading インスタンスは、参照カウント方式のシングルトンです。 読み取り値の取得は、非常に高速で軽量な操作です。メモリの割り当てやコピーは実行されず、API の呼び出しはロックフリーであり、カーネル モードの遷移はありません。 読み取り値はシングルトンであるため、アプリケーションでは読み取りポインターを比較して同じであるかを調べることにより、GetCurrentReading の 2 つの呼び出しが同じ読み取り値を返したかどうか (つまり新しい入力値が生成されていないこと) を判断できます。
それほど複雑でない入力のニーズを持つゲームは、単にフレームごとに 1 回新しい入力をポーリングして、2 つの読み取り値 (それらが同じ読み取り値ではない場合) に格納されている状態に差がないかどうかを調べることができます。 しかし、より複雑な入力のニーズを持つゲームは、入力ストリーム内を調べて、前のフレーム以降に発生したすべての入力状態の変化を完全に把握する必要があります。 それには、GetNextReading メソッドと GetPreviousReading メソッドを使用します。これらのメソッドでも GetCurrentReading と同じフィルターを使用できます。 入力ストリームは、そのバッファー内に、直近の 0.5 秒以内に発生した過去の読み取り値を保持します。
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 のコールバック」を参照してください。
読み取り値からのデータの取得
すべての読み取り値にはデバイスからの未加工入力パケット データが含まれますが、通常は、そのデータの上位レベルへの変換も 1 つ以上含まれます。 たとえば、ゲームパッドから受け取った入力は、よく知られたボタンやサム スティックの識別子を使用する標準的な固定形式の構造にも解析されます。 読み取り値には、多くの場合、同じ未加工入力データのいくつかの異なる表現が含まれているため、アプリケーションはニーズに最も合った形式を選択できます。
読み取り値に含まれるデータの種類をクエリするには、アプリケーションで GetInputKind メソッドを呼び出します。 これにより、GameInputKind 列挙型に含まれる 1 つ以上のフラグ値が返されます。
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 インターフェイスには、読み取り値でサポートされているいずれかの形式で状態を取得するためのメソッドが含まれています。 読み取り値で使用できるさまざまな表現はすべて事前に計算されているため、これらのメソッドは単に数バイトのデータをコピーして返すだけです。
単純なゲームパッド入力ループ
次のサンプル コードは、完全に機能するゲームパッドの入力ループの一例です。 このサンプルに関して注目すべき 1 つの点は、明示的なデバイスの列挙がないことです。 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;
}
}