DirectInput8的简单使用

下面将要介绍一个完整的例子,学习如何用DirectInput从鼠标、键盘、Xbox 360控制器获得输入。这个程序将装载位图来创建简单的2D图像,基于Direct3D表面,而不是D3DXSprite精灵。好了,下面直接进入代码部分,先看看DirectX的头文件(DirectX.h):

 1 #pragma once
 2 
 3 #define WIN32_EXTRA_LEAN
 4 #define DIRECTINPUT_VERSION 0x0800
 5 #include <Windows.h>
 6 #include <d3d9.h>
 7 #include <d3dx9.h>
 8 #include <dinput.h>
 9 #include <XInput.h>
10 #include <ctime>
11 #include <iostream>
12 #include <iomanip>
13 using namespace std; 14 
15 #pragma comment(lib, "winmm.lib")
16 #pragma comment(lib, "user32.lib")
17 #pragma comment(lib, "gdi32.lib")
18 #pragma comment(lib, "dxguid.lib")
19 #pragma comment(lib, "d3d9.lib")
20 #pragma comment(lib, "d3dx9.lib")
21 #pragma comment(lib, "dinput8.lib")
22 #pragma comment(lib, "xinput.lib")
23 
24 extern const string APPTITLE; 25 extern const int SCREENW; 26 extern const int SCREENH; 27 extern bool gameover; 28 
29 extern LPDIRECT3D9 d3d; 30 extern LPDIRECT3DDEVICE9 d3ddev; 31 extern LPDIRECT3DSURFACE9 backbuffer; 32 
33 //Direct3D functions
34 bool Direct3D_Init(HWND hwnd, int width, int height, bool fullscreen); 35 void Direct3D_Shutdown(); 36 bool LoadSurface(string filename, D3DXIMAGE_INFO &info, LPDIRECT3DSURFACE9 &image); 37 void DrawSurface(LPDIRECT3DSURFACE9 dest, float x, float y, LPDIRECT3DSURFACE9 source); 38 
39 //DirectInput objects, devices, and states
40 extern LPDIRECTINPUT8 dinput; 41 extern LPDIRECTINPUTDEVICE8 dimouse; 42 extern LPDIRECTINPUTDEVICE8 dikeyboard; 43 extern DIMOUSESTATE mouse_state; 44 extern XINPUT_GAMEPAD controllers[4]; 45 
46 //DirectInput functions
47 bool DirectInput_Init(HWND); 48 void DirectInput_Update(); 49 void DirectInput_Shutdown(); 50 int Key_Down(int); 51 int Mouse_Button(int); 52 int Mouse_X(); 53 int Mouse_Y(); 54 void XInput_Vibrate(int contNum = 0, int amount = 65535); 55 bool XInput_Controller_Found(); 56 
57 //game functions
58 bool Game_Init(HWND window); 59 void Game_Run(HWND window); 60 void Game_End();

这个程序没有使用C++中的类,请不要奇怪这种C语言式的风格,感兴趣的可以把它修改为C++的风格。对于#define WIN32_EXTRA_LEAN这段,表示不包含额外类库来编译程序,所以无法在程序中使用MFC。这些定义可以望文生义,但要注意extern关键字,等你看了其他文件就会发现这些变量实际上已经在其他文件定义过了,这里仅表示引用。

 

下面是上一个文件中函数的实现文件DirectX.cpp,函数很多,还是先看看开头部分,定义了鼠标dimouse、键盘dikeyboard、控制器controllers等。

 1 #include "DirectX.h"
 2 #include <iostream>
 3 using namespace std;  4 
 5 //Direct3D variables
 6 LPDIRECT3D9 d3d = NULL;  7 LPDIRECT3DDEVICE9 d3ddev = NULL;  8 LPDIRECT3DSURFACE9 backbuffer = NULL;  9 
10 //DirectInput variables
11 LPDIRECTINPUT8 dinput = NULL; 12 LPDIRECTINPUTDEVICE8 dimouse = NULL; 13 LPDIRECTINPUTDEVICE8 dikeyboard = NULL; 14 DIMOUSESTATE mouse_state; 15 char keys[256]; 16 XINPUT_GAMEPAD controllers[4];

 

