键盘、鼠标、游戏杆是最为常用的游戏输入设备。
Xna直接向客户提供了对于以上输入设备的支持。例如,我们可以直接使用
MouseState mouseState = Mouse.GetState();
获取鼠标输入设备的当前状态,从而进一步检测各个键位的当前状态。Keyboard、GamePad用法与Mouse类似,在此不再赘述。
回到Direct3D环境中。因为工程本身基于Win32App,所以我们可以获得窗口消息的支持,从而检测鼠标和键盘的当前状态。不过,处于效率考虑,这里推荐大家使用更加高效的机制——DirectInput。
下面我们构建基于DirectInput机制的鼠标输入设备。
1.在前篇工程基础上新建CD3DInput类;
/*
-------------------------------------
代码清单:D3DInput.h
来自:
http://www.cnblogs.com/kenkao
-------------------------------------
*/
#include
"
D3DInit.h
"
#include
<
dinput.h
>
#pragma
comment(lib, "dinput8.lib")
#define
BUTTONSTATE_PRESSED 0x01
#define
BUTTONSTATE_RELEASED 0x00
#pragma
once
class
CMouseInput
{
public
:
CMouseInput();
~
CMouseInput();
HRESULT Initialize(HINSTANCE hInst,HWND hWnd);
//
初始化输入设备
void
GetState();
//
获取设备状态
DWORD LeftButton();
//
鼠标左键状态
DWORD MiddleButton();
//
鼠标滚轮状态
DWORD RightButton();
//
鼠标右键状态
void
Release();
//
释放输入设备
long
MouseMoveX();
//
鼠标X方向偏移
long
MouseMoveY();
//
鼠标Y方向偏移
void
SetPosition(POINT point);
//
设置鼠标位置
void
GetPosition(POINT
&
point);
//
获取鼠标位置
LONG GetWheelLenth();
//
获取滚轮滚动的距离
private
:
HWND m_hWnd;
//
设备所属的窗口句柄
LPDIRECTINPUT8 m_pIDirectInput;
//
IDirectInput接口对象
LPDIRECTINPUTDEVICE8 m_pIDirectInputDevice;
//
IDirectInput设备对象
DIMOUSESTATE m_mouseState;
//
鼠标状态结构体
};
D3DInput.cpp
/*
-------------------------------------
代码清单:D3DInput.cpp
来自:
http://www.cnblogs.com/kenkao
-------------------------------------
*/
#include
"
StdAfx.h
"
#include
"
D3DInput.h
"
CMouseInput::CMouseInput() : m_pIDirectInput(NULL),
m_pIDirectInputDevice(NULL)
{
}
CMouseInput::
~
CMouseInput()
{
}
HRESULT CMouseInput::Initialize(HINSTANCE hInst,HWND hWnd)
{
m_hWnd
=
hWnd;
HRESULT hr;
//
创建IDirectInput接口对象
hr
=
DirectInput8Create(hInst,DIRECTINPUT_VERSION,IID_IDirectInput8,(
void
**
)
&
m_pIDirectInput,NULL);
if
(FAILED(hr)){
return
hr;
}
//
初始化鼠标输入设备
hr
=
m_pIDirectInput
->
CreateDevice(GUID_SysMouse,
&
m_pIDirectInputDevice,NULL);
//
GUID_SysMouse代表初始化设备为鼠标设备
if
(FAILED(hr)){
ReleaseCOM(m_pIDirectInput);
return
hr;
}
//
设置鼠标设备的数据格式
hr
=
m_pIDirectInputDevice
->
SetDataFormat(
&
c_dfDIMouse);
if
(FAILED(hr)){
ReleaseCOM(m_pIDirectInputDevice);
ReleaseCOM(m_pIDirectInput);
return
hr;
}
//
设置鼠标设备的协调级别
hr
=
m_pIDirectInputDevice
->
SetCooperativeLevel(hWnd,DISCL_FOREGROUND
|
DISCL_NONEXCLUSIVE);
if
(FAILED(hr)){
ReleaseCOM(m_pIDirectInputDevice);
ReleaseCOM(m_pIDirectInput);
return
hr;
}
//
获取鼠标设备访问权限
hr
=
m_pIDirectInputDevice
->
Acquire();
return
S_OK;
}
void
CMouseInput::GetState()
{
HRESULT hr;
DWORD dwReadNum
=
1
;
hr
=
m_pIDirectInputDevice
->
GetDeviceState(
sizeof
(m_mouseState),(LPVOID)
&
m_mouseState);
if
(FAILED(hr)){
hr
=
m_pIDirectInputDevice
->
Acquire();
}
}
DWORD CMouseInput::LeftButton()
{
if
(m_mouseState.rgbButtons[
0
]
&
0x80
)
{
return
BUTTONSTATE_PRESSED;
}
else
return
BUTTONSTATE_RELEASED;
}
DWORD CMouseInput::MiddleButton()
{
if
(m_mouseState.rgbButtons[
2
]
&
0x80
)
{
return
BUTTONSTATE_PRESSED;
}
else
return
BUTTONSTATE_RELEASED;
}
DWORD CMouseInput::RightButton()
{
if
(m_mouseState.rgbButtons[
1
]
&
0x80
)
{
return
BUTTONSTATE_PRESSED;
}
else
return
BUTTONSTATE_RELEASED;
}
void
CMouseInput::Release()
{
//
释放鼠标设备访问权限
m_pIDirectInputDevice
->
Unacquire();
ReleaseCOM(m_pIDirectInputDevice);
ReleaseCOM(m_pIDirectInput);
}
void
CMouseInput::SetPosition(POINT point)
{
ScreenToClient(m_hWnd,
&
point);
SetCursorPos(point.x,point.y);
}
void
CMouseInput::GetPosition(POINT
&
point)
{
GetCursorPos(
&
point);
ScreenToClient(m_hWnd,
&
point);
}
LONG CMouseInput::GetWheelLenth()
{
return
m_mouseState.lZ;
}
虽然构建的类名为CD3DInput,不过我实际写入的类名是CMouseInput,后续可能会把键盘和游戏杆输入设备的构建也放到这两个文件里,引用起来比较方便。
默认情况下,DirectInput设备以立即模式获取输入信息,意味着数据读取的前一刻,输入信息不会被DirectInput记录。
与立即模式相对的是缓冲模式,输入信息将被放在一个缓冲区内,供DirectInput设备读取。
这里我们运用立即模式构建鼠标输入设备。
通过GetDeviceState函数,我们将当前的鼠标设备状态获取到名为DIMOUSESTATE的结构体中。关于这个结构体的定义,大家可以看下SDK的说明文档,很好理解~
完成各个键位的检测之后,便可在前篇工程的代码基础上加入鼠标输入设备的单元测试代码。
2.丰富D3DGame.cpp的代码内容;
D3DGame.cpp
/*
-------------------------------------
代码清单:D3DGame.cpp
来自:
http://www.cnblogs.com/kenkao
-------------------------------------
*/
#include
"
StdAfx.h
"
#include
"
D3DGame.h
"
#include
<
stdio.h
>
HINSTANCE g_hInst;
HWND g_hWnd;
IDirect3D9
*
g_pD3D
=
NULL;
IDirect3DDevice9
*
g_pD3DDevice
=
NULL;
CMouseInput
*
g_pMouseInput
=
NULL;
//
鼠标输入单元测试函数
void
TestMouseInput();
void
Initialize(HINSTANCE hInst, HWND hWnd)
{
g_hInst
=
hInst;
g_hWnd
=
hWnd;
InitD3D(
&
g_pD3D,
&
g_pD3DDevice, hWnd);
g_pMouseInput
=
new
CMouseInput;
g_pMouseInput
->
Initialize(hInst,hWnd);
}
void
LoadContent()
{
}
void
Update()
{
TestMouseInput();
}
void
Draw()
{
g_pD3DDevice
->
Clear(
0
, NULL, D3DCLEAR_TARGET
|
D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(
100
,
149
,
237
,
255
),
1.0f
,
0
);
if
(SUCCEEDED(g_pD3DDevice
->
BeginScene()))
{
g_pD3DDevice
->
EndScene();
}
g_pD3DDevice
->
Present(NULL, NULL, NULL, NULL);
}
void
UnloadContent()
{
}
void
Dispose()
{
ReleaseCOM(g_pMouseInput);
ReleaseCOM(g_pD3DDevice);
ReleaseCOM(g_pD3D);
}
void
TestMouseInput()
{
POINT point;
g_pMouseInput
->
GetState();
g_pMouseInput
->
GetPosition(point);
TCHAR tmpText[
50
];
if
(g_pMouseInput
->
LeftButton()
==
BUTTONSTATE_PRESSED)
{
sprintf(tmpText,
"
鼠标左键已按下,X-Y坐标为(%d,%d)
"
,point.x,point.y);
MessageBox(NULL,tmpText,
"
提示
"
,MB_OK
|
MB_ICONINFORMATION);
}
else
if
(g_pMouseInput
->
MiddleButton()
==
BUTTONSTATE_PRESSED)
{
sprintf(tmpText,
"
鼠标滚轮已按下,X-Y坐标为(%d,%d)
"
,point.x,point.y);
MessageBox(NULL,tmpText,
"
提示
"
,MB_OK
|
MB_ICONINFORMATION);
}
else
if
(g_pMouseInput
->
RightButton()
==
BUTTONSTATE_PRESSED)
{
sprintf(tmpText,
"
鼠标右键已按下,X-Y坐标为(%d,%d)
"
,point.x,point.y);
MessageBox(NULL,tmpText,
"
提示
"
,MB_OK
|
MB_ICONINFORMATION);
}
}
我们为Initialize新增一个HINSTANCE hInst参数,这个参数在初始化鼠标输入设备时要用到,之后,通过
g_pMouseInput = new CMouseInput;
g_pMouseInput->Initialize(hInst,hWnd);
来完成鼠标输入设备的声明及初始化。
我们在Update函数中调用鼠标输入单元检测函数TestMouseInput。该函数中,首先要通过
g_pMouseInput->GetState();
获取当前的设备状态,其实也就是输入信息的读取。之后便可对鼠标各个键位状态进行检测。
当然,最后不要忘记在Dispose函数中调用
ReleaseCOM(g_pMouseInput);//联动调用CMouseInput::Release()函数
将我们构建的鼠标输入设备释放掉~ 这是C++环境,GC的工作只能我们自己完成咯 ^ ^
如下为效果图:
回到最初,我们之所以认为DirectInput是一种更加高效的机制,是因其基于硬件驱动实现。
感兴趣的朋友不妨做个实验:
在 开始--->控制面板--->鼠标 中更换左右键功能,DirectInput设备不受影响,因为控制面板里的设置是基于软件实现的~
Good Luck~