共用方式為


利用高畫質滑鼠移動

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