Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Стандартная мышь компьютера возвращает данные в 400 точек на дюйм (DPI), в то время как мышь с высоким определением создает данные в 800 DPI или больше. Это делает сигнал от мыши высокого разрешения гораздо точнее стандартной мыши. Однако данные высокого определения нельзя получить с помощью стандартных WM_MOUSEMOVE сообщений. Как правило, игры будут выигрывать от использования высокочувствительных устройств ввода, но игры, которые получают данные мыши, используя только WM_MOUSEMOVE, не смогут получить доступ к полному, нефильтрованному разрешению мыши.
Ряд компаний производит устройства с высоким разрешением, такие как Microsoft и Logitech. Благодаря растущей популярности устройств с мышью с высоким разрешением важно понимать, как оптимально использовать информацию, созданную этими устройствами. В этой статье рассматривается лучший способ оптимизации производительности ввода высокоточной мыши в игре, такой как стрелялка от первого лица.
Получение данных перемещения мыши
Ниже приведены три основных метода для получения данных мыши:
Существуют преимущества и недостатки каждого метода в зависимости от того, как будут использоваться данные.
WM_MOUSEMOVE
Самый простой способ чтения данных перемещения мыши осуществляется через WM_MOUSEMOVE сообщения. Ниже приведен пример чтения данных перемещения мыши из сообщения WM_MOUSEMOVE:
case WM_MOUSEMOVE:
{
int xPosAbsolute = GET_X_PARAM(lParam);
int yPosAbsolute = GET_Y_PARAM(lParam);
// ...
break;
}
Основным недостатком данных из WM_MOUSEMOVE является ограничение разрешения экрана. Это означает, что если вы немного переместите мышь, но недостаточно, чтобы указатель переместился на следующий пиксель, то не создается WM_MOUSEMOVE сообщение. Таким образом, использование этого метода для чтения перемещения мыши отрицает преимущества входных данных высокого определения.
Однако преимуществом WM_MOUSEMOVE является то, что Windows применяет ускорение указателя (также известное как баллистика) к необработанным данным мыши, что заставляет указатель мыши вести себя так, как ожидают пользователи. Это делает WM_MOUSEMOVE предпочтительный вариант для управления указателем (над WM_INPUT или DirectInput), так как он приводит к более естественному поведению для пользователей. Хотя WM_MOUSEMOVE идеально подходит для перемещения указателей мыши, он не так хорош для перемещения камеры от первого лица, так как высокая точность будет потеряна.
Дополнительные сведения о WM_MOUSEMOVE см. в WM_MOUSEMOVE.
WM_INPUT
Вторым способом получения данных мыши является чтение WM_INPUT сообщений. Обработка сообщений WM_INPUT более сложна, чем обработка сообщений WM_MOUSEMOVE, но сообщения WM_INPUT считываются непосредственно из стека устройства человеческого интерфейса (HID) и отражают результаты высокой четкости.
Чтобы считывать данные перемещения мыши из сообщения WM_INPUT, устройство должно быть зарегистрировано; В следующем коде приведен пример этого:
// 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]));
Следующий код обрабатывает сообщения WM_INPUT в обработчике WinProc приложения:
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;
}
Преимущество использования WM_INPUT заключается в том, что ваша игра получает данные от мыши в необработанном виде на самом низком уровне.
Недостатком является то, что к данным WM_INPUT не применяются баллистические преобразования, поэтому, если вы хотите управлять курсором на основе этих данных, потребуется приложить дополнительные усилия, чтобы курсор вел себя так, как в Windows. Дополнительные сведения о применении баллистики указателя см. в разделе Баллистика указателя для Windows XP.
Дополнительные сведения о WM_INPUT см. в разделе О необработанном вводе.
DirectInput
DirectInput — это набор вызовов API, которые абстрагируют устройства ввода в системе. Внутренне DirectInput создает второй поток для чтения данных WM_INPUT, и использование API DirectInput добавляет дополнительную нагрузку по сравнению с простым чтением WM_INPUT напрямую. DirectInput полезна только для чтения данных из джойстиков DirectInput; Однако если вам нужно поддерживать только контроллеры для Windows, используйте вместо этого XInput. В целом использование DirectInput не дает никаких преимуществ при чтении данных с устройств мыши или клавиатуры, а использование DirectInput в этих сценариях не рекомендуется.
Сравните сложность использования DirectInput, показанной в следующем коде, с описанными ранее методами. Для создания мыши 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();
А затем устройство мыши DirectInput можно считывать каждый кадр:
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;
Сводка
В целом, лучший метод получения данных перемещения мыши с высоким разрешением — WM_INPUT. Если пользователи просто перемещают указатель мыши, рассмотрите возможность использования WM_MOUSEMOVE, чтобы избежать необходимости выполнять баллистики указателя. Оба этих сообщения окон будут работать хорошо, даже если мышь не поддерживает высокое разрешение. Поддерживая высокое определение, игры Windows могут более точно управлять пользователями.