使用DirectInput来控制游戏

使用DirectInput来控制游戏
DirectInput是我很久以前就接触的了。在一个学期前,我就调试过由Allen Sherrod编写的程序,由于对DirectInput不了解,所以我只能从程序的表象来认识它,没有从内部了解它的原理。

以前文章这里

在这个寒假我看了很多的游戏编程的书,它们都介绍了怎样使用DirectInput进行游戏编程,这才使我对它有了更深层次的了解。但是争论还是存在的。主要原因是到底该不该使用这个技术。正方观点认为DirectInput可以绕开windows消息队列机制,能够更即时地操作硬件,这对游戏的操作来说是至关重要的。反方观点则用微软自己官方的建议来说明使用DirectInput的效果不如其它效果。至于我,由于自己还未涉足项目,所以不好说孰优孰劣。但是自从我实现了DirectInput后我就觉得应当使用这个技术,因为它在处理即时事件的效果确实比windows的消息队列机制要好。
顺便说明一下,还有一个方法可以实现按键的功能。那就是GetKeyState()和GetAsyncKeyState()函数。其中GetKeyState()用来处理延时的消息,而GetAsyncKeyState()采用了异步操作,可以处理即时的消息。对于windows开发者来说,使用一个函数要比建立对象并且初始化,程序结束后删除对象、释放空间要快得多。但是这或许对小游戏是如此,但是对于专业的游戏呢?你们看到一些3D游戏使用的是方向盘,另一些使用的是电子枪,而更多的或许是手柄。这些使用windows的消息机制又是怎样映射呢?对于键盘和鼠标的消息,windows可以转换为ASCII码,但是那些游戏交互设备的消息windows就无能为力了,所以我们只好退而求其次:使用虽然复杂但是更加专业的DirectInput了。
一些书和博客上详尽地讲述了DirectInput的使用。这里我就我遇到的情况进行分析讲述,希望能够解大家的困惑。
我知道当初为什么Allen Sherrod会编写错误的代码了。因为它引用的库函数和我们开发的不同。也就是说,Direct的SDK版本不一致。诚然,Allen Sherrod没错,我们的电脑也没错,就是版本的不一致而使得他们的代码变得难以辨认。我在看了一些游戏编程的书后,决定不再沿着Allen Sherrod的老路走了,因为自己已经创建了一个win32的程序框架,有什么理由不让我在自己的代码的基础上进行开发呢?
好吧,我们行动了!
首先我要说明的是,我使用的是XP系统+VS2005,配上的DirectX SDK版本是“Microsoft DirectX SDK (April 2006)”,比较老了,但是还挺好用。以后如果是任何运行不了我程序的问题,可以参照一下我的配置,问题应该出现在这儿。
接下来我就要说的就是我遇到的问题了。首先我在遵循书上的代码进行编译的时候,发现有一个编译错误和两个连接错误。编译错误是我在写#include<dinput.h>的时候,它提醒我没有定义DIRECTINPUT_VERSION这个宏。经过它们的提醒,我在include语句前面加上了define语句,将宏的值定义为0x0800,错误就消失了。另外两个连接错误就有些难办了。我开始想是不是少用了include语句添加头文件呢?结果我在include文件夹找到了一些类似的头文件,结果还是没有解决问题。后来根据我的经验,发现是没有这个函数的实现造成的。我就想是不是少连接了什么库函数。于是在这种想法的驱使下,我打开了以前Allen Sherrod的代码,这个代码是我修改过的,应该没有什么问题。结果我发现,我的代码相比我以前修改的代码,少了两句“#pragma comment( lib, "dinput8.lib")和#pragma comment( lib, "dxguid.lib")”。我在加上这两句后,编译,连接,一切正常。这个问题终于解决了。
现在我给大家分享一下我使用DirectInput(键盘)的步骤:
1、在全局定义一个LPDIRECTINPUT8和LPDIRECTINPUTDEVICE8对象,初始化为NULL。
2、使用DirectInput8Create()函数来创建对象。
3、使用LPDIRECTINPUT8对象的成员函数CreateDevice()来创建设备。
4、使用LPDIRECTINPUTDEVICE8对象的成员函数SetDataFormat()来设置数据格式。
5、使用LPDIRECTINPUTDEVICE8对象的成员函数SetCooperativeLevel()来设置合作等级,这里非独占有,程序前台控制有效的标志符最好。
6、使用LPDIRECTINPUTDEVICE8对象的成员函数Acquire()来获取输入设备。
7、使用LPDIRECTINPUTDEVICE8对象的成员函数GetDeviceState()来将键盘的按键映射到一个长度为256字节的字符数组中。
8、现在可以使用按位与运算和0x80进行运算,判断按键是否被按下。
9、程序关闭后先使用LPDIRECTINPUTDEVICE8对象的成员函数Unacquire()来解除获取输入设备。并且调用Release()函数进行对象内存空间的释放。
听起来听复杂的,但是为了在大型的游戏中获取比windows消息更优越的性能,这一点工作还是值得的。
我自己做了一个程序,将它和我以前使用windows消息机制的程序作对比,发现使用DirectInput果然好用。它可以连贯、流畅地响应按键,而使用windows消息的话则没有那么连贯。举个例子吧,我在以前的程序中,按着一个键不放,精灵图形先走一步,顿一下,再进行连贯的行走;而使用DirectInput可以没有停顿地进行行走,更好的是它支持组合按键同时响应。我试了下,按紧上和左,精灵图片就往左上走,这多么的方便啊!

