Aprovechar el movimiento del ratón High-Definition
Un ratón de ordenador estándar devuelve datos a 400 puntos por pulgada (PPP), mientras que un ratón de alta definición genera datos a 800 PPP o más. Esto hace que la entrada de un mouse de alta definición sea mucho más precisa que la de un mouse estándar. Sin embargo, los datos de alta definición no se pueden obtener a través de los mensajes estándar WM_MOUSEMOVE. Por lo general, los juegos se beneficiarán de dispositivos de ratón de alta definición, pero los juegos que obtienen datos del ratón utilizando solo WM_MOUSEMOVE no podrán acceder a la resolución completa y sin filtrar del ratón.
Una serie de empresas están fabricando dispositivos de mouse de alta definición, como Microsoft y Logitech. Con la creciente popularidad de los dispositivos de mouse de alta resolución, es importante que los desarrolladores comprendan cómo usar la información generada por estos dispositivos de forma óptima. Este artículo se centra en la mejor manera de optimizar el rendimiento de la entrada de ratón en alta definición en un juego de disparos en primera persona.
Recuperación de datos de movimiento del mouse
Estos son los tres métodos principales para recuperar datos del mouse:
- WM_MOUSEMOVE
- WM_INPUT
- DirectInput
Hay ventajas y desventajas para cada método, en función de cómo se usarán los datos.
WM_MOUSEMOVE
El método más sencillo de leer los datos de movimiento del mouse es a través de WM_MOUSEMOVE mensajes. A continuación se muestra un ejemplo de cómo leer datos de movimiento del mouse desde el mensaje de WM_MOUSEMOVE:
case WM_MOUSEMOVE:
{
int xPosAbsolute = GET_X_PARAM(lParam);
int yPosAbsolute = GET_Y_PARAM(lParam);
// ...
break;
}
La desventaja principal de los datos de WM_MOUSEMOVE es que se limita a la resolución de pantalla. Esto significa que si mueve el mouse ligeramente , pero no lo suficiente para hacer que el puntero se mueva al siguiente píxel, no se genere ningún mensaje WM_MOUSEMOVE. Por lo tanto, el uso de este método para leer el movimiento del mouse niega las ventajas de la entrada de alta definición.
Sin embargo, la ventaja de WM_MOUSEMOVE es que Windows aplica la aceleración del puntero (también conocida como balística) a los datos sin procesar del mouse, lo que hace que el puntero del mouse se comporte como esperan los clientes. Esto hace que WM_MOUSEMOVE la opción preferida para el control de puntero (sobre WM_INPUT o DirectInput), ya que da como resultado un comportamiento más natural para los usuarios. Aunque WM_MOUSEMOVE es ideal para mover cursores del ratón, no resulta tan eficaz para mover una cámara de primera persona, ya que se pierde precisión en alta definición.
Para obtener más información sobre WM_MOUSEMOVE, consulta WM_MOUSEMOVE.
WM_INPUT
El segundo método para obtener datos del mouse es leer los mensajes WM_INPUT. El procesamiento de los mensajes WM_INPUT es más complicado que el de los mensajes WM_MOUSEMOVE, pero los mensajes WM_INPUT se leen directamente desde la pila del dispositivo de interfaz humana (HID) y reflejan resultados de alta definición.
Para leer los datos de movimiento del mouse del WM_INPUT mensaje, el dispositivo debe registrarse primero; El código siguiente proporciona un ejemplo de esto:
// 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]));
El siguiente código maneja los mensajes WM_INPUT en el manejador WinProc de la aplicación.
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;
}
La ventaja de usar WM_INPUT es que el juego recibe datos sin procesar del mouse en el nivel más bajo posible.
La desventaja es que WM_INPUT no tiene ningún balístico aplicado a sus datos, por lo que si desea conducir un cursor con estos datos, se requerirá un esfuerzo adicional para hacer que el cursor se comporte como lo hace en Windows. Para obtener más información sobre cómo aplicar balística de puntero, consulta Balística de puntero para Windows XP.
Para más información sobre WM_INPUT, consulta Acerca de la entrada de datos sin procesar.
DirectInput
directInput es un conjunto de llamadas API que abstrae los dispositivos de entrada en el sistema. Internamente, DirectInput crea un segundo subproceso para leer WM_INPUT datos y el uso de las API de DirectInput agregará más sobrecarga que simplemente leer WM_INPUT directamente. DirectInput solo es útil para leer datos de joysticks directInput; Sin embargo, si solo necesita admitir controladores para Windows, use XInput en su lugar. En general, el uso de DirectInput no ofrece ninguna ventaja al leer datos desde dispositivos de mouse o teclado, y no se recomienda el uso de DirectInput en estos escenarios.
Compare la complejidad de usar DirectInput, que se muestra en el código siguiente, con los métodos descritos anteriormente. Se necesita el siguiente conjunto de llamadas para crear un 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();
Y, a continuación, el dispositivo de ratón DirectInput se puede leer cada fotograma.
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;
Resumen
En general, el mejor método para recibir datos de movimiento de mouse de alta definición es WM_INPUT. Si los usuarios simplemente mueven un puntero del mouse, considere la posibilidad de usar WM_MOUSEMOVE para evitar tener que realizar balística de puntero. Ambos mensajes de ventana funcionarán bien incluso si el mouse no es un mouse de alta definición. Al admitir la alta definición, los juegos de Windows pueden ofrecer un control más preciso a los usuarios.