接着介绍Direct3D初始化函数,和我前一个简单的DirectX例子中的一样使用Direct3DCreate9创建Direct3D对象,然后设置好所需参数d3dpp。设置好了参数,最后使用d3d->CreateDevice将创建的Direct3D设备保存于变量d3ddev中,可以根据d3ddev获取操作后台缓冲区的指针backbuffer。

 1 bool Direct3D_Init(HWND window, int width, int height, bool fullscreen)  2 {  3     //Initialize Direct3D
 4     d3d = Direct3DCreate9(D3D_SDK_VERSION);  5     if (!d3d)  6         return false;  7 
 8     //set Direct3D presentation parameters
 9  D3DPRESENT_PARAMETERS d3dpp; 10     ZeroMemory(&d3dpp, sizeof(d3dpp)); 11     d3dpp.Windowed = (!fullscreen); 12     d3dpp.SwapEffect = D3DSWAPEFFECT_COPY; 13     d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; 14     d3dpp.BackBufferCount = 1; 15     d3dpp.BackBufferWidth = width; 16     d3dpp.BackBufferHeight = height; 17     d3dpp.hDeviceWindow = window; 18 
19     //create Direct3D device
20     d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, 21         D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev); 22     if (!d3ddev) 23         return false; 24 
25     //get a pointer to the back buffer surface
26     d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer); 27 
28     return true; 29 }

 

 下面是简单的Direct3D关闭函数:

1 void Direct3D_Shutdown() 2 { 3     if (d3ddev) 4         d3ddev->Release(); 5     if (d3d) 6         d3d->Release(); 7 }

 

接着我们一次看两个函数,第一个DrawSurface函数使用了Direct3D表面的知识,这里直接以Direct3D表面为参数,先定义描述变量desc,用source来给它赋值,接着使用desc来创建一个窗口区域source_rect,而要绘图的目标区域为dest_rect,关键在使用StretchRect函数将位图画在了窗口中;第二个函数是LoadSurface,即读取位图文件并将其与LPDIRECT3DSURFACE9对象关联,如果调用了这个函数,就可以通过参数image来操作该图片,而参数info可以用于获取图片信息。这里使用的函数很简单,就不用讲了,只要注意参数前的引用就可以了。

 1 void DrawSurface(LPDIRECT3DSURFACE9 dest, float x, float y, LPDIRECT3DSURFACE9 source)  2 {  3     //get width height from source surface
 4  D3DSURFACE_DESC desc;  5     source->GetDesc(&desc);  6 
 7     //create rects for drawing
 8     RECT source_rect = {0, 0, (long)desc.Width, (long)desc.Height };  9     RECT dest_rect = { (long)x, (long)y, (long)x + desc.Width, (long)y + desc.Height}; 10 
11     //draw the source surface onto the dest
12     d3ddev->StretchRect(source, &source_rect, dest, &dest_rect, D3DTEXF_NONE); 13 } 14 
15 bool LoadSurface(string filename, D3DXIMAGE_INFO &info, LPDIRECT3DSURFACE9 &image) 16 { 17     //get width and height from bitmap file
18     HRESULT result = D3DXGetImageInfoFromFile(filename.c_str(), &info); 19     if (result != D3D_OK) 20         return false; 21 
22     //create surface
23     result = d3ddev->CreateOffscreenPlainSurface( 24  info.Width, info.Height, 25  D3DFMT_X8R8G8B8, 26  D3DPOOL_DEFAULT, 27         &image, 28  NULL); 29     if (result != D3D_OK) 30         return false; 31 
32     //load surface from file into newly created surface
33     result = D3DXLoadSurfaceFromFile( 34         image,    //destination surface
35         NULL,    //destination palette
36         NULL,    //destination rectangle
37  filename.c_str(), 38         NULL,    //source rectangle
39         D3DX_DEFAULT, //controls how image is filtered
40         D3DCOLOR_XRGB(0,0,0), 41         NULL);    //source image info
42     if (result != D3D_OK) 43         return false; 44 
45     return true; 46 }

 

