Skip to main content
スキップしてメイン コンテンツへ
XInput と DirectInput

XInput とは、Windows 向け Xbox 360 コントローラーからの入力値を、アプリケーションで受け取れるようにする API です。このドキュメントでは、Xbox 360 コントローラーの XInput と DirectInput の実装との違いを説明し、XInput デバイスとレガシー DirectInput デバイスを同時にサポートする方法を説明します。

新しい規格:XInput

XInput をゲーム開発に使用できるようになりました。この新しい入力規格は、Xbox 360 と Windows XP Service Pack 1 以降の両方、および Windows Vista で使用できます。この API は DirectX SDK で提供されており、ドライバーは Windows Update および Windowsgaming.com で入手できます。

DirectInput よりも XInput を使用する利点はいくつかあります。

  • XInput は使用しやすく、DirectInput ほどのセットアップは必要ありません。

  • Xbox 360 と Windows プログラミングの両方が同じコア API セットを使用しているため、プラットフォーム間を変換するプログラミングがはるかに簡単です。

  • Xbox 360 コントローラーのインストール ベースは大きくなります。

  • XInput デバイス (すなわち Xbox 360 コントローラー) は、XInput API を使用する場合のみバイブレーション機能を持ちます。

  • Xbox 360 コンソール (すなわちハンドル型コントローラー) 向けに将来リリースされるコントローラーは、Windows でも動作します。

DirectInput での Xbox 360 コントローラーの使用

Xbox 360 コントローラーは、DirectInput で適切に列挙され、DirectInput API で使用できます。ただし、以下のように XInput で実現されている一部の機能は、DirectInput では実装されません。

  • 個々に動作していた左右のトリガー ボタンは、1 つのボタンとして動作するようになります。

  • バイブレーション エフェクトは使用できなくなります。

  • ヘッドセット デバイスへのクエリが使用できなくなります。

DirectInput の左右のトリガーの組み合わせは、意図的なものです。ゲームでは、DirectInput デバイスとユーザーとの対話操作がない場合、このデバイスの軸は常に中央に配置されると想定しています。ただし、トリガーが保持されない場合、Xbox 360 コントローラーは中心ではなく最小値を登録するように設計されていました。したがって、以前のゲームはユーザーの対話操作を想定しています。

この解決策とは、トリガーを組み合わせて、あるトリガーを正の方向に設定し、もう 1 つのトリガーを負の方向に設定するというものでした。それによって、DirectInput に制御が中央にあることをユーザーの対話操作で示すことがなくなります。

トリガー値を別々にテストするには、XInput を使用する必要があります。

XInput と DirectInput を並列に使用

XInput のみをサポートするため、ゲームはレガシー DirectInput デバイスで動作しなくなります。XInput はこれらのデバイスを認識しません。

ゲームでレガシー DirectInput デバイスをサポートしたい場合は、DirectInput と XInput を並列に使用できます。DirectInput デバイスを列挙する場合、すべての DirectInput デバイスは正確に列挙します。XInput デバイスは、XInput デバイスと DirectInput デバイスの両方として表示されますが、DirectInput からは処理しないようにします。Dinput デバイスのいずれがレガシー デバイスか、どれが XInput デバイスかを決定し、これらを DirectInput デバイスの列挙から削除する必要があります。

これを行うには、このコードを DirectInput 列挙コールバックに挿入します。

