我们把对DirectInput进行初始化的过程放在DXInit函数中。
#include <dinput.h> //DirectInput头文件
LPDIRECTINPUT8 g_DI; //DirectInput对象指针
LPDIRECTINPUTDEVICE8 g_KDIDev; //DirectInput设备对象指针
int DXInit()
{
//先创建DirectInput对象
if ( DirectInput8Create(hInst, // 应用程序实例句柄,
// 这里的hInst是全局变量
DIRECTINPUT_VERSION, // Direct Input版本
IID_IDirectInput8, // 版本为8
(void**)&g_DI, // Direct Input对象
NULL ) ) // 指向IUnknown接口的指针,通常取NULL
{
return FALSE; // 初始化失败就返回FALSE
}
// 创建键盘设备
if ( g_DI->CreateDevice(GUID_SysKeyboard, &g_KDIDev, NULL ) )
{
return FALSE; // 创建失败就返回FALSE
}
// 设置键盘设备的数据格式
if ( g_KDIDev->SetDataFormat(&c_dfDIKeyboard) )
{
return FALSE; // 设置失败返回FALSE
}
DirectInput还需要设置设备的工作协作等级,其中DISCL_BACKGROUND和DISCL_FOREGROUND必须选择一种,分别表示后台和前台数据接收。当选择前者时,表示即便应用程序非当前活动程序,DirectInupt仍然会接收输入数据,并且根据程序的设计来进入不同的流程。后者则表示只有程序是当前活动程序,DirectInput才会接收输入数据,并且一旦应用程序切换到后台,就会自动失去设备。
DISCL_NONEXCLUSIVE和DISCL_EXCLUSIVE也必须选择一种,前者表示对输入设备的使用的非独占式使用,不会影响到其他应用程序对该设备的使用。后者表示对输入设备独占使用,当程序获得设备时,其他程序就不能使用该设备,哪怕是非独占式使用都不行
if ( g_KDIDev->SetCooperativeLevel(g_hWnd, DISCL_FOREGROUND |
DISCL_NONEXCLUSIVE) )
{
return FALSE; // 设置失败返回FALSE
}
if (g_KDIDev)
g_KDIDev->Acquire(); // 创建设备成功后需要获得设备,
//否则无法接收输入数据
else
return FALSE;
return TRUE; // 初始化成功,返回TRUE
}
和DirectInput初始化相反的过程就是关闭DirectInput,和OpenGL一样,我们也放在一个DXShutdown函数中处理。
void DXShutdown()
{
if (g_DI)
{
if (g_KDIDev)
{
g_KDIDev->Unacquire();
g_KDIDev->Release();
g_KDIDev = NULL;
}
g_DI->Release();
g_DI = NULL;
}
}
因为我们设置的DirectInput的协作等级为前台非独占式工作模式,因此当应用程序切换到后台后,会自动失去设备。为了使得程序重新切换回前台时,设备继续正常工作,我们必须手动的获取它,应用程序是不会自动获取设备的。对设备的重新获取,放在Wndproc回调函数中。
LRESULT CALLBACK WndProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case WM_ACTIVATE: //程序处于活动状态
{
if (!HIWORD(wParam))
{
active=TRUE;
}
else
{
active=FALSE;
}
// 如果键盘设备为空,就直接返回
if (NULL == g_KDIDev)
return S_FALSE;
if (active)
{
// 获得输入设备
g_KDIDev->Acquire();
}
else
{
// 失去输入设备
g_KDIDev->Unacquire();
}
return 0;
}
...
}
DirectInput的初始化过程也放在OpenGL的初始化过程中,如果DirectInput初始化不成功,就返回失败。
int glInit(GLvoid)
{
if (!DXInit()) // 初始化DirectInput
{
return FALSE; //失败,返回
}
...
return TRUE;
}
DirectInput的关闭也整合在OpenGL的关闭过程中。
void glShutdown(GLvoid)
{
……
DXShutdown(); //关闭DirectInput
}
当DirectInput设备全部就绪后,就可以获取输入数据了,GetDeviceState完成对设备输入数据的记录,这个过程在程序的主循环流程glMain中。
BYTE buffer[256];
int glMain(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
//获取输入设备的数据,保存在buffer中
HRESULT hr = g_KDIDev->GetDeviceState(sizeof(buffer), &buffer);
//对键盘输入进行处理
if(!KeyPressed())
return FALSE;
gluLookAt(
g_Camera.m_vPosition.x, g_Camera.m_vPosition.y, g_Camera.m_vPosition.z, g_Camera.m_vView.x, g_Camera.m_vView.y, g_Camera.m_vView.z,
g_Camera.m_vUpVector.x,g_Camera.m_vUpVector.y,g_Camera.m_vUpVector.z);
//绘制表示地面的网格
Draw3DSGrid();
//创建四个金字塔
CreatePyramid(-6, 0, 6, 1, 1);
CreatePyramid(6, 0, 6, 1, 1);
CreatePyramid(6, 0, -6, 1, 1);
CreatePyramid(-6, 0, -6, 1, 1);
SwapBuffers(g_hDC);
return TRUE;
}
对键盘的处理就很简单了,这里我们需要程序做出反应的只有6个键:F1、ESC和四个方向键。
int KeyPressed()
{
if(buffer[DIK_ESCAPE]) //这里的值都是DIK_***,而不是VK_***
return FALSE;
if (buffer[DIK_F1]) //F1切换全屏和窗口模式
{
buffer[DIK_F1]=FALSE;
glShutdown();
fullscreen=!fullscreen;
if (!InitInstance(WIDTH, HEIGHT, BITS))
{
return FALSE;
}
}
if(buffer[DIK_UP]) //前进
{
g_Camera.MoveCamera(kSpeed);
}
if(buffer[DIK_DOWN]) //后退
{
g_Camera.MoveCamera(-kSpeed);
}
if(buffer[DIK_LEFT]) //向左转
{
g_Camera.RotateView(kSpeed, 0, 1, 0);
}
if(buffer[DIK_RIGHT]) //向右转
{
g_Camera.RotateView(-kSpeed, 0, 1, 0);
}
return TRUE;
}