简单的DirectX9程序

学了点DirectX9,下面介绍一个简单的Winows程序,采用游戏程序的结构,高手可以离开了。。。只需要一个源文件,在VS2010下成功编译运行,没有用到C++的类,先看看开头部分。由于用到了DirectX类库,所以编译前先要配置好DirectX开发环境,这里不做讲解。

 1 #include <Windows.h>
 2 #include <d3d9.h>
 3 #include <d3dx9.h>
 4 #include <iostream>
 5 #include <time.h>
 6 using namespace std;  7 
 8 #pragma comment (lib, "d3d9.lib")
 9 #pragma comment (lib, "d3dx9.lib")
10 
11 //program settings
12 const string APPTitle = "Create Surface Program"; 13 const int SCREENW = 1024; 14 const int SCREENH = 576; 15 
16 //Direct3D objects
17 LPDIRECT3D9 d3d = NULL; 18 LPDIRECT3DDEVICE9 d3ddev = NULL; 19 LPDIRECT3DSURFACE9 backbuffer = NULL; 20 LPDIRECT3DSURFACE9 surface = NULL; 21 
22 bool gameover = false; 23 
24 //macro to detect key presses
25 #define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)

在上面这段代码中声明了Direct3D objects,其中的LPDIRECT3D9、LPDIRECT3DDEVICE9可能让人费解,在VS2010中可以看到这样的解释:

typedef struct IDirect3D9 *LPDIRECT3D9, *PDIRECT3D9;

typedef struct IDirect3DDevice9 *LPDIRECT3DDEVICE9, *PDIRECT3DDEVICE9;

明显看出这里LP意为long pointer,LPDIRECT3D9就表示IDirect3D9,既一个接口Interface,指向Direct3D9接口的长指针;同理,LPDIRECT3DDEVICE9也表示接口,代表设备,既显卡。那么LPDIRECT3DSURFACE9呢?这个接口表示Direct3D表面,可以用于装载并绘制位图,这个程序就要实现这个功能。感觉这个表面的概念难以理解,不过习惯了就行,可以看成一个画布,可以画图就行。其中还声明了一个backbuffer,既后台缓冲区,这里不得不提“双缓冲”的概念,简单讲就是在后台缓冲区填充好要画到屏幕上的图像,然后直接复制到前台缓冲(屏幕)上显示,避免闪烁问题,这是最常用的图形显示技术。

最后是个简单的宏,用于响应用户按键,因为扫描码的最低7为(0~6位)是描述码值,而最高位(第7位)就描述该键是否被按下了,所以要想知道该键是否被按下就必须与0x80(1000000)相与。

第一个要介绍的函数是游戏初始化函数:

 1 /*
 2 game initialization function  3 */
 4 bool Game_Init(HWND window)  5 {  6     //initialize Direct3D
 7     d3d = Direct3DCreate9(D3D_SDK_VERSION);  8     if (d3d == NULL)  9  { 10         MessageBox(window, "Error initializing Direct3D", "Error", MB_OK); 11         return false; 12  } 13 
14     //set Direct3D presentation parameters
15  D3DPRESENT_PARAMETERS d3dpp; 16     ZeroMemory(&d3dpp, sizeof(d3dpp)); 17     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; 18     d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; 19     d3dpp.BackBufferCount = 1; 20     d3dpp.BackBufferWidth = SCREENW; 21     d3dpp.BackBufferHeight = SCREENH; 22     d3dpp.hDeviceWindow = window; 23     d3dpp.Windowed = TRUE; 24 
25     //create Direct3D device
26     d3d->CreateDevice( 27  D3DADAPTER_DEFAULT, 28  D3DDEVTYPE_HAL, 29  window, 30  D3DCREATE_SOFTWARE_VERTEXPROCESSING, 31         &d3dpp, 32         &d3ddev); 33     if (!d3ddev) 34  { 35         MessageBox(window, "Error creating Direct3D device", "Error", MB_OK); 36         return false; 37  } 38 
39     //clear the backbuffer to black
40     d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); 41 
42     //create surface
43     HRESULT result = d3ddev->CreateOffscreenPlainSurface( 44         SCREENW,            //width of the surface
45         SCREENH,            //height of the surface
46         D3DFMT_X8R8G8B8,//surface format
47         D3DPOOL_DEFAULT,//memory pool to use
48         &surface,        //pointer to the surface
49         NULL);            //reserved (always NULL)
50     if (FAILED(result)) 51  { 52         return false; 53  } 54 
55     //load surface from file into newly created surface
56     result = D3DXLoadSurfaceFromFile( 57         surface,            //destination surface
58         NULL,                //destination palette
59         NULL,                //destination rectangle
60         "c.bmp",            //source filename
61         NULL,                //source rectangle
62         D3DX_DEFAULT,        //controls how image is filled
63         0,                    //for transparency (0 for none)
64         NULL);                //source image info (usually NULL)
65     if (!SUCCEEDED(result)) 66  { 67         return false; 68  } 69 
70     return true; 71 }