下面是DirectInput的初始化函数,dinput前面已经声明了,这里先用DirectInput8Create来初始化dinput对象。有了这个对象,接着就可以创建各种设备对象了。先是dikeyboard,用系统内置参数c_dfDIKeyboard来设置数据格式,接下来用SetCooperateLevel设置于系统的协作级别,DISCL_NONEXCLUSIVE表示你按下windows键时程序会被打扰,与之相对的是DISCL_EXCLUSIVE;参数DISCL_FOREGROUND表示程序在失去焦点时无法响应按键,与之对应的是DISCL_BACKGROUND参数。而函数Acquire表示程序获取到了设备,可以使用设备,与之配合使用的是unacquire操作。最后的ShowCursor决定是否显示鼠标。

 1 bool DirectInput_Init(HWND hwnd)  2 {  3     //initialize DirectInput object
 4     HRESULT result = DirectInput8Create(  5  GetModuleHandle(NULL),  6  DIRECTINPUT_VERSION,  7  IID_IDirectInput8,  8         (void**)&dinput,  9  NULL); 10 
11     //initialize the keyboard
12     dinput->CreateDevice(GUID_SysKeyboard, &dikeyboard, NULL); 13     dikeyboard->SetDataFormat(&c_dfDIKeyboard); 14     dikeyboard->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); 15     dikeyboard->Acquire(); 16 
17     //initialize the mouse
18     dinput->CreateDevice(GUID_SysMouse, &dimouse, NULL); 19     dimouse->SetDataFormat(&c_dfDIMouse); 20     dimouse->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); 21     dimouse->Acquire(); 22     
23     ShowCursor(false); 24 
25     return true; 26 }

 

接着是设备消息更新函数,不断获取输入设备的状态。

 1 void DirectInput_Update()  2 {  3     //update mouse
 4     dimouse->GetDeviceState(sizeof(mouse_state), (LPVOID)&mouse_state);  5 
 6     //update keyboard
 7     dikeyboard->GetDeviceState(sizeof(keys), (LPVOID)&keys);  8 
 9     //update controllers
10     for (int i = 0; i < 4; i++) 11  { 12         ZeroMemory(&controllers[i], sizeof(XINPUT_STATE)); 13 
14         //get the state of the controller
15  XINPUT_STATE state; 16         DWORD result = XInputGetState(i, &state); 17 
18         //store state in global controllers array
19         if (result == 0) 20             controllers[i] = state.Gamepad; 21  } 22 }

 

下面的几个函数较为简单,Mouse_X与Mouse_Y返回鼠标的位置信息,Mouse_Button返回鼠标是否被点击,这个程序没有使用,而Key_Down表示某个按键是否被按下。

 1 int Mouse_X()  2 {  3     return mouse_state.lX;  4 }  5 
 6 int Mouse_Y()  7 {  8     return mouse_state.lY;  9 } 10 
11 //return mouse button state
12 int Mouse_Button(int button) 13 { 14     return mouse_state.rgbButtons[button] & 0x80; 15 } 16 
17 //return key press state
18 int Key_Down(int key) 19 { 20     return (keys[key] & 0x80); 21 }

 

下面三个函数也很简单,第一个是关闭函数,第二个表示你是否插入了控制器,第三个函数使你的控制器震动。

 1 void DirectInput_Shutdown()  2 {  3     if (dikeyboard)  4  {  5         dikeyboard->Unacquire();  6         dikeyboard->Release();  7         dikeyboard = NULL;  8  }  9 
10     if (dimouse) 11  { 12         dimouse->Unacquire(); 13         dimouse->Release(); 14         dimouse = NULL; 15  } 16 } 17 
18 //returns true if controller is plugged in
19 bool XInput_Controller_Found() 20 { 21  XINPUT_CAPABILITIES caps; 22     ZeroMemory(&caps, sizeof(XINPUT_CAPABILITIES)); 23     XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps); 24     if (caps.Type != 0) 25         return false; 26 
27     return true; 28 } 29 
30 //vibrates the controller
31 void XInput_Vibrate(int contNum, int amount) 32 { 33  XINPUT_VIBRATION vibration; 34     ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION)); 35     vibration.wLeftMotorSpeed = amount; 36     vibration.wRightMotorSpeed = amount; 37     XInputSetState(contNum, &vibration); 38 }

 

 DirectX.cpp文件到这里就介绍完了,下面再讲个简单的Windows窗口函数文件,即main.cpp文件:我这里不介绍这个文件了,可以参考我的创建简单的Directx9程序这篇文章。

 1 #include "DirectX.h"
 2 using namespace std;  3 
 4 bool gameover = false;  5 
 6 //windows event handling function
 7 LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  8 {  9     switch (message) 10  { 11     case WM_DESTROY: 12         gameover = true; 13         PostQuitMessage(0); 14         return 0; 15  } 16 
17     return DefWindowProc(hwnd, message, wParam, lParam); 18 } 19 
20 
21 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow) 22 { 23     //set the windows properties
24  WNDCLASSEX wc; 25     wc.cbSize = sizeof(WNDCLASSEX); 26     wc.style = CS_HREDRAW | CS_VREDRAW; 27     wc.lpfnWndProc = (WNDPROC)WinProc; 28     wc.cbClsExtra = 0; 29     wc.cbWndExtra = 0; 30     wc.hInstance = hInstance; 31     wc.hIcon = NULL; 32     wc.hCursor = LoadCursor(NULL, IDC_ARROW); 33     wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 34     wc.lpszMenuName = NULL; 35     wc.lpszClassName = APPTITLE.c_str(); 36     wc.hIconSm = NULL; 37     RegisterClassEx(&wc); 38 
39     //determine the resolution of the clients desktop screen
40     int screenWidth = GetSystemMetrics(SM_CXSCREEN); 41     int screenHeight = GetSystemMetrics(SM_CYSCREEN); 42 
43     //place the window in the middle of screen
44     int posX = (GetSystemMetrics(SM_CXSCREEN) - SCREENW) / 2; 45     int posY = (GetSystemMetrics(SM_CYSCREEN) - SCREENH) / 2; 46 
47     //Create a window
48  HWND window; 49     window = CreateWindow(APPTITLE.c_str(), APPTITLE.c_str(), 50         WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, posX, posY, SCREENW, SCREENH, 51  NULL, NULL, hInstance, NULL); 52     if (window == 0) 53         return false; 54 
55     //display the window
56  ShowWindow(window, nCmdShow); 57  UpdateWindow(window); 58 
59     //initialize the game
60     if (!Game_Init(window)) 61         return 0; 62 
63     //main message loop
64  MSG msg; 65     while (!gameover) 66  { 67         //process windows events
68         if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 69  { 70             //handle any event messages
71             TranslateMessage(&msg); 72             DispatchMessage(&msg); 73  } 74 
75         //process game loop
76  Game_Run(window); 77  } 78 
79     //free game resources
80  Game_End(); 81 
82     return msg.wParam; 83 }

 

