次の方法で共有


Windows アプリケーションでの XInput の概要

XInput を使用すると、Windows アプリケーションでコントローラーの相互作用を処理できます (コントローラーの振動効果や音声の入力と出力を含む)。

このトピックでは、XInput の機能と、アプリケーションで設定する方法の簡単な概要について説明します。 これには次のものが含まれます。

XInput の概要

アプリケーションは、XInput API を使用して、ゲーム コントローラーが Windows PC に接続されているときに通信できます (一度に最大 4 つの一意のコントローラーを接続できます)。

この API を使用すると、互換性のある接続コントローラーの状態を照会し、振動効果を設定できます。 ヘッドセットが接続されているコントローラーは、音声処理にヘッドセットと共に使用できるサウンド入出力デバイスのクエリも実行できます。

コントローラーのレイアウト

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

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

必要に応じて、ヘッドセットをコントローラーに接続できます。 ヘッドセットには音声入力用のマイクと、サウンド出力用のヘッドフォンがあります。 XInputGetAudioDeviceIds または従来の XInputGetDSoundAudioDeviceGuids 関数を呼び出して、マイクとヘッドフォンのデバイスに対応するデバイス識別子を取得できます。 その後、 コア オーディオ API を 使用して音声入力を受信し、サウンド出力を送信できます。

XInput の使用

XInput の使用は、必要に応じて XInput 関数を呼び出すのと同じくらい簡単です。 XInput 関数を使用すると、コントローラーの状態の取得、ヘッドセットのオーディオ ID の取得、コントローラーの振動効果の設定を行うことができます。

複数のコントローラー

XInput API では、いつでも最大 4 つのコントローラーが接続できます。 XInput 関数はすべて、設定または照会されるコントローラーを識別するために渡される dwUserIndex パラメーターを必要とします。 この ID は 0 から 3 の範囲になり、XInput によって自動的に設定されます。 この番号は、コントローラーが接続されているポートに対応しており、変更できません。

各コントローラーは、コントローラーの中央にある "光のリング" にクアドラントを点灯することによって、使用している ID を表示します。 dwUserIndex 値 0 は、左上のクアドラントに対応します。番号はリングの周りを時計回りの順序で進みます。

アプリケーションでは、複数のコントローラーをサポートする必要があります。

コントローラーの状態の取得

アプリケーションの期間中、コントローラーから状態を取得することは、おそらく最も頻繁に行われます。 ゲーム アプリケーションのフレームからフレームまで、状態を取得し、コントローラーの変更を反映するようにゲーム情報を更新する必要があります。

状態を取得するには、 XInputGetState 関数を使用します。