它只有一个窗口句柄参数window,先对前面定义的d3d赋值,用Direct3DCreate9创建Direct3D对象;有了d3d对象,接着可以用CreateDevice创建设备,不过先要定义函数所需的参数d3dpp,最终d3d->CreateDevice创建Direct3D设备,我们需要的d3ddev终于有值了,后面就不需要d3d了。d3ddev->Clear用于清理屏幕,这里为黑色。接着CreateOffscreenPlainSurface,创建离屏表面,这种类型的表面实际上只是在内存中看起来像个位图的数组。游戏中所有图形都存储在表面或纹理中,这些图像都通过一个名为位块传输(bit-block transfer)的过程复制到屏幕上,GDI中的BitBlt函数就是这个功能。最后,我们用D3DXLoadSurfaceFromFile来加载一个位图文件到surface中,注意c.bmp,所以你需要一张bmp图片。经过这个初始化函数,我们前面声明的d3d、d3ddev、surface都被赋值初始化了。

 

第二个函数是游戏更新函数:

 1 /*
 2 game update function  3 */
 4 void Game_Run(HWND hwnd)  5 {  6     //make sure the Direct3D device is valid
 7     if (!d3ddev)  8         return;  9 
10     //create pointer to the back buffer
11     d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer); 12 
13     //start rendering
14     if (d3ddev->BeginScene()) 15  { 16         //draw surface to the backbuffer
17         d3ddev->StretchRect(surface, NULL, backbuffer, NULL, D3DTEXF_NONE); 18 
19         //stop rendering
20         d3ddev->EndScene(); 21 
22         //display the back buffer on the screen
23         d3ddev->Present(NULL, NULL, NULL, NULL); 24  } 25 
26     //check for escape key to exit
27     if (KEY_DOWN(VK_ESCAPE)) 28  { 29         PostMessage(hwnd, WM_DESTROY, 0, 0); 30  } 31 }

这里先看d3ddev->GetBackBuffer函数,就是用于初始化之前声明的backbuffer变量的,既获得可以操作后台缓冲区的指针。下面注意BeginSceneEndScene函数,你的绘图部分都要写在它们之间,我在这里使用了d3ddev->StretchRect这个函数来把我们已经初始化好的surface表面填充到backbuffer中,这个函数和GDI中的BitBlt功能相似,既然后台缓冲区已经填充好了,使用d3ddev->Present就可以输出到屏幕了。最后一段来检测Escape键是否被按下,如果按下则向程序发送退出的消息,接收消息的函数将在后面介绍。

 

第三个函数在游戏退出时做释放资源的工作,调用各自系统函数,没什么好说的:

 1 /*
 2 game shutdown function  3 */
 4 void Game_End(HWND hwnd)  5 {  6     //free memory
 7     if (surface)  8  {  9         surface->Release(); 10  } 11 
12     if (d3ddev) 13  { 14         d3ddev->Release(); 15  } 16 
17     if (d3d) 18  { 19         d3d->Release(); 20  } 21 }

 

第四个函数用于Windows程序的消息处理:

 1 /*
 2 windows event handling function  3 */
 4 LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  5 {  6     switch (message)  7  {  8     case WM_DESTROY:  9         gameover = true; 10         PostQuitMessage(0); 11         return 0; 12  } 13 
14     return DefWindowProc(hwnd, message, wParam, lParam); 15 }

