XInput 機能と DirectInput 機能の比較
大事な
Microsoft Game Development Kit (GDK)を介して PC と Xbox でサポートされる次世代入力 API の詳細については、GameInput API を参照してください。
このドキュメントでは、コントローラー入力の XInput と DirectInput の実装と、XInput デバイスとレガシ DirectInput デバイスの両方をサポートする方法を比較します。
Windows ストア アプリでは、DirectInput はサポートされていません。
概要
XInput を使用すると、アプリケーションは XUSB コントローラーから入力を受信できます。 API は DirectX SDK を通じて利用でき、ドライバーは Windows Update から入手できます。
DirectInput よりも XInput使用する利点はいくつかあります。
- XInput は使いやすく、DirectInput よりもセットアップが少なくて済みます
- Xbox と Windows の両方のプログラミングで同じコア API のセットが使用されるため、プログラミングでクロスプラットフォームをはるかに簡単に翻訳できます
- コントローラーの大きなベースがインストールされます
- XInput デバイスには、XInput API を使用する場合にのみ振動機能があります
DirectInput での XUSB コントローラーの使用
XUSB コントローラーは、DirectInput 適切に列挙され、DirectInputAPIs と共に使用できます。 ただし、XInput によって提供される一部の機能は、DirectInput の実装には存在しません。
- 左右のトリガー ボタンは、独立してではなく、1 つのボタンとして機能します
- 振動効果は使用できません
- ヘッドセット デバイスのクエリは使用できません
DirectInput の左トリガーと右トリガーの組み合わせは仕様です。 ゲームでは、ユーザーがデバイスを操作できない場合、DirectInput デバイス軸が中央揃えであると常に想定されています。 ただし、新しいコントローラーは、トリガーが保持されていないときに、中央ではなく最小値を登録するように設計されました。 そのため、古いゲームではユーザーの操作が想定されます。
解決策は、トリガーを組み合わせ、一方のトリガーを正の方向に設定し、もう一方を負の方向に設定するため、"コントロール" が中央にある directInput を するユーザーの操作は示されません。
トリガー値を個別にテストするには、XInput を使用する必要があります。
XInput と DirectInput のサイド バイ サイド
XInput のみをサポートすることで、ゲームはレガシ DirectInput デバイスでは動作しません。 XInput はこれらのデバイスを認識しません。
ゲームでレガシ DirectInput デバイスをサポートする場合は、DirectInput と XInput をサイド バイ サイドで使用できます。 DirectInput デバイスを列挙すると、すべての DirectInput デバイスが正しく列挙されます。 すべての XInput デバイスは XInput デバイスと DirectInput デバイスの両方として表示されますが、DirectInput では処理しないでください。 どの DirectInput デバイスがレガシ デバイスで、どのデバイスが XInput デバイスであるかを判断し、DirectInput デバイスの列挙体から削除する必要があります。
これを行うには、DirectInput 列挙コールバックに次のコードを挿入します。
#include <wbemidl.h>
#include <oleauto.h>
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p) = nullptr; } }
#endif
//-----------------------------------------------------------------------------
// Enum each PNP device using WMI and check each device ID to see if it contains
// "IG_" (ex. "VID_0000&PID_0000&IG_00"). If it does, then it's an XInput device
// Unfortunately this information cannot be found by just using DirectInput
//-----------------------------------------------------------------------------
BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput )
{
IWbemLocator* pIWbemLocator = nullptr;
IEnumWbemClassObject* pEnumDevices = nullptr;
IWbemClassObject* pDevices[20] = {};
IWbemServices* pIWbemServices = nullptr;
BSTR bstrNamespace = nullptr;
BSTR bstrDeviceID = nullptr;
BSTR bstrClassName = nullptr;
bool bIsXinputDevice = false;
// CoInit if needed
HRESULT hr = CoInitialize(nullptr);
bool bCleanupCOM = SUCCEEDED(hr);
// So we can call VariantClear() later, even if we never had a successful IWbemClassObject::Get().
VARIANT var = {};
VariantInit(&var);
// Create WMI
hr = CoCreateInstance(__uuidof(WbemLocator),
nullptr,
CLSCTX_INPROC_SERVER,
__uuidof(IWbemLocator),
(LPVOID*)&pIWbemLocator);
if (FAILED(hr) || pIWbemLocator == nullptr)
goto LCleanup;
bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); if (bstrNamespace == nullptr) goto LCleanup;
bstrClassName = SysAllocString(L"Win32_PNPEntity"); if (bstrClassName == nullptr) goto LCleanup;
bstrDeviceID = SysAllocString(L"DeviceID"); if (bstrDeviceID == nullptr) goto LCleanup;
// Connect to WMI
hr = pIWbemLocator->ConnectServer(bstrNamespace, nullptr, nullptr, 0L,
0L, nullptr, nullptr, &pIWbemServices);
if (FAILED(hr) || pIWbemServices == nullptr)
goto LCleanup;
// Switch security level to IMPERSONATE.
hr = CoSetProxyBlanket(pIWbemServices,
RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr,
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE,
nullptr, EOAC_NONE);
if ( FAILED(hr) )
goto LCleanup;
hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, nullptr, &pEnumDevices);
if (FAILED(hr) || pEnumDevices == nullptr)
goto LCleanup;
// Loop over all devices
for (;;)
{
ULONG uReturned = 0;
hr = pEnumDevices->Next(10000, _countof(pDevices), pDevices, &uReturned);
if (FAILED(hr))
goto LCleanup;
if (uReturned == 0)
break;
for (size_t iDevice = 0; iDevice < uReturned; ++iDevice)
{
// For each device, get its device ID
hr = pDevices[iDevice]->Get(bstrDeviceID, 0L, &var, nullptr, nullptr);
if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != nullptr)
{
// Check if the device ID contains "IG_". If it does, then it's an XInput device
// This information cannot 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_s(strVid, L"VID_%4X", &dwVid) != 1)
dwVid = 0;
WCHAR* strPid = wcsstr(var.bstrVal, L"PID_");
if (strPid && swscanf_s(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;
}
}
}
VariantClear(&var);
SAFE_RELEASE(pDevices[iDevice]);
}
}
LCleanup:
VariantClear(&var);
if(bstrNamespace)
SysFreeString(bstrNamespace);
if(bstrDeviceID)
SysFreeString(bstrDeviceID);
if(bstrClassName)
SysFreeString(bstrClassName);
for (size_t iDevice = 0; iDevice < _countof(pDevices); ++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 )
{
if( IsXInputDevice( &pdidInstance->guidProduct ) )
return DIENUM_CONTINUE;
// Device is verified not XInput, so add it to the list of DInput devices
return DIENUM_CONTINUE;
}
このコードの少し改善されたバージョンは、従来の DirectInput ジョイスティック サンプルにあります。
関連トピック
XInput の概要