XInput の基礎知識

XInput とは、Windows 向け Xbox 360 コントローラーからの入力値を、アプリケーションで受け取れるようにする API です。コントローラーの振動エフェクトと音声の入出力がサポートされています。

ここでは、XInput の機能とアプリケーションへのセットアップ方法について概要を示します。これには次のようなトピックが含まれます。

  • XInput の概要
  • XInput の使用

XInput の概要

Xbox 360 コンソールでは、Windows と互換性のあるゲーム コントローラーが使用されます。アプリケーションは、Windows PC に接続されていれば (最大 4 基のコントローラーを同時に接続できます)、XInput API を使ってこれらのコントローラーと通信できます。

この API を使用すると、接続されているすべての Xbox 360 コントローラーがその状態を問い合わせることができ、振動エフェクトを設定できます。ヘッドセットが接続されているコントローラーも、ヘッドセットで音声処理のために使用できる音声入出力デバイスに問い合わせることができます。

オペレーティング システムの要件

XInput は、Windows XP (Service Pack 1) 以上でサポートされてています。

Xbox 360 コントローラー

Xbox 360 コントローラーには、2 つのアナログ方向スティック、それぞれのデジタル ボタン、2 つのアナログ トリガー、4 方向のデジタル方向パッド、および 8 つのデジタル ボタンがあります。XInputGetState 関数を呼び出すと、これらの各入力の状態が XINPUT_GAMEPAD 構造体に返されます。

また、コントローラーには、ユーザーにフォース フィードバック エフェクトを提供する 2 つの振動モーターもあります。これらのモーターの速度は、XINPUT_VIBRATION 構造体で指定されます。これは XInputSetState 関数に渡され、振動エフェクトが設定されます。

オプションで、コントローラーにヘッドセットを接続できます。ヘッドセットには、音声入力のためのマイク、サウンド出力のためのヘッドフォンがあります。XInputGetDSoundAudioDeviceGuids 関数を使用すると、マイクとヘッドフォン用の DirectSound デバイスに対応した GUID を取得できます。また、音声入力を受信でき、DirectSound API を使って音声出力を送信できます。

    Xbox 360 コントローラーのデバイスは、SDK または DirectX 再配布には含まれません。ドライバーは Windows Update または windowsgaming.com から入手できます。

XInput の使用

XInput の使用は、必要に応じて XInput 関数を呼び出すというもので単純です。XIput 関数を使用すると、コントローラーの状態の取得、ヘッドセット DirectSound GUID の取得、およびコントローラーの振動エフェクトの設定が可能です。

複数のコントローラー

XInput API では同時にコントローラーを 4 基まで接続できます。XInput 関数は、すべて dwUserIndex パラメーターを必要とします。このパラメーターは設定されている、または問い合わされているコントローラーを識別するために渡されます。この ID の範囲は 0~3 で、XInput によって自動的に設定されます。この番号は、コントローラーが接続されているポートに対応していて、変更できません。

各コントローラーでは、コントローラーの中央にある "ライトのリング" でクアドラントを点灯することで使用している ID を表示します。dwUserIndex 値 0 は、左上のクアドラントに対応し、リング周囲は時計回りに番号が付けられています。

アプリケーションは複数のコントローラーに対応していなければなりません。複数のコントローラーをサポートする方法の例は、「サンプル」を参照してください。

コントローラー ステートの取得

アプリケーションの動作中には、コントローラーからのステートの取得がおそらく最も頻繁に行われます。ゲーム アプリケーションのフレーム間でステートが取得され、ゲーム情報はコントローラーの変更を反映して更新されます。

ステートを取得するには、XInputGetState 関数を使用します。

DWORD dwResult;    
for (DWORD i=0; i< MAX_CONTROLLERS; i++ )
{
    XINPUT_STATE state;
    ZeroMemory( &state, sizeof(XINPUT_STATE) );

        // Simply get the state of the controller from XInput.
        dwResult = XInputGetState( i, &state );

        if( dwResult == ERROR_SUCCESS )
    { 
        // Controller is connected 
    }
        else
    {
            // Controller is not connected 
    }
}

XInputGetState の戻り値は、コントローラーが接続されているかどうかの判別に使用できます。アプリケーションは構造体を定義して、内部コントローラー情報を保持しなければなりません。この情報は XInputGetState の結果と比較され、ボタンの押下やアナログ コントローラーのデルタなど、そのフレームにどのような変更が行われたかが決定されます。上記の例では、g_Controllers は構造体などを表します。

ステートが XINPUT_STATE 構造体から取得されたら、変更がないかこれをチェックして、コントローラーのステートに関する特定情報を取得できます。

XINPUT_STATE 構造体の dwPacketNumber メンバーを使用すると、XInputGetState を最後に呼び出した以降、コントローラーのステートが変更されているかどうかを確認できます。XInputGetState の連続する 2 回の呼び出しの間で dwPacketNumber に変化がない場合、ステートは変更されていません。変化がある場合、アプリケーションは XINPUT_STATE 構造体の Gamepad メンバーをチェックして、より詳細なステート情報を取得します。

デッド ゾーン

