Porovnání funkcí XInput a DirectInput

Důležitý

Podrobnosti o vstupním rozhraní API nové generace podporovaném na počítači a Xboxu prostřednictvím sady Microsoft Game Development Kit (GDK) najdete v tématu rozhraní API GameInput .

Tento dokument porovnává XInput a DirectInput implementace vstupu kontroleru a jak podporovat zařízení XInput i starší zařízení DirectInput.

aplikace pro Windows Store nepodporují DirectInput .

Přehled

XInput umožňuje aplikacím přijímat vstupy z kontrolerů XUSB. Rozhraní API jsou k dispozici prostřednictvím sady DirectX SDK a ovladač je k dispozici prostřednictvím služby Windows Update.

Použití XInput oproti directInputmá několik výhod:

  • XInput je jednodušší a vyžaduje menší nastavení než DirectInput
  • Programování pro Xbox i Windows bude používat stejné sady základních rozhraní API, což usnadňuje programování pro různé platformy.
  • K dispozici bude velká nainstalovaná základna kontrolerů.
  • Zařízení XInput bude mít funkce kmitání pouze při použití rozhraní API XInput

Použití kontrolerů XUSB s DirectInputem

Kontrolery XUSB jsou správně vyčíslované na DirectInputa lze je použít s Rozhraním DirectInputAPI. V implementaci DirectInput však chybí některé funkce poskytované XInputem:

  • Tlačítka triggeru vlevo a vpravo budou fungovat jako jedno tlačítko, ne nezávisle na sobě.
  • Efekty kmitání nebudou k dispozici.
  • Dotazování na zařízení s náhlavní soupravou nebude k dispozici.

Kombinace triggerů zleva a doprava v DirectInput je navržena. Hry vždy předpokládají, že osy zařízení DirectInput jsou zacentrovány, pokud se zařízení nijak neaktuuje. Novější kontrolery však byly navrženy tak, aby registrovaly minimální hodnotu, nikoli na střed, když se triggery neuchovávají. Starší hry by proto předpokládaly interakci uživatele.

Řešením bylo zkombinovat triggery, nastavit jeden trigger na pozitivní směr a druhý na negativní směr, takže žádná interakce uživatele indikuje, že DirectInput "řízení", který je uprostřed.

Abyste mohli hodnoty triggeru testovat samostatně, musíte použít XInput.

XInput a DirectInput Vedle sebe

Podpora pouze XInput nebude fungovat se starší verzí zařízení DirectInput. XInput tato zařízení nerozpozná.

Pokud chcete, aby vaše hra podporovala starší verze zařízení DirectInput, můžete souběžně používat DirectInput a XInput. Při vytváření výčtu vašich zařízení DirectInput budou všechna zařízení DirectInput vyčíslit správně. Všechna zařízení XInput se zobrazí jako zařízení XInput i DirectInput, ale neměla by se zpracovávat prostřednictvím DirectInputu. Budete muset určit, která zařízení DirectInput jsou starší verze zařízení, a která jsou zařízení XInput, a odebrat je z výčtu zařízení DirectInput.

Uděláte to tak, že tento kód vložíte do zpětného volání výčtu 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;    
}

Mírně vylepšená verze tohoto kódu je ve starší verzi directInput Joystick ukázky.

začínáme s XInput

Referenční programování