Bagikan melalui


Perbandingan fitur XInput dan DirectInput

Penting

Lihat API GameInput untuk detail tentang API input generasi berikutnya yang didukung pada PC dan Xbox melalui Microsoft Game Development Kit (GDK).

Dokumen ini membandingkan XInput dan DirectInput implementasi input pengontrol dan cara mendukung perangkat XInput dan perangkat DirectInput warisan.

aplikasi Windows Store tidak mendukung DirectInput.

Ikhtisar

XInput memungkinkan aplikasi menerima input dari pengontrol XUSB. API tersedia melalui DirectX SDK, dan driver tersedia melalui Windows Update.

Ada beberapa keuntungan menggunakan XInput daripada DirectInput:

  • XInput lebih mudah digunakan dan membutuhkan lebih sedikit pengaturan daripada DirectInput
  • Pemrograman Xbox dan Windows akan menggunakan set API inti yang sama, memungkinkan pemrograman untuk menerjemahkan lintas platform jauh lebih mudah
  • Akan ada basis pengontrol besar yang dipasang
  • Perangkat XInput hanya akan memiliki fungsionalitas getaran saat menggunakan API XInput

Menggunakan pengontrol XUSB dengan DirectInput

Pengontrol XUSB dijumlahkan dengan benar pada DirectInput, dan dapat digunakan dengan DirectInputAPIs. Namun, beberapa fungsionalitas yang disediakan oleh XInput akan hilang dari implementasi DirectInput:

  • Tombol pemicu kiri dan kanan akan bertindak sebagai satu tombol, tidak secara independen
  • Efek getaran tidak akan tersedia
  • Kueri untuk perangkat headset tidak akan tersedia

Kombinasi pemicu kiri dan kanan di DirectInput dirancang. Game selalu mengasumsikan bahwa sumbu perangkat DirectInput berpusat ketika tidak ada interaksi pengguna dengan perangkat. Namun, pengontrol yang lebih baru dirancang untuk mendaftarkan nilai minimum, bukan pusat, ketika pemicu tidak ditahan. Oleh karena itu, game yang lebih lama akan mengasumsikan interaksi pengguna.

Solusinya adalah menggabungkan pemicu, mengatur satu pemicu ke arah positif dan yang lain ke arah negatif, sehingga tidak ada interaksi pengguna yang menunjukkan DirectInput "kontrol" berada di tengah.

Untuk menguji nilai pemicu secara terpisah, Anda harus menggunakan XInput.

XInput dan DirectInput Berdampingan

Hanya dengan mendukung XInput, game Anda tidak akan berfungsi dengan perangkat directInput warisan. XInput tidak akan mengenali perangkat ini.

Jika anda ingin game mendukung warisan DirectInput perangkat, anda dapat menggunakan DirectInput dan XInput secara berdampingan. Saat menghitung perangkat DirectInput Anda, semua perangkat DirectInput akan menghitung dengan benar. Semua perangkat XInput akan muncul sebagai perangkat XInput dan DirectInput, tetapi tidak boleh ditangani melalui DirectInput. Anda harus menentukan perangkat DirectInput mana yang merupakan perangkat warisan, dan yang merupakan perangkat XInput, dan menghapusnya dari enumerasi perangkat DirectInput.

Untuk melakukan ini, masukkan kode ini ke dalam panggilan balik enumerasi DirectInput Anda:

#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;    
}

Versi kode ini yang sedikit ditingkatkan ada dalam sampel DirectInput Joystick warisan.

Memulai XInput

Referensi Pemrograman