以下把我的程序贴出来。想要整个工程的话,可以下载。因为它还包含了背景图片、精灵图片、动态鼠标指针等其它东西。

Code:
  1. /*---------------------------------------------------------------------------
  2. 蒋轶民制作E-mail:jiangcaiyang123@163.com
  3. 文件名:MainFrame.cpp
  4. 作用:使用DirectInput来控制游戏
  5. ----------------------------------------------------------------------------*/
  6. /*--------------------------------------------------------------------------*/
  7. //头文件
  8. #include<windows.h>
  9. #include<d3d9.h>
  10. #include<d3dx9.h>
  11. #include<cstdio>
  12. #defineDIRECTINPUT_VERSION0x0800//使用DirectInput前需要确定DirectInput版本
  13. #include<dinput.h>
  14. //库文件
  15. #pragmacomment(lib,"d3d9.lib")
  16. #pragmacomment(lib,"d3dx9.lib")
  17. #pragmacomment(lib,"dinput8.lib")
  18. #pragmacomment(lib,"dxguid.lib")
  19. //定义的宏
  20. #defineJCLASSNAME"优化的程序"
  21. #defineJCAPTION"程序演示"
  22. #defineWINDOW_WIDTH320
  23. #defineWINDOW_HEIGHT240
  24. #defineFULLSCREENFALSE//全屏非全屏的标识符
  25. #defineSAFE_RELEASE(p)if(p)p->Release();p=NULL;
  26. #defineKEYDOWN(name,key)(name[key]&0x80)
  27. //调整编译器设置
  28. #pragmawarning(disable:4100)
  29. #ifFULLSCREEN//全屏非全屏的设置
  30. #defineWINDOW_STYLEWS_EX_TOPMOST|WS_POPUP|WS_VISIBLE
  31. #else
  32. #defineWINDOW_STYLEWS_CAPTION|WS_SYSMENU
  33. #endif
  34. /*----------------------------------------------------------------------------*/
  35. //全局变量
  36. LPDIRECT3D9g_JD3D=NULL;//D3D结构体
  37. LPDIRECT3DDEVICE9g_JDevice=NULL;//D3D装置结构体
  38. LPD3DXFONTg_FPSFont=NULL;//指向FPS字体的指针
  39. RECTg_FPSFontPos={WINDOW_WIDTH-100,WINDOW_HEIGHT-15,
  40. WINDOW_WIDTH,WINDOW_HEIGHT};//FPS所在的矩形框
  41. INTg_FrameCount=0;//帧的计数器
  42. INTg_lastTime=0;//记录上一秒的时间
  43. INTg_currentTime=0;//记录当前的时间
  44. CHARg_FPSstr[25]={0};//记录当前帧率的字符串
  45. IDirect3DSurface9*g_Surface=NULL;//平面的指针
  46. LPDIRECT3DTEXTURE9g_pTexture=NULL;//图像纹理指针
  47. LPD3DXSPRITEg_pSprite=NULL;//子图形指针
  48. RECTg_SpriteRect={0,0,0,0};//子图形所在的矩形
  49. D3DXVECTOR3g_vCenter(0.0f,0.0f,0.0f);//子图形中心的向量
  50. D3DXVECTOR3g_vPosition(WINDOW_WIDTH/2,WINDOW_HEIGHT/2,0.0f);//子图形位置的向量
  51. LPDIRECTINPUT8g_JInputObject=NULL;//DirectInput8的对象
  52. LPDIRECTINPUTDEVICE8g_JInputDevice=NULL;//DirectInput的设备
  53. /*----------------------------------------------------------------------------*/
  54. //初始化Direct3D函数
  55. BOOLInitializeD3D(HINSTANCEhInst,HWNDhWnd)
  56. {
  57. D3DDISPLAYMODEdisplayMode;
  58. //创建D3D对象
  59. g_JD3D=Direct3DCreate9(D3D_SDK_VERSION);
  60. if(g_JD3D==NULL)
  61. returnFALSE;
  62. //获取显示器的模式
  63. if(FAILED(g_JD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&displayMode)))
  64. returnFALSE;
  65. //D3D显示的参数
  66. D3DPRESENT_PARAMETERSjd3dpp;
  67. ZeroMemory(&jd3dpp,sizeof(jd3dpp));
  68. //初始化D3DPRESENT_PARAMETERS
  69. jd3dpp.Windowed=!FULLSCREEN;
  70. jd3dpp.BackBufferWidth=WINDOW_WIDTH;
  71. jd3dpp.BackBufferHeight=WINDOW_HEIGHT;
  72. jd3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;
  73. jd3dpp.BackBufferFormat=displayMode.Format;
  74. jd3dpp.EnableAutoDepthStencil=TRUE;
  75. jd3dpp.AutoDepthStencilFormat=D3DFMT_D16;
  76. //创建设备
  77. if(FAILED(g_JD3D->CreateDevice(D3DADAPTER_DEFAULT,
  78. D3DDEVTYPE_HAL,hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING,
  79. &jd3dpp,&g_JDevice)))
  80. returnFALSE;
  81. //设置渲染状态
  82. g_JDevice->SetRenderState(D3DRS_LIGHTING,FALSE);
  83. g_JDevice->SetRenderState(D3DRS_ZENABLE,D3DZB_TRUE);
  84. //创建字体
  85. if(FAILED(D3DXCreateFont(g_JDevice,15,0,1,1,0,DEFAULT_CHARSET,
  86. OUT_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH|FF_DONTCARE,
  87. "Impact",&g_FPSFont)))returnFALSE;
  88. //创建离屏平面
  89. if(FAILED(g_JDevice->CreateOffscreenPlainSurface(WINDOW_WIDTH,WINDOW_HEIGHT,
  90. D3DFMT_X8R8G8B8,//平面格式
  91. D3DPOOL_DEFAULT,//内存池格式
  92. &g_Surface,//保存结果的缓存
  93. NULL)))//保留参数
  94. returnFALSE;
  95. //载入背景平面图像
  96. if(FAILED(D3DXLoadSurfaceFromFile(g_Surface,NULL,NULL,"测试的临时用图.png",
  97. NULL,D3DX_DEFAULT,0,NULL)))returnFALSE;
  98. //载入子图像(精灵图像)
  99. D3DXIMAGE_INFOinfo;
  100. D3DXGetImageInfoFromFile("采用Alpha混合的精灵图.png",&info);//提取图片信息
  101. if(FAILED(D3DXCreateTextureFromFileEx(g_JDevice,
  102. "采用Alpha混合的精灵图.png",info.Width,info.Height,D3DFMT_FROM_FILE,
  103. 0,D3DFMT_A8R8G8B8,D3DPOOL_MANAGED,D3DX_FILTER_NONE,D3DX_DEFAULT,
  104. D3DCOLOR_XRGB(240,194,180),NULL,NULL,&g_pTexture)))returnFALSE;
  105. //设置min和mag滤波
  106. g_JDevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
  107. g_JDevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
  108. //设置三角形的背面不被剔除
  109. g_JDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
  110. //创建子图形
  111. if(FAILED(D3DXCreateSprite(g_JDevice,&g_pSprite)))returnfalse;
  112. g_SpriteRect.right=info.Width;
  113. g_SpriteRect.bottom=info.Height;
  114. //创建DirectInput
  115. if(FAILED(DirectInput8Create(hInst,DIRECTINPUT_VERSION,IID_IDirectInput8,
  116. (void**)&g_JInputObject,NULL)))
  117. {
  118. MessageBox(NULL,"创建DirectInput对象失败。","程序消息",MB_OK);
  119. returnFALSE;
  120. }
  121. //创建DirectInput设备
  122. if(FAILED(g_JInputObject->CreateDevice(GUID_SysKeyboard,
  123. &g_JInputDevice,NULL)))
  124. {
  125. MessageBox(NULL,"创建DirectInput设备失败。","程序消息",MB_OK);
  126. returnFALSE;
  127. }
  128. //设置数据格式
  129. if(FAILED(g_JInputDevice->SetDataFormat(&c_dfDIKeyboard)))
  130. {
  131. MessageBox(NULL,"设置数据格式失败。","程序消息",MB_OK);
  132. returnFALSE;
  133. }
  134. //设置合作等级
  135. if(FAILED(g_JInputDevice->SetCooperativeLevel(hWnd,
  136. DISCL_NONEXCLUSIVE|DISCL_FOREGROUND)))//非独占有,程序前台控制有效
  137. {
  138. MessageBox(NULL,"设置合作等级失败。","程序消息",MB_OK);
  139. returnFALSE;
  140. }
  141. //获取输入设备
  142. if(FAILED(g_JInputDevice->Acquire()))
  143. {
  144. MessageBox(NULL,"取得输入装置失败。","程序消息",MB_OK);
  145. returnFALSE;
  146. }
  147. returnTRUE;
  148. }
  149. /*----------------------------------------------------------------------------*/
  150. //释放所有资源
  151. voidReleaseMemory(void)
  152. {
  153. SAFE_RELEASE(g_JD3D);
  154. SAFE_RELEASE(g_JDevice);
  155. SAFE_RELEASE(g_FPSFont);
  156. SAFE_RELEASE(g_Surface);
  157. g_JInputDevice->Unacquire();
  158. SAFE_RELEASE(g_JInputDevice);
  159. SAFE_RELEASE(g_JInputObject);
  160. }
  161. /*----------------------------------------------------------------------------*/
  162. //程序的交互
  163. voidGameInteraction(void)
  164. {
  165. charstate[256];//键盘的状态
  166. //取得设备的状态
  167. if(FAILED(g_JInputDevice->GetDeviceState(sizeof(state),state)))
  168. {
  169. MessageBox(NULL,"获取设备状态失败。","程序消息",MB_OK);
  170. return;
  171. }
  172. //检测某个按键是否按下
  173. if(KEYDOWN(state,DIK_LEFT))
  174. g_vPosition.x-=2;
  175. if(KEYDOWN(state,DIK_RIGHT))
  176. g_vPosition.x+=2;
  177. if(KEYDOWN(state,DIK_UP))
  178. g_vPosition.y-=2;
  179. if(KEYDOWN(state,DIK_DOWN))
  180. g_vPosition.y+=2;
  181. }
  182. /*----------------------------------------------------------------------------*/
  183. //渲染屏幕
  184. voidRenderScene(void)
  185. {
  186. //计数器开始计数(以毫秒计)
  187. g_currentTime=GetTickCount();
  188. if(g_currentTime-g_lastTime>1000)
  189. {
  190. sprintf_s(g_FPSstr,25,"当前FPS:%d",g_FrameCount);
  191. g_lastTime=g_currentTime;
  192. g_FrameCount=0;
  193. }
  194. elseg_FrameCount++;
  195. g_JDevice->Clear(0,NULL,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
  196. D3DCOLOR_XRGB(0,0,0),1.0f,0);//清除屏幕
  197. //载入背景平面图像
  198. IDirect3DSurface9*backbuffer=NULL;
  199. g_JDevice->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,
  200. &backbuffer);//获取后台缓存
  201. g_JDevice->StretchRect(g_Surface,NULL,
  202. backbuffer,NULL,D3DTEXF_NONE);//将后台矩形放入前景矩形
  203. //开始渲染
  204. g_JDevice->BeginScene();
  205. //渲染Sprite
  206. g_pSprite->Begin(D3DXSPRITE_ALPHABLEND);//以Alpha混合形式进行渲染
  207. g_pSprite->Draw(g_pTexture,&g_SpriteRect,&g_vCenter,&g_vPosition,
  208. D3DCOLOR_XRGB(255,255,255));
  209. g_pSprite->End();//结束渲染
  210. g_FPSFont->DrawText(NULL,g_FPSstr,-1,&g_FPSFontPos,DT_CENTER,
  211. D3DCOLOR_XRGB(255,255,255));//显示字体
  212. //结束渲染
  213. g_JDevice->EndScene();
  214. g_JDevice->Present(NULL,NULL,NULL,NULL);
  215. }
  216. /*----------------------------------------------------------------------------*/
  217. //回调函数,用来处理消息
  218. HRESULTCALLBACKMyAppProc(HWNDhWnd,UINTmsg,WPARAMwParam,LPARAMlParam)
  219. {
  220. switch(msg)
  221. {
  222. caseWM_DESTROY:
  223. PostQuitMessage(0);
  224. return0;
  225. caseWM_KEYDOWN:
  226. if(wParam==VK_ESCAPE)PostQuitMessage(0);
  227. return0;
  228. }
  229. return(HRESULT)DefWindowProc(hWnd,msg,wParam,lParam);
  230. };
  231. /*----------------------------------------------------------------------------*/
  232. //程序的入口,主函数
  233. INTWINAPIWinMain(HINSTANCEhInst,HINSTANCEhPrevInst,LPSTRcmd,INTshow)
  234. {
  235. //设置WindowClass结构并且注册它
  236. WNDCLASSEXjWndCls={sizeof(jWndCls),CS_CLASSDC,MyAppProc,0L,0L,hInst,
  237. NULL,LoadCursorFromFile("自定义鼠标指针.ani"),0,NULL,JCLASSNAME,NULL};
  238. RegisterClassEx(&jWndCls);
  239. //设置窗口并且显示窗口
  240. HWNDhWnd=CreateWindow(JCLASSNAME,JCAPTION,WINDOW_STYLE,100,40,
  241. WINDOW_WIDTH,WINDOW_HEIGHT,GetDesktopWindow(),NULL,jWndCls.hInstance,NULL);
  242. if(hWnd==NULL)
  243. returnFALSE;
  244. ShowWindow(hWnd,SW_SHOWDEFAULT);
  245. UpdateWindow(hWnd);
  246. //初始化设置
  247. if(InitializeD3D(hInst,hWnd)==FALSE)
  248. returnFALSE;
  249. //进入消息循环
  250. MSGmsg;
  251. ZeroMemory(&msg,sizeof(msg));
  252. while(msg.message!=WM_QUIT)
  253. {
  254. if(PeekMessage(&msg,NULL,0U,0U,PM_REMOVE))
  255. {
  256. TranslateMessage(&msg);
  257. DispatchMessage(&msg);
  258. }
  259. else
  260. {
  261. RenderScene();//渲染屏幕
  262. GameInteraction();//游戏的交互
  263. }
  264. }
  265. //程序结束,释放内存所有资源
  266. ReleaseMemory();
  267. //解除窗口注册
  268. UnregisterClass(JCLASSNAME,jWndCls.hInstance);
  269. return(INT)msg.wParam;
  270. }

程序的截图如下所示:


这个程序还存在着以下两个问题:
1、载入精灵图像的时候,发现和源图像有着一定比例的压缩,图像总是扁一些。我不知道为什么会这样;
2、在进行程序切换,再切回我们编写的程序的时候,DirectInput无法工作,这个问题我暂时没能拿出好的解决方案。

有时间的话,我还会对游戏的其它的部分(例如模型载入、声音系统、脚本系统)进行深入的研究的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值