Partilhar via


Tirar partido do movimento do rato High-Definition

Um mouse de computador padrão retorna dados a 400 pontos por polegada (DPI), enquanto um mouse de alta definição gera dados a 800 DPI ou superior. Isso torna a entrada de um mouse de alta definição muito mais precisa do que a de um mouse padrão. No entanto, os dados de alta definição não podem ser obtidos através das mensagens WM_MOUSEMOVE padrão. Em geral, os jogos se beneficiarão de dispositivos de mouse de alta definição, mas os jogos que obtêm dados do mouse usando apenas WM_MOUSEMOVE não poderão acessar a resolução completa e não filtrada do mouse.

Várias empresas estão fabricando dispositivos de mouse de alta definição, como a Microsoft e a Logitech. Com a crescente popularidade dos dispositivos de mouse de alta resolução, é importante que os desenvolvedores entendam como usar as informações geradas por esses dispositivos de forma otimizada. Este artigo se concentra na melhor maneira de otimizar o desempenho da entrada do mouse de alta definição em um jogo como um jogo de tiro em primeira pessoa.

Recuperando dados de movimento do mouse

Aqui estão os três métodos principais para recuperar dados do mouse:

Existem vantagens e desvantagens para cada método, dependendo de como os dados serão usados.

WM_MOUSEMOVE

O método mais simples de ler dados de movimento do mouse é através de mensagens WM_MOUSEMOVE. Segue-se um exemplo de como ler dados de movimento do rato a partir da mensagem WM_MOUSEMOVE:

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

A principal desvantagem dos dados de WM_MOUSEMOVE é que eles são limitados à resolução da tela. Isso significa que, se você mover o mouse ligeiramente — mas não o suficiente para fazer com que o ponteiro se mova para o próximo pixel — nenhuma mensagem WM_MOUSEMOVE será gerada. Assim, usar este método para ler o movimento do mouse nega os benefícios da entrada de alta definição.

A vantagem do WM_MOUSEMOVE, no entanto, é que o Windows aplica aceleração de ponteiro (também conhecida como balística) aos dados brutos do mouse, o que faz com que o ponteiro do mouse se comporte como os clientes esperam. Isso torna WM_MOUSEMOVE a opção preferida para controle de ponteiro (sobre WM_INPUT ou DirectInput), uma vez que resulta em um comportamento mais natural para os usuários. Embora WM_MOUSEMOVE seja ideal para mover o cursor do rato, não é tão bom para mover uma câmara em primeira pessoa, já que a precisão em alta definição será perdida.

Para obter mais informações sobre WM_MOUSEMOVE, consulte WM_MOUSEMOVE.

WM_INPUT

O segundo método de obtenção de dados do mouse é ler mensagens WM_INPUT. O processamento de mensagens WM_INPUT é mais complicado do que o processamento de mensagens WM_MOUSEMOVE, mas as mensagens WM_INPUT são lidas diretamente da pilha do Dispositivo de Interface Humana (HID) e refletem os resultados de alta definição.

Para ler os dados de movimento do mouse da mensagem WM_INPUT, o dispositivo deve primeiro ser registrado; O código a seguir fornece um exemplo disso:

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

O código a seguir manipula mensagens de WM_INPUT no manipulador WinProc do aplicativo:

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

A vantagem de usar WM_INPUT é que seu jogo recebe dados brutos do mouse no nível mais baixo possível.

A desvantagem é que WM_INPUT não tem balística aplicada aos seus dados, portanto, se você quiser dirigir um cursor com esses dados, será necessário um esforço extra para fazer com que o cursor se comporte como no Windows. Para obter mais informações sobre como aplicar balística de ponteiro, consulte balística de ponteiro para Windows XP.

Para obter mais informações sobre WM_INPUT, consulte Sobre a entrada bruta.

DirectInput

DirectInput é um conjunto de chamadas de API que abstrai dispositivos de entrada no sistema. Internamente, o DirectInput cria um segundo thread para ler WM_INPUT dados, e usar as APIs do DirectInput adicionará mais sobrecarga do que simplesmente ler WM_INPUT diretamente. DirectInput só é útil para ler dados de joysticks DirectInput; no entanto, se você só precisa suportar controladores para Windows, use XInput em vez disso. No geral, o uso do DirectInput não oferece vantagens ao ler dados de dispositivos de mouse ou teclado, e o uso do DirectInput nesses cenários é desencorajado.

Compare a complexidade de usar DirectInput , mostrado no código a seguir, com os métodos descritos anteriormente. O seguinte conjunto de chamadas é necessário para criar um 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();

E, em seguida, o dispositivo de mouse DirectInput pode ser lido cada quadro:

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;

Resumo

No geral, o melhor método para receber dados de movimento do mouse de alta definição é WM_INPUT. Se os utilizadores estiverem apenas movendo o ponteiro de rato, considere usar WM_MOUSEMOVE para evitar a necessidade de realizar cálculos de movimento do ponteiro. Ambas as mensagens de janela funcionarão bem, mesmo que o mouse não seja um mouse de alta definição. Ao suportar alta definição, os jogos do Windows podem oferecer um controle mais preciso aos usuários.