利用高畫質滑鼠移動
標準電腦滑鼠會以每英吋 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訊息會直接從 Human Interface Device (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]));
下列程式碼會處理應用程式 WinProc 處理常式中的WM_INPUT訊息:
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資料,而使用 DirectInput API 會增加比直接讀取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 遊戲可以為使用者提供更精確的控制。