本篇是使用DirectInput进行交互(2)的续篇。
使用鼠标玩游戏
鼠标的工作原理理解起来并不难,在最底层,鼠标通知系统它要移动到某个方向,每次移动一个记号,驱动程序读取这个数据后,将记号转化为相对移动值。
在通常的应用程序中,windows得到鼠标的移动并通过消息处理函数将移动作为消息报告给用户。使用消息处理函数有时速度会非常慢,因为传递给消息处理函数的每个消息要被插入到队列中,这样消息就只会按照他们加入到队列中的顺序被处理。要加快接收以及处理鼠标输入的过程,就必须直接同鼠标的驱动程序进行交互,而不采用windows消息处理函数。
不论采用哪种移动方式接收鼠标移动,都要从跟踪鼠标在屏幕上的坐标开始,可以选择追踪绝对鼠标坐标或相对鼠标坐标。绝对表示当前的鼠标位置都是基于某个固定点(通常是屏幕的左上角)。
如下图所示,通过鼠标距屏幕左上角的像素数量来衡量鼠标的绝对坐标。
相对指的是从上个已知位置到当前位置所发生的移动量,上个位置可能位于左边、右边、上边或下边。
使用DirectInput处理鼠标
除了指定的是鼠标标识符以及鼠标数据格式外,初始化鼠标就和初始化键盘几乎完全相同。
//--------------------------------------------------------------------------------
// Initialize mouse interface, return a mouse interface pointer.
//--------------------------------------------------------------------------------
IDirectInputDevice8* Init_Mouse(HWND hwnd, IDirectInput8* directinput)
{
IDirectInputDevice8* directinput_device;
// create the device object
if(FAILED(directinput->CreateDevice(GUID_SysMouse, &directinput_device, NULL)))
return NULL;
// set the data format
if(FAILED(directinput_device->SetDataFormat(&c_dfDIMouse)))
{
directinput_device->Release();
return NULL;
}
// set the coooperative mode
if(FAILED(directinput_device->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE)))
{
directinput_device->Release();
return NULL;
}
// acquire the device for use
if(FAILED(directinput_device->Acquire()))
{
directinput_device->Release();
return NULL;
}
// everything well, so return a vaild pointer.
return directinput_device;
}
要调用DirectInputDevice8::GetDeviceState,使用诸如相对移动和按键状态等鼠标的相关信息来填充 DIMOUSESTATE结构体。
DIMOUSESTATE结构体的定义如下:
Describes the state of a mouse device that has up to
four buttons, or another device that is being accessed as if it were a mouse
device. This structure is used with the IDirectInputDevice8::GetDeviceState
method.
typedef struct DIMOUSESTATE {
LONG lX;
LONG lY;
LONG lZ;
BYTE rgbButtons[4];
} DIMOUSESTATE, *LPDIMOUSESTATE;
Members
- lX
- X-axis.
- lY
- Y-axis.
- lZ
- Z-axis, typically a wheel. If the mouse does not have a z-axis, the
value is 0.
- rgbButtons
- Array of buttons. The high-order bit of the byte is set if the
corresponding button is down.
Remarks
You must prepare the device for mouse-style access by calling the
IDirectInputDevice8::SetDataFormat method, passing the c_dfDIMouse global data
format variable.
The mouse is a relative-axis device, so the absolute axis positions for mouse
axes are accumulated relative motion. Therefore, the value of the absolute axis
position is not meaningful except in comparison with other absolute axis
positions.
If an axis is in relative mode, the appropriate member contains the change in
position. If it is in absolute mode, the member contains the absolute axis
position.
点击下载源码和工程
完整源码示例:
/***************************************************************************************
PURPOSE:
Mouse device Demo
***************************************************************************************/
#define DIRECTINPUT_VERSION 0x0800
#include <windows.h>
#include <stdio.h>
#include <dinput.h>
#include "resource.h"
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dinput8.lib")
#pragma warning(disable : 4996)
#define Safe_Release(p) if((p)) (p)->Release();
// window handles, class and caption text.
HWND g_hwnd;
char g_class_name[] = "MouseClass";
IDirectInput8* g_directinput; // directinput component
IDirectInputDevice8* g_directinput_device; // mouse device
//--------------------------------------------------------------------------------
// Window procedure.
//--------------------------------------------------------------------------------
long WINAPI Window_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return (long) DefWindowProc(hwnd, msg, wParam, lParam);
}
//--------------------------------------------------------------------------------
// Initialize mouse interface, return a mouse interface pointer.
//--------------------------------------------------------------------------------
IDirectInputDevice8* Init_Mouse(HWND hwnd, IDirectInput8* directinput)
{
IDirectInputDevice8* directinput_device;
// create the device object
if(FAILED(directinput->CreateDevice(GUID_SysMouse, &directinput_device, NULL)))
return NULL;
// set the data format
if(FAILED(directinput_device->SetDataFormat(&c_dfDIMouse)))
{
directinput_device->Release();
return NULL;
}
// set the coooperative mode
if(FAILED(directinput_device->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE)))
{
directinput_device->Release();
return NULL;
}
// acquire the device for use
if(FAILED(directinput_device->Acquire()))
{
directinput_device->Release();
return NULL;
}
// everything well, so return a vaild pointer.
return directinput_device;
}
//--------------------------------------------------------------------------------
// Read mouse buffer.
//--------------------------------------------------------------------------------
BOOL Read_Device(IDirectInputDevice8* directinput_device, void* buffer, long buffer_size)
{
HRESULT rv;
while(1)
{
// poll device
g_directinput_device->Poll();
// read in state
if(SUCCEEDED(rv = g_directinput_device->GetDeviceState(buffer_size, buffer)))
break;
// return when an unknown error
if(rv != DIERR_INPUTLOST || rv != DIERR_NOTACQUIRED)
return FALSE;
// re-acquire and try again
if(FAILED(g_directinput_device->Acquire()))
return FALSE;
}
return TRUE;
}
//--------------------------------------------------------------------------------
// Main function, routine entry.
//--------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
WNDCLASS win_class;
MSG msg;
DIMOUSESTATE mouse_state = {0};
char text[256];
long x_pos = 0, y_pos = 0;
// create window class and register it
win_class.style = CS_HREDRAW | CS_VREDRAW;
win_class.lpfnWndProc = Window_Proc;
win_class.cbClsExtra = 0;
win_class.cbWndExtra = DLGWINDOWEXTRA;
win_class.hInstance = inst;
win_class.hIcon = LoadIcon(inst, IDI_APPLICATION);
win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
win_class.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
win_class.lpszMenuName = NULL;
win_class.lpszClassName = g_class_name;
if(! RegisterClass(&win_class))
return FALSE;
// create the main window
g_hwnd = CreateDialog(inst, MAKEINTRESOURCE(IDD_MOUSE), 0, NULL);
ShowWindow(g_hwnd, cmd_show);
UpdateWindow(g_hwnd);
// initialize directinput and get keyboard device
DirectInput8Create(inst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void **) &g_directinput, NULL);
// initialize mouse
g_directinput_device = Init_Mouse(g_hwnd, g_directinput);
// start message pump, waiting for signal to quit.
ZeroMemory(&msg, sizeof(MSG));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// read in mouse and display coordinates
Read_Device(g_directinput_device, &mouse_state, sizeof(DIMOUSESTATE));
x_pos += mouse_state.lX;
y_pos += mouse_state.lY;
if(mouse_state.lX != 0 || mouse_state.lY != 0)
{
sprintf(text, "%ld, %ld", x_pos, y_pos);
SetWindowText(GetDlgItem(g_hwnd, IDC_COORDINATES), text);
}
}
// release directinput objects
g_directinput_device->Unacquire();
g_directinput_device->Release();
g_directinput->Release();
UnregisterClass(g_class_name, inst);
return (int) msg.wParam;
}
运行截图:
阅读下篇:
使用DirectInput进行交互(4)