共用方式為


善用滑鼠移動 High-Definition

標準計算機滑鼠會以每英吋 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 遊戲可以為使用者提供更精確的控制。