Compartir a través de


Comparación de las características XInput y DirectInput

Importante

Consulta api gameInput para obtener más información sobre la API de entrada de próxima generación compatible con PC y Xbox a través del kit de desarrollo de juegos de Microsoft (GDK) de.

En este documento se comparan las implementaciones de XInput y DirectInput de entrada del controlador y cómo admitir dispositivos XInput y dispositivos DirectInput heredados.

aplicaciones de la Tienda Windows no admiten directInput.

Visión general

XInput permite a las aplicaciones recibir la entrada de los controladores XUSB. Las API están disponibles a través del SDK de DirectX y el controlador está disponible a través de Windows Update.

Hay varias ventajas de usar XInput en DirectInput:

  • XInput es más fácil de usar y requiere menos configuración que DirectInput
  • Tanto la programación de Xbox como Windows usarán los mismos conjuntos de API principales, lo que permite programar para traducir la plataforma cruzada mucho más fácil
  • Habrá una gran base instalada de controladores
  • El dispositivo XInput solo tendrá funcionalidad de vibración al usar las API de XInput

Uso de controladores XUSB con DirectInput

Los controladores XUSB se enumeran correctamente en DirectInputy se pueden usar con directInputAPIs. Sin embargo, faltará alguna funcionalidad proporcionada por XInput en la implementación de DirectInput:

  • Los botones de desencadenador izquierdo y derecho actuarán como un solo botón, no de forma independiente.
  • Los efectos de vibración no estarán disponibles.
  • La consulta de los dispositivos de auriculares no estará disponible

La combinación de los desencadenadores izquierdo y derecho en directInput es por diseño. Los juegos siempre han asumido que los ejes del dispositivo DirectInput se centran cuando no hay ninguna interacción del usuario con el dispositivo. Sin embargo, los controladores más recientes se diseñaron para registrar el valor mínimo, no el centro, cuando no se mantienen los desencadenadores. Por lo tanto, los juegos más antiguos presuponen la interacción del usuario.

La solución era combinar los desencadenadores, establecer un desencadenador en una dirección positiva y la otra en una dirección negativa, por lo que ninguna interacción del usuario es indicativo de DirectInput del "control" en el centro.

Para probar los valores del desencadenador por separado, debe usar XInput.

XInput y DirectInput en paralelo

Al admitir XInput solo, tu juego no funcionará con dispositivos directInput heredados. XInput no reconocerá estos dispositivos.

Si quieres que tu juego admita dispositivos heredados DirectInput, puedes usar DirectInput y XInput en paralelo. Al enumerar los dispositivos DirectInput, todos los dispositivos DirectInput se enumerarán correctamente. Todos los dispositivos XInput se mostrarán como dispositivos XInput y DirectInput, pero no se deben controlar a través de DirectInput. Deberá determinar cuál de los dispositivos DirectInput son dispositivos heredados y cuáles son dispositivos XInput y quitarlos de la enumeración de dispositivos DirectInput.

Para ello, inserte este código en la devolución de llamada de enumeración 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;    
}

Una versión ligeramente mejorada de este código se encuentra en el ejemplo heredado de DirectInput Joystick.

Introducción a XInput

de referencia de programación