Memanfaatkan Gerakan Mouse Definisi Tinggi

Mouse komputer standar mengembalikan data pada 400 titik per inci (DPI), sedangkan mouse definisi tinggi menghasilkan data pada 800 DPI atau lebih besar. Ini membuat input dari mouse definisi tinggi jauh lebih tepat daripada itu dari mouse standar. Namun, data definisi tinggi tidak dapat diperoleh melalui pesan WM_MOUSEMOVE standar. Secara umum, game akan mendapat manfaat dari perangkat mouse definisi tinggi tetapi game yang mendapatkan data mouse hanya menggunakan WM_MOUSEMOVE tidak akan dapat mengakses resolusi mouse yang lengkap dan tidak difilter.

Sejumlah perusahaan memproduksi perangkat mouse definisi tinggi, seperti Microsoft dan Logitech. Dengan meningkatnya popularitas perangkat mouse resolusi tinggi, penting bagi pengembang untuk memahami cara menggunakan informasi yang dihasilkan oleh perangkat ini secara optimal. Artikel ini berfokus pada cara terbaik untuk mengoptimalkan performa input mouse definisi tinggi dalam game seperti penembak orang pertama.

Mengambil data gerakan mouse

Berikut adalah tiga metode utama untuk mengambil data mouse:

Ada kelebihan dan kekurangan untuk setiap metode, tergantung pada bagaimana data akan digunakan.

WM_MOUSEMOVE

Metode paling sederhana untuk membaca data pergerakan mouse adalah melalui pesan WM_MOUSEMOVE. Berikut ini adalah contoh cara membaca data pergerakan mouse dari pesan WM_MOUSEMOVE:

case WM_MOUSEMOVE:
{
    int xPosAbsolute = GET_X_PARAM(lParam); 
    int yPosAbsolute = GET_Y_PARAM(lParam);
    // ...
    break;
}

Kerugian utama terhadap data dari WM_MOUSEMOVE adalah bahwa itu terbatas pada resolusi layar. Ini berarti bahwa jika Anda sedikit memindahkan mouse — tetapi tidak cukup untuk menyebabkan penunjuk berpindah ke piksel berikutnya — maka tidak ada pesan WM_MOUSEMOVE yang dihasilkan. Jadi, menggunakan metode ini untuk membaca gerakan mouse meniadakan manfaat input definisi tinggi.

Keuntungan untuk WM_MOUSEMOVE, bagaimanapun, adalah windows menerapkan akselerasi pointer (juga dikenal sebagai balistik) ke data mouse mentah, yang membuat penunjuk mouse berulah seperti yang diharapkan pelanggan. Ini membuat WM_MOUSEMOVE opsi pilihan untuk kontrol penunjuk (melalui WM_INPUT atau DirectInput), karena menghasilkan perilaku yang lebih alami bagi pengguna. Meskipun WM_MOUSEMOVE sangat ideal untuk memindahkan penunjuk mouse, tidak begitu baik untuk memindahkan kamera orang pertama, karena presisi definisi tinggi akan hilang.

Untuk informasi selengkapnya tentang WM_MOUSEMOVE, lihat WM_MOUSEMOVE.

WM_INPUT

Metode kedua untuk mendapatkan data mouse adalah membaca pesan WM_INPUT. Memproses pesan WM_INPUT lebih rumit daripada memproses pesan WM_MOUSEMOVE, tetapi pesan WM_INPUT dibaca langsung dari tumpukan Perangkat Antarmuka Manusia (HID) dan mencerminkan hasil definisi tinggi.

Untuk membaca data pergerakan mouse dari pesan WM_INPUT, perangkat harus terlebih dahulu didaftarkan; kode berikut memberikan contoh ini:

// you can #include <hidusage.h> for these defines
#ifndef HID_USAGE_PAGE_GENERIC
#define HID_USAGE_PAGE_GENERIC         ((USHORT) 0x01)
#endif
#ifndef HID_USAGE_GENERIC_MOUSE
#define HID_USAGE_GENERIC_MOUSE        ((USHORT) 0x02)
#endif

RAWINPUTDEVICE Rid[1];
Rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC; 
Rid[0].usUsage = HID_USAGE_GENERIC_MOUSE; 
Rid[0].dwFlags = RIDEV_INPUTSINK;   
Rid[0].hwndTarget = hWnd;
RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]));

Kode berikut menangani pesan WM_INPUT di handler WinProc aplikasi:

case WM_INPUT: 
{
    UINT dwSize = sizeof(RAWINPUT);
    static BYTE lpb[sizeof(RAWINPUT)];

    GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER));

    RAWINPUT* raw = (RAWINPUT*)lpb;

    if (raw->header.dwType == RIM_TYPEMOUSE) 
    {
        int xPosRelative = raw->data.mouse.lLastX;
        int yPosRelative = raw->data.mouse.lLastY;
    } 
    break;
}

Keuntungan menggunakan WM_INPUT adalah permainan Anda menerima data mentah dari mouse pada tingkat terendah yang mungkin.

Kerugiannya adalah bahwa WM_INPUT tidak memiliki balistik yang diterapkan pada datanya, jadi jika Anda ingin mendorong kursor dengan data ini, upaya ekstra akan diperlukan untuk membuat kursor bertingkah seperti di Windows. Untuk informasi selengkapnya tentang menerapkan balistik pointer, lihat Balistik pointer untuk Windows XP.

Untuk informasi selengkapnya tentang WM_INPUT, lihat Tentang input mentah.

DirectInput

DirectInput adalah sekumpulan panggilan API yang mengabstraksi perangkat input pada sistem. Secara internal, DirectInput membuat utas kedua untuk membaca data WM_INPUT, dan menggunakan API DirectInput akan menambahkan lebih banyak overhead daripada hanya membaca WM_INPUT secara langsung. DirectInput hanya berguna untuk membaca data dari joystick DirectInput; namun, jika Anda hanya perlu mendukung pengontrol untuk Windows, gunakan XInput sebagai gantinya. Secara keseluruhan, menggunakan DirectInput tidak menawarkan keuntungan saat membaca data dari perangkat mouse atau keyboard, dan penggunaan DirectInput dalam skenario ini tidak disarankan.

Bandingkan kompleksitas penggunaan DirectInput, yang ditunjukkan dalam kode berikut, dengan metode yang dijelaskan sebelumnya. Kumpulan panggilan berikut diperlukan untuk membuat mouse DirectInput:

LPDIRECTINPUT8 pDI;
LPDIRECTINPUTDEVICE8 pMouse;

hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&pDI, NULL);
if(FAILED(hr))
    return hr;

hr = pDI->CreateDevice(GUID_SysMouse, &pMouse, NULL);
if(FAILED(hr))
    return hr;

hr = pMouse->SetDataFormat(&c_dfDIMouse2);
if(FAILED(hr))
    return hr;

hr = pMouse->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
if(FAILED(hr))
    return hr;

if(!bImmediate)
{
    DIPROPDWORD dipdw;
    dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
    dipdw.diph.dwObj        = 0;
    dipdw.diph.dwHow        = DIPH_DEVICE;
    dipdw.dwData            = 16; // Arbitrary buffer size

    if(FAILED(hr = pMouse->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)))
        return hr;
}

pMouse->Acquire();

Dan kemudian perangkat mouse DirectInput dapat dibaca setiap bingkai:

DIMOUSESTATE2 dims2; 
ZeroMemory(&dims2, sizeof(dims2));

hr = pMouse->GetDeviceState(sizeof(DIMOUSESTATE2), &dims2);
if(FAILED(hr)) 
{
    hr = pMouse->Acquire();
    while(hr == DIERR_INPUTLOST) 
        hr = pMouse->Acquire();

    return S_OK; 
}

int xPosRelative = dims2.lX;
int yPosRelative = dims2.lY;

Ringkasan

Secara keseluruhan, metode terbaik untuk menerima data pergerakan mouse definisi tinggi adalah WM_INPUT. Jika pengguna Anda hanya memindahkan pointer mouse, pertimbangkan untuk menggunakan WM_MOUSEMOVE untuk menghindari perlunya melakukan balistik pointer. Kedua pesan jendela ini akan bekerja dengan baik meskipun mouse bukan mouse definisi tinggi. Dengan mendukung definisi tinggi, game Windows dapat menawarkan kontrol yang lebih tepat kepada pengguna.