最后是本程序的游戏逻辑处理文件game.cpp,这部分代码也不难,根据注释就可完全看懂。其中创建了两个结构体,bomb表示炸弹,bucket表示要接住炸弹的篮子,接不住就输了,这好像没什么道理,不过能玩就行。核心处理函数还是Game_Run,你可以使用A、D键控制移动,也可以使用鼠标与手柄玩,不过我没有测试手柄部分。

View Code
 1 #include "DirectX.h"
 2 using namespace std;  3 
 4 const string APPTITLE = "BombGame";  5 const int SCREENW = 1024;  6 const int SCREENH = 576;  7 
 8 LPDIRECT3DSURFACE9 bomb_surf = NULL;  9 LPDIRECT3DSURFACE9 bucket_surf = NULL;  10 int bombSizeX;//size of picture
 11 int bombSizeY;  12 int bucketSizeX;  13 int bucketSizeY;  14 
 15 //bomb
 16 struct BOMB  17 {  18     float x, y;  19     void reset()  20  {  21         x = (float)(rand() % (SCREENW - bombSizeX));  22         y = 0;  23  }  24 };  25 BOMB bomb;  26 
 27 //bucket
 28 struct BUCKET  29 {  30     float x, y;  31 };  32 BUCKET bucket;  33 
 34 int score = 0;  35 int vibrating = 0;  36 
 37 //game initialization
 38 bool Game_Init(HWND window)  39 {  40     Direct3D_Init(window, SCREENW, SCREENH, false);  41  DirectInput_Init(window);  42 
 43     D3DXIMAGE_INFO info;//picture info  44 
 45     //load surface bomb, initialize picture info
 46     if (!LoadSurface("bomb.bmp", info, bomb_surf))  47  {  48         MessageBox(window, "Error loading surface bomb", "Error", 0);  49         return false;  50  }  51     bombSizeX = info.Width;  52     bombSizeY = info.Height;  53 
 54     //load surface bucket, initialize picture info
 55     if (!LoadSurface("bucket.bmp", info, bucket_surf))  56  {  57         MessageBox(window, "Error loading surface bucket", "Error", 0);  58         return false;  59  }  60     bucketSizeX = info.Width;  61     bucketSizeY = info.Height;  62 
 63     //get the back buffer surface
 64     d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);  65 
 66     //position the bomb
 67     srand( (unsigned int)time(NULL) );  68  bomb.reset();  69 
 70     //position the bucket
 71     bucket.x = SCREENW / 2;  72     bucket.y = SCREENH - bucketSizeY;  73 
 74     return true;  75 }  76 
 77 //game update
 78 void Game_Run(HWND window)  79 {  80     const float bombSpeed = 2.0f;  81     const float bucketSpeed = 8.0f;  82 
 83     //make sure the Direct3D device is valid
 84     if (!d3ddev)  85         return;  86 
 87     //update input devices
 88  DirectInput_Update();  89 
 90     //move the bomb down the screen
 91     bomb.y += bombSpeed;  92 
 93     //see if bomb hit the floor
 94     if (bomb.y > SCREENH)  95  {  96         MessageBox(0, "The bomb exploded!", "Over", 0);  97         gameover = true;  98  }  99 
100     //move the bucket with the mouse
101     int mx = Mouse_X(); 102     if (mx < 0) 103         bucket.x -= bucketSpeed; 104     else if (mx > 0) 105  { 106         bucket.x += bucketSpeed; 107  } 108 
109     //move the bucket with the keyboard
110     if (Key_Down(DIK_A)) 111         bucket.x -= bucketSpeed; 112     else if (Key_Down(DIK_D)) 113         bucket.x += bucketSpeed; 114 
115     //move the bucket with controller
116     if (XInput_Controller_Found()) 117  { 118         //left analog thumb stick
119         if (controllers[0].sThumbLX < -5000) 120             bucket.x -= bucketSpeed; 121         else if (controllers[0].sThumbLX > 5000) 122             bucket.x += bucketSpeed; 123 
124         //left and right triggers
125         if (controllers[0].bLeftTrigger > bucketSizeX) 126             bucket.x -= bucketSpeed; 127         else if (controllers[0].bRightTrigger > bucketSizeY) 128             bucket.x += bucketSpeed; 129 
130         //left and right D-PAD
131         if (controllers[0].wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) 132             bucket.x -= bucketSpeed; 133         else if (controllers[0].wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) 134             bucket.x += bucketSpeed; 135 
136         //left and right shoulders
137         if (controllers[0].wButtons & XINPUT_GAMEPAD_DPAD_LEFT) 138             bucket.x -= bucketSpeed; 139         else if (controllers[0].wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) 140             bucket.x += bucketSpeed; 141  } 142 
143     //update vibration
144     if (vibrating > 0) 145  { 146         vibrating++; 147         if (vibrating > 20) 148  { 149             XInput_Vibrate(0, 0); 150             vibrating = 0; 151  } 152  } 153 
154     //keep bucket inside the screen
155     if (bucket.x < 0) 156         bucket.x = 0; 157     if (bucket.x > SCREENW - bucketSizeX) 158         bucket.x = SCREENW - bucketSizeX; 159 
160     //see if bucket caught the bomb
161     int cx = bomb.x + bombSizeX / 2; 162     int cy = bomb.y + bombSizeY / 1.2; 163     if (cx > bucket.x && cx < bucket.x + bucketSizeX &&
164         cy > bucket.y && cy < bucket.y + bucketSizeY) 165  { 166         //update and display score
167         score++; 168         char s[255]; 169 
170         sprintf(s, "%s [SCORE %d]", APPTITLE.c_str(), score); 171  SetWindowText(window, s); 172 
173         //vibrate the controller
174         XInput_Vibrate(0, 65000); 175         vibrating = 1; 176 
177         //restart bomb
178  bomb.reset(); 179  } 180 
181     //clear the backbuffer
182     d3ddev->ColorFill(backbuffer, NULL, D3DCOLOR_XRGB(0, 0, 0)); 183 
184     //start rendering
185     if (d3ddev->BeginScene()) 186  { 187         //draw the bucket
188  DrawSurface(backbuffer, bucket.x, bucket.y, bucket_surf); 189 
190         //draw the bomb
191  DrawSurface(backbuffer, bomb.x, bomb.y, bomb_surf); 192 
193         //stop rendering
194         d3ddev->EndScene(); 195         d3ddev->Present(NULL, NULL, NULL, NULL); 196  } 197 
198     //escape key exits
199     if (Key_Down(DIK_ESCAPE)) 200         gameover = true; 201 
202     //controller back button also exits
203     if (controllers[0].wButtons & XINPUT_GAMEPAD_BACK) 204         gameover = true; 205 } 206 
207 void Game_End() 208 { 209     if (bomb_surf) 210         bomb_surf->Release(); 211     if (bucket_surf) 212         bucket_surf->Release(); 213  DirectInput_Shutdown(); 214  Direct3D_Shutdown(); 215 }

 

最后是程序的运行截图,本程序参照《游戏编程入门》一书而写。游戏在图像上的最大缺点是炸弹与篮子接触时的边缘效果很差,这时使用Direct3D表面的不足,可以使用ID3DXSprite添加对透明效果的支持,源代码

DirectInput8的简单使用_第1张图片

你可能感兴趣的:(input)