ユーザーの一貫性のあるゲームプレイ体験を実現するには、ゲームにデッド ゾーンを正確に実装しなければなりません。デッド ゾーンは、アナログ スティックに触っていないときや、ステックが中央にある場合でも、コントローラーが報告する "動作" 値です。これは、2 つのアナログ トリガーのデッド ゾーンでもあります。

    デッド ゾーンをまったくフィルターしない XInput を使用するゲームは、満足のいくゲームプレイ体験を提供できません。コントローラーによっては、特別に感度が高いものもあるため、デッド ゾーンはユニットごとに変わることがあります。さまざまなシステムでいくつかの Xbox 360 コントローラーを使ってゲームをテストすることをお勧めします。

アプリケーションは、"デッド ゾーン" をアナログ入力 (トリガー、スティック) で使用して、スティックまたはトリガーで有効と見なされる動作が十分に行われたのがいつかを示さなければなりません。

アプリケーションは、次の例に示すとおり、デッド ゾーンをチェックして、適切に応答する必要があります。

// Zero value if thumbsticks are within the dead zone 
if( (state.Gamepad.sThumbLX < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE && 
     state.Gamepad.sThumbLX > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) && 
    (state.Gamepad.sThumbLY < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE && 
     state.Gamepad.sThumbLY > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) )
{   
   state.Gamepad.sThumbLX = 0;
   state.Gamepad.sThumbLY = 0;
}

if( (state.Gamepad.sThumbRX < XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE && 
     state.Gamepad.sThumbRX > -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) && 
    (state.Gamepad.sThumbRY < XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE && 
state.Gamepad.sThumbRY > -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) ) 
{
   state.Gamepad.sThumbRX = 0;
   state.Gamepad.sThumbRY = 0;
}

スティックとトリガーには独自のデッド ゾーン (0 ~ 65534 のいずれか) を定義できます。または、XInput.h に XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE、XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE、および XINPUT_GAMEPAD_TRIGGER_THRESHOLD として定義されているデッド ゾーンを使用することもできます。

#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE  7849
#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD    30

デッドゾーンが適用されると、これが結果範囲の [0.0..1.0] 浮動小数点をスケーリングし、オプションで非線形トランスフォームを適用するのに便利なことがわかります。

たとえば、ドライビング ゲームでは、ゲームパッドを使った運転により臨場感を与えるために、結果をキューブにすると有益なことがあります。結果をキューブにすると、低い範囲をより細かくすることができ、これは好ましいといえます。なぜなら、通常、ゲーマーはソフト フォースを適用して微妙な動きを得るか、一方向にハード フォースを適用して迅速な応答を得るためです。

振動エフェクトの設定

コントローラーのステートを取得するのに加えて、コントローラーに振動データを送信して、コントローラーのユーザーに提供されるフィードバックを変更することもできます。コントローラーには、2 つの振動モーターが搭載されています。これは XInputSetState 関数に値を渡すことで独立して制御できます。

各モーターの速度は、次のように XInputSetState 関数に渡される XINPUT_VIBRATION 構造体の WORD 値を使って指定できます。

XINPUT_VIBRATION vibration;
ZeroMemory( &vibration, sizeof(XINPUT_VIBRATION) );
vibration.wLeftMotorSpeed = 32000; // use any value between 0-65535 here
vibration.wRightMotorSpeed = 16000; // use any value between 0-65535 here
XInputSetState( i, &vibration );

右のモーターは高周波モーターで、左のモーターは低周波モーターです。これらのモーターは、同じ量に設定する必要はありません。これらのモーターが提供するエフェクトは異なるためです。

DirectSound GUID の取得

Xbox 360 コントローラーに接続できるヘッドセットには、2 つの機能があります。マイクを使ってサウンドを録音する機能、そしてヘッドフォンを使ってサウンドを再生する機能です。XInput API では、これらの機能は IDirectSound8 インターフェイスと IDirectSoundCapture8 インターフェイスを使って DirectSound から達成されます。

ヘッドセットのマイクとヘッドフォンをそれらの適切な DirectSound インターフェイスに関連付けるには、キャプチャーの DirectSound GUID を取得して、XInputGetDSoundAudioDeviceGuids を呼び出すことでデバイスをレンダリングしなければなりません。

XInputGetDSoundAudioDeviceGuids( i, &dsRenderGuid, &dsCaptureGuid );

GUID を取得したら、次のように DirectSoundCreate8DirectSoundCaptureCreate8 を呼び出すことで適切なインターフェイスを作成できます。

// Create IDirectSound8 using the controller's render device
if( FAILED( hr = DirectSoundCreate8( &dsRenderGuid, &pDS, NULL ) ) )
   return hr;

// Set coop level to DSSCL_PRIORITY
if( FAILED( hr = pDS->SetCooperativeLevel( hWnd, DSSCL_NORMAL ) ) )
   return hr;

// Create IDirectSoundCapture using the controller's capture device
if( FAILED( hr = DirectSoundCaptureCreate8( &dsCaptureGuid, &pDSCapture, NULL ) ) )
   return hr;

関連項目

XInput のリファレンス | XInput の基礎知識 | DirectSound のリファレンス