DWORD dwResult;    
for (DWORD i=0; i< XUSER_MAX_COUNT; 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 の前回の呼び出し以降にコントローラーの状態が変更されたかどうかを確認できます。 dwPacketNumberXInputGetState への 2 つの順次呼び出しの間で変化しない場合、状態は変更されていません。 異なる場合は、XINPUT_STATE構造体のGamepad メンバーを確認して、より詳細な状態情報を取得する必要があります。

パフォーマンス上の理由から、「空」のユーザースロットに対して毎フレームXInputGetStateを呼び出さないでください。 代わりに、新しいコントローラーのチェックを数秒ごとにスペースアウトすることをお勧めします。

デッド ゾーン

ユーザーが一貫したゲームプレイ エクスペリエンスを得るには、ゲームでデッド ゾーンを正しく実装する必要があります。 デッド ゾーンは、アナログサムスティックが手付かずで中央に配置されている場合でも、コントローラーによって報告される "移動" 値です。 2つのアナログトリガにはデッドゾーンもあります。

配信不能ゾーンをまったくフィルター処理しない XInput を使用するゲームでは、ゲームプレイが低下します。 一部のコントローラーは他のコントローラーよりも機密性が高いため、デッド ゾーンはユニットごとに異なる場合があることに注意してください。 異なるシステム上の複数の異なるコントローラーでゲームをテストすることをお勧めします。

アプリケーションでは、アナログ入力 (トリガー、スティック) で "デッド ゾーン" を使用して、スティックまたはトリガーで動作が有効と見なされるタイミングを示す必要があります。

アプリケーションでは、次の例のように、デッド ゾーンを確認し、適切に応答する必要があります。

XINPUT_STATE state = g_Controllers[i].state;

float LX = state.Gamepad.sThumbLX;
float LY = state.Gamepad.sThumbLY;

//determine how far the controller is pushed
float magnitude = sqrt(LX*LX + LY*LY);

//determine the direction the controller is pushed
float normalizedLX = LX / magnitude;
float normalizedLY = LY / magnitude;

float normalizedMagnitude = 0;

//check if the controller is outside a circular dead zone
if (magnitude > INPUT_DEADZONE)
{
    //clip the magnitude at its expected maximum value
    if (magnitude > 32767) magnitude = 32767;

    //adjust magnitude relative to the end of the dead zone
    magnitude -= INPUT_DEADZONE;

    //optionally normalize the magnitude with respect to its expected range
    //giving a magnitude value of 0.0 to 1.0
    normalizedMagnitude = magnitude / (32767 - INPUT_DEADZONE);
}
else //if the controller is in the deadzone zero out the magnitude
{
    magnitude = 0.0;
    normalizedMagnitude = 0.0;
}

//repeat for right thumb stick

次の使用例は、コントローラーの方向ベクトルと、コントローラーがプッシュされたベクトルに沿った距離を計算します。 これにより、コントローラーの大きさがデッドゾーン値より大きいかどうかを確認するだけで、循環デッドゾーンを適用できます。 さらに、コードはコントローラーの大きさを正規化し、ゲーム固有の係数を乗算して、コントローラーの位置をゲームに関連する単位に変換できます。

スティックとトリガー (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] 浮動小数点をスケーリングし (上の例のように)、必要に応じて非線形変換を適用すると便利な場合があります。

たとえば、レースゲームでは、結果を3乗することで、ゲームパッドを使用して車を操作する際の感覚を向上させることができます。結果を3乗すると、低い範囲でより精密な操作が可能になり、これはゲーマーが微妙な動きを得るために軽い力を加えたり、素早い反応を得るために一方向に強い力を加えたりする際に望ましい特性です。

振動効果の設定

コントローラーの状態を取得するだけでなく、コントローラーに振動データを送信して、コントローラーのユーザーに提供されるフィードバックを変更することもできます。 コントローラーには、 XInputSetState 関数に値を渡すことによって独立して制御できる 2 つの振動モーターが含まれています。

各モーターの速度は、次のように 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 );

右モータは高周波モータ、左モータは高周波モータであることに注意してください。 異なる効果を提供するため、常に同じ量に設定する必要はありません。

オーディオ デバイス識別子の取得

コントローラーのヘッドセットには、次の機能があります。

  • マイクを使用してサウンドを録音する
  • ヘッドフォンを使用してサウンドを再生する

ヘッドセットのデバイス識別子を取得するには、次のコードを使用します。

WCHAR renderId[ 256 ] = {0};
WCHAR captureId[ 256 ] = {0};
UINT rcount = 256;
UINT ccount = 256;

XInputGetAudioDeviceIds( i, renderId, &rcount, captureId, &ccount );

デバイス識別子を取得したら、適切なインターフェイスを作成できます。 たとえば、XAudio 2.8 を使用する場合は、次のコードを使用して、このデバイスのマスタリング音声を作成します。

IXAudio2* pXAudio2 = NULL;
HRESULT hr;
if ( FAILED(hr = XAudio2Create( &pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR ) ) )
    return hr;

IXAudio2MasteringVoice* pMasterVoice = NULL;
if ( FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, renderId, NULL, AudioCategory_Communications ) ) )
    return hr;

captureId デバイス識別子の使用方法については、「 ストリームのキャプチャ」を参照してください。

DirectSound GUID の取得 (レガシ DirectX SDK のみ)

コントローラーに接続できるヘッドセットには、マイクを使用してサウンドを録音でき、ヘッドフォンを使用してサウンドを再生するという 2 つの機能があります。 XInput API では、これらの関数は DirectSound を通じて、IDirectSound8IDirectSoundCapture8 インターフェイスを使用して実現されます。

ヘッドセットのマイクとヘッドフォンを適切な DirectSound インターフェイスに関連付けるには、 XInputGetDSoundAudioDeviceGuids を呼び出して、キャプチャおよびレンダリング デバイスの DirectSoundGUID を取得する必要があります。

従来の DirectSound の使用は推奨されず、Windows ストア アプリでは使用できません。 このセクションの情報は、DirectX SDK バージョンの XInput (XInput 1.3) にのみ適用されます。 Windows 8 バージョンの XInput (XInput 1.4) では、 XInputGetAudioDeviceIds を介して取得された Windows Audio Session API (WASAPI) デバイス識別子のみが使用されます。

XInputGetDSoundAudioDeviceGuids( i, &dsRenderGuid, &dsCaptureGuid );

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

// 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;

プログラミング リファレンス