#include <wbemidl.h>#include <oleauto.h>#include <wmsstd.h>//-----------------------------------------------------------------------------// Enum each PNP device using WMI and check each device ID to see if it contains // "IG_" (ex. "VID_045E&PID_028E&IG_00").  If it does, then it's an XInput device// Unfortunately this information can not be found by just using DirectInput //-----------------------------------------------------------------------------BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput ){    IWbemLocator*           pIWbemLocator  = NULL;    IEnumWbemClassObject*   pEnumDevices   = NULL;    IWbemClassObject*       pDevices[20]   = {0};    IWbemServices*          pIWbemServices = NULL;    BSTR                    bstrNamespace  = NULL;    BSTR                    bstrDeviceID   = NULL;    BSTR                    bstrClassName  = NULL;    DWORD                   uReturned      = 0;    bool                    bIsXinputDevice= false;    UINT                    iDevice        = 0;    VARIANT                 var;    HRESULT                 hr;    // CoInit if needed    hr = CoInitialize(NULL);    bool bCleanupCOM = SUCCEEDED(hr);    // Create WMI    hr = CoCreateInstance( __uuidof(WbemLocator),                           NULL,                           CLSCTX_INPROC_SERVER,                           __uuidof(IWbemLocator),                           (LPVOID*) &pIWbemLocator);    if( FAILED(hr) || pIWbemLocator == NULL )        goto LCleanup;    bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup;            bstrClassName = SysAllocString( L"Win32_PNPEntity" );   if( bstrClassName == NULL ) goto LCleanup;            bstrDeviceID  = SysAllocString( L"DeviceID" );          if( bstrDeviceID == NULL )  goto LCleanup;                // Connect to WMI     hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L,                                        0L, NULL, NULL, &pIWbemServices );    if( FAILED(hr) || pIWbemServices == NULL )        goto LCleanup;    // Switch security level to IMPERSONATE.     CoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,                        RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE );                        hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices );     if( FAILED(hr) || pEnumDevices == NULL )        goto LCleanup;    // Loop over all devices    for( ;; )    {        // Get 20 at a time        hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned );        if( FAILED(hr) )            goto LCleanup;        if( uReturned == 0 )            break;        for( iDevice=0; iDevice<uReturned; iDevice++ )        {            // For each device, get its device ID            hr = pDevices[iDevice]->Get( bstrDeviceID, 0L, &var, NULL, NULL );            if( SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL )            {                // Check if the device ID contains "IG_".  If it does, then it's an XInput device				    // This information can not be found from DirectInput                 if( wcsstr( var.bstrVal, L"IG_" ) )                {                    // If it does, then get the VID/PID from var.bstrVal                    DWORD dwPid = 0, dwVid = 0;                    WCHAR* strVid = wcsstr( var.bstrVal, L"VID_" );                    if( strVid && swscanf( strVid, L"VID_%4X", &dwVid ) != 1 )                        dwVid = 0;                    WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" );                    if( strPid && swscanf( strPid, L"PID_%4X", &dwPid ) != 1 )                        dwPid = 0;                    // Compare the VID/PID to the DInput device                    DWORD dwVidPid = MAKELONG( dwVid, dwPid );                    if( dwVidPid == pGuidProductFromDirectInput->Data1 )                    {                        bIsXinputDevice = true;                        goto LCleanup;                    }                }            }               SAFE_RELEASE( pDevices[iDevice] );        }    }LCleanup:    if(bstrNamespace)        SysFreeString(bstrNamespace);    if(bstrDeviceID)        SysFreeString(bstrDeviceID);    if(bstrClassName)        SysFreeString(bstrClassName);    for( iDevice=0; iDevice<20; iDevice++ )        SAFE_RELEASE( pDevices[iDevice] );    SAFE_RELEASE( pEnumDevices );    SAFE_RELEASE( pIWbemLocator );    SAFE_RELEASE( pIWbemServices );    if( bCleanupCOM )        CoUninitialize();    return bIsXinputDevice;}//-----------------------------------------------------------------------------// Name: EnumJoysticksCallback()// Desc: Called once for each enumerated joystick. If we find one, create a//       device interface on it so we can play with it.//-----------------------------------------------------------------------------BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,                                     VOID* pContext ){    HRESULT hr;    if( IsXInputDevice( &pdidInstance->guidProduct ) )        return DIENUM_CONTINUE;	 // Device is verified not XInput, so add it to the list of DInput devices	 return DIENUM_CONTINUE;	}