Dela via


Utnyttja rörelse med musen High-Definition

En vanlig datormus returnerar data med 400 punkter per tum (DPI), medan en hd-mus genererar data vid 800 DPI eller högre. Detta gör indata från en hd-mus mycket mer exakt än från en standardmus. Hd-data kan dock inte hämtas via standardmeddelanden WM_MOUSEMOVE. I allmänhet kommer spel att dra nytta av hd-musenheter, men spel som hämtar musdata med bara WM_MOUSEMOVE kommer inte att kunna komma åt musens fullständiga, ofiltrerade upplösning.

Ett antal företag tillverkar hd-musenheter, till exempel Microsoft och Logitech. Med den ökande populariteten för högupplösta musenheter är det viktigt att utvecklare förstår hur de använder den information som genereras av dessa enheter optimalt. Den här artikeln fokuserar på hur man bäst optimerar prestandan för högupplöst musinmatning i spel av förstapersonsskjutartyp.

Hämtar musförflyttningsdata

Här är de tre primära metoderna för att hämta musdata:

Det finns fördelar och nackdelar med varje metod, beroende på hur data ska användas.

WM_MOUSEMOVE

Den enklaste metoden för att läsa musförflyttningsdata är genom WM_MOUSEMOVE meddelanden. Följande är ett exempel på hur du läser musförflyttningsdata från WM_MOUSEMOVE meddelande:

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

Den främsta nackdelen med data från WM_MOUSEMOVE är att den är begränsad till skärmupplösningen. Det innebär att om du flyttar musen något – men inte tillräckligt för att få pekaren att flytta till nästa pixel – genereras inget WM_MOUSEMOVE meddelande. Genom att använda den här metoden för att läsa musrörelser negerar du därför fördelarna med hd-indata.

Fördelen med att WM_MOUSEMOVE är dock att Windows tillämpar pekaracceleration (även kallat ballistik) på rådata, vilket gör att muspekaren beter sig som kunderna förväntar sig. Detta gör WM_MOUSEMOVE det föredragna alternativet för pekarkontroll (över WM_INPUT eller DirectInput), eftersom det resulterar i ett mer naturligt beteende för användare. Även om WM_MOUSEMOVE är perfekt för att flytta muspekare, är det inte så bra för att flytta en förstapersonskamera, eftersom hd-precisionen kommer att gå förlorad.

Mer information om WM_MOUSEMOVE finns i WM_MOUSEMOVE.

WM_INPUT

Den andra metoden för att hämta musdata är att läsa WM_INPUT meddelanden. Det är mer komplicerat att bearbeta WM_INPUT meddelanden än att bearbeta WM_MOUSEMOVE meddelanden, men WM_INPUT meddelanden läss direkt från HID-stacken (Human Interface Device) och återspeglar hd-resultat.

Om du vill läsa musförflyttningsdata från WM_INPUT-meddelandet måste enheten först registreras. följande kod innehåller ett exempel på detta:

// 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]));

Följande kod hanterar WM_INPUT meddelanden i programmets WinProc-hanterare:

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

Fördelen med att använda WM_INPUT är att ditt spel tar emot rådata från musen på lägsta möjliga nivå.

Nackdelen är att WM_INPUT inte har någon ballistik som tillämpas på dess data, så om du vill köra en markör med dessa data krävs extra ansträngning för att få markören att bete sig som den gör i Windows. Mer information om hur man tillämpar pekarballistik finns under Pekarballistik för Windows XP.

Mer information om WM_INPUT finns i Om råa indata.

DirectInput

DirectInput är en uppsättning API-anrop som abstraherar indataenheter i systemet. Internt skapar DirectInput en andra tråd för att läsa WM_INPUT data, och om du använder DirectInput-API:erna läggs mer omkostnader än att bara läsa WM_INPUT direkt. DirectInput är bara användbart för att läsa data från DirectInput-joysticks. Men om du bara behöver stöd för kontrollanter för Windows använder du XInput- i stället. Att använda DirectInput ger inga fördelar när du läser data från mus- eller tangentbordsenheter, och användningen av DirectInput i dessa scenarier rekommenderas inte.

Jämför komplexiteten med att använda DirectInput, som visas i följande kod, med de metoder som beskrevs tidigare. Följande uppsättning anrop krävs för att skapa en DirectInput-mus:

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();

Och sedan kan DirectInput-musenheten läsas varje bildruta:

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;

Sammanfattning

Sammantaget är den bästa metoden för att ta emot hd-musförflyttningsdata WM_INPUT. Om användarna bara flyttar en muspekare bör du överväga att använda WM_MOUSEMOVE för att undvika att behöva utföra pekarballistik. Båda dessa fönstermeddelanden fungerar bra även om musen inte är en hd-mus. Genom att stödja hd kan Windows-spel erbjuda mer exakt kontroll till användare.