Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Important
Consultez 'API GameInput pour plus d’informations sur l’API d’entrée de nouvelle génération prise en charge sur PC et Xbox via le Microsoft Game Development Kit (GDK).
Ce document compare XInput et DirectInput implémentations d’entrée du contrôleur et comment prendre en charge les appareils XInput et les appareils DirectInput hérités.
applications du Windows Store ne prennent pas en charge DirectInput .
Aperçu
XInput permet aux applications de recevoir des entrées des contrôleurs XUSB. Les API sont disponibles via le Kit de développement logiciel (SDK) DirectX et le pilote est disponible via Windows Update.
L’utilisation de XInput sur DirectInputprésente plusieurs avantages :
- XInput est plus facile à utiliser et nécessite moins de configuration que DirectInput
- La programmation Xbox et Windows utilisera les mêmes ensembles d’API principales, ce qui permet de traduire la programmation multiplateforme beaucoup plus facilement
- Il y aura une grande base installée de contrôleurs
- L’appareil XInput aura des fonctionnalités de vibration uniquement lors de l’utilisation des API XInput
Utilisation de contrôleurs XUSB avec DirectInput
Les contrôleurs XUSB sont correctement énumérés sur directInputet peuvent être utilisés avec les API DirectInput. Toutefois, certaines fonctionnalités fournies par XInput sont manquantes dans l’implémentation DirectInput :
- Les boutons de déclencheur gauche et droit agissent comme un seul bouton, et non indépendamment
- Les effets de vibration ne seront pas disponibles
- L’interrogation des appareils casques ne sera pas disponible
La combinaison des déclencheurs de gauche et de droite dans DirectInput est par conception. Les jeux ont toujours supposé que les axes de l’appareil DirectInput sont centrés lorsqu’il n’existe aucune interaction utilisateur avec l’appareil. Toutefois, les contrôleurs plus récents ont été conçus pour inscrire la valeur minimale, et non le centre, lorsque les déclencheurs ne sont pas conservés. Les jeux plus anciens supposent donc l’interaction utilisateur.
La solution consistait à combiner les déclencheurs, à définir un déclencheur dans une direction positive et l’autre à une direction négative, de sorte qu’aucune interaction utilisateur n’indique DirectInput du « contrôle » au centre.
Pour tester les valeurs de déclencheur séparément, vous devez utiliser XInput.
XInput et DirectInput côte à côte
En prenant en charge XInput uniquement, votre jeu ne fonctionne pas avec les appareils DirectInput hérités. XInput ne reconnaît pas ces appareils.
Si vous souhaitez que votre jeu prend en charge les appareils hérités DirectInput, vous pouvez utiliser DirectInput et XInput côte à côte. Lors de l’énumération de vos appareils DirectInput, tous les appareils DirectInput sont énumérés correctement. Tous les appareils XInput s’affichent en tant qu’appareils XInput et DirectInput, mais ils ne doivent pas être gérés via DirectInput. Vous devez déterminer quels appareils DirectInput sont des appareils hérités et qui sont des appareils XInput, et les supprimer de l’énumération des appareils DirectInput.
Pour ce faire, insérez ce code dans votre rappel d’énumération 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;
}
Une version légèrement améliorée de ce code se trouve dans l’exemple Hérité de DirectInput Joystick.