这个CALLBACK回调函数很简单,但是很重要,在我们创建窗口时需要指定处理消息的函数就是它WinProc,这里不讲回调函数的概念。当你按下Escape键时,会触发Game_Run函数中的那个宏定义,那个宏会产生一个WM_DESTROY的消息给这个WinProc函数,于是导致了gameover,游戏退出;对于默认的其他类型消息,采用系统函数DefWindowProc来处理。

 

下面是最后一个函数,只能是main函数了,在Win窗口程序中,入口点是WinMain:

 1 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)  2 {  3     //set the windows properties
 4  WNDCLASSEX wc;  5     wc.cbSize = sizeof(WNDCLASSEX);  6     wc.style = CS_HREDRAW | CS_VREDRAW;  7     wc.lpfnWndProc = (WNDPROC)WinProc;  8     wc.cbClsExtra = 0;  9     wc.cbWndExtra = 0; 10     wc.hInstance = hInstance; 11     wc.hIcon = NULL; 12     wc.hCursor = NULL; 13     wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); 14     wc.lpszMenuName = NULL; 15     wc.lpszClassName = APPTitle.c_str(); 16     wc.hIconSm = NULL; 17     RegisterClassEx(&wc); 18 
19     //determine the resolution of the clients desktop screen
20     int screenWidth = GetSystemMetrics(SM_CXSCREEN); 21     int screenHeight = GetSystemMetrics(SM_CYSCREEN); 22 
23     //place the window in the middle of screen
24     int posX = (GetSystemMetrics(SM_CXSCREEN) - SCREENW) / 2; 25     int posY = (GetSystemMetrics(SM_CYSCREEN) - SCREENH) / 2; 26 
27     //Create a window
28  HWND window; 29     window = CreateWindow( 30         APPTitle.c_str(), //window class
31         APPTitle.c_str(), //title bar
32         WS_OVERLAPPEDWINDOW, //window style
33         posX, //position of window
34  posY, 35         SCREENW, //dimensions of window
36  SCREENH, 37         NULL, //parent window
38         NULL, //menu
39         hInstance, //application instance
40         NULL); //window parameters
41     if (window == 0) 42         return false; 43 
44     //display the window
45  ShowWindow(window, nCmdShow); 46  UpdateWindow(window); 47 
48     //initialize the game
49     if (!Game_Init(window)) 50         return 0; 51 
52     //main message loop
53  MSG msg; 54     while (!gameover) 55  { 56         //process windows events
57         if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 58  { 59             //handle any event messages
60             TranslateMessage(&msg); 61             DispatchMessage(&msg); 62  } 63 
64         //process game loop
65  Game_Run(window); 66  } 67 
68     //free game resources
69  Game_End(window); 70 
71     return msg.wParam; 72 }

这个函数稍微长了一点,但仍然很简单,除去次要代码,你可以看见整个的游戏程序的结构。首先声明了一个WNDCLASSEX的结构体wc,下面为wc赋值,指定要创建一个什么样的Windows窗口。这里的lpfnWndProc指明用于消息处理的函数;lpszClassName参数必须正确,否则无法创建窗口,如果VS2010提示错误,需要在项目属性中把字符集改为多字节字符集。接着的RegisterClassEx用刚才的参数wc来注册系统窗口。下面的GetSystemMetrics函数可以获取桌面屏幕的像素大小,posX与posY表明了窗口的位置,这里居中显示窗口。做这么多工作都是为了调用CreateWindow来创建一个Windows窗口,CreateWindowEx也可以用于创建窗口。创建窗口部分是本程序的次要部分,请注意接下来的程序部分,这里展示了一个典型的游戏程序结构,既先调用Game_Init初始化Direct3D,然后进入一个While循环,直到你按下Escape键或关闭窗口才退出程序。在循环中,先处理消息,然后调用Game_Run来更新图形显示部分。整个程序就这样介绍完了,很简单吧?最后得到了一个显示图片的窗口,初始化了Direct3D,不懂的函数可以自己搜索。

 简单的DirectX9程序_第1张图片

你可能感兴趣的:(DI)