用D3DXSprite绘制精灵动画

图像可以由图片单元形成的行和列组成,在精灵的上下文中,我们称这种平铺的图像为精灵表。需要做的是,算出图片单元的左上角在位图中位置,然后从图像中按照精灵的宽度和高度复制出一个源矩形来。ID3DXSprite::Draw函数可以用来定义图像的源矩形,我们在需要时可以在大图像中指定出一个小的图片单元。

用D3DXSprite绘制精灵动画_第1张图片

比如上面这一简单的图片组合,我们用它将实现简单的爆炸效果。首先,需要算出图片单元的左位置,使用模运算符%来实现,给出图片单元的水平起始地址。爆炸动画的精灵表中有6个列和5个行,每一帧的尺寸为128X128像素,我们想绘制第10帧,记得行列都从0开始,第10帧在第1行第3列。

left = (9 % 6) * 128; // 3 * 128

top = (9 / 6) * 128; // 1 * 128

right = left + 128;

bottom = top + 128;

使用这种方法,我们可以编写一个能绘制精灵表中任何一帧的函数。下面看看程序代码,这个例子同样是在上一个例子用D3DXSprite简单绘制2D精灵的基础上修改的,首先是DirectX.h文件,添加了两个函数Sprite_Draw_Frame与Sprite_Animate用于实现动画效果

View Code
 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 D3DXVECTOR2 GetBitmapSize(string filename); 39 LPDIRECT3DTEXTURE9 LoadTexture(string filename, D3DCOLOR transcolor = D3DCOLOR_XRGB(0,0,0)); 40 
41 //DirectInput objects, devices, and states
42 extern LPDIRECTINPUT8 dinput; 43 extern LPDIRECTINPUTDEVICE8 dimouse; 44 extern LPDIRECTINPUTDEVICE8 dikeyboard; 45 extern DIMOUSESTATE mouse_state; 46 extern LPD3DXSPRITE spriteobj; 47 
48 //DirectInput functions
49 bool DirectInput_Init(HWND); 50 void DirectInput_Update(); 51 void DirectInput_Shutdown(); 52 int Key_Down(int); 53 int Mouse_Button(int); 54 int Mouse_X(); 55 int Mouse_Y(); 56 
57 //game functions
58 bool Game_Init(HWND window); 59 void Game_Run(HWND window); 60 void Game_End(); 61 
62 //draw functions
63 void Sprite_Draw_Frame(LPDIRECT3DTEXTURE9 texture, int destx, int desty, 64     int framenum, int framew, int frameh, int columns); 65 void Sprite_Animate(int &frame, int startframe, int endframe, 66     int direction, int &starttime, int delay);

 

下面是DirectX.cpp文件,在Direct3D_Init函数中对参数d3dpp做了些改变,还添加了两个新的函数。

View Code
 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 LPD3DXSPRITE spriteobj;  10 
 11 //DirectInput variables
 12 LPDIRECTINPUT8 dinput = NULL;  13 LPDIRECTINPUTDEVICE8 dimouse = NULL;  14 LPDIRECTINPUTDEVICE8 dikeyboard = NULL;  15 DIMOUSESTATE mouse_state;  16 char keys[256];  17 
 18 bool Direct3D_Init(HWND window, int width, int height, bool fullscreen)  19 {  20     //Initialize Direct3D
 21     d3d = Direct3DCreate9(D3D_SDK_VERSION);  22     if (!d3d)  23         return false;  24 
 25     //set Direct3D presentation parameters
 26  D3DPRESENT_PARAMETERS d3dpp;  27     ZeroMemory(&d3dpp, sizeof(d3dpp));  28     d3dpp.hDeviceWindow = window;  29     d3dpp.Windowed = (!fullscreen);  30     d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;  31     d3dpp.EnableAutoDepthStencil = 1;  32     d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;  33     d3dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;  34     d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;  35     d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;  36     d3dpp.BackBufferCount = 1;  37     d3dpp.BackBufferWidth = width;  38     d3dpp.BackBufferHeight = height;  39 
 40     //create Direct3D device
 41     d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window,  42         D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);  43     if (!d3ddev)  44         return false;  45 
 46     //get a pointer to the back buffer surface
 47     d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);  48 
 49     //create sprite object
 50     D3DXCreateSprite(d3ddev, &spriteobj);  51 
 52     return true;  53 }  54 
 55 void Direct3D_Shutdown()  56 {  57     if (spriteobj)  58         spriteobj->Release();  59     if (d3ddev)  60         d3ddev->Release();  61     if (d3d)  62         d3d->Release();  63 }  64 
 65 void DrawSurface(LPDIRECT3DSURFACE9 dest, float x, float y, LPDIRECT3DSURFACE9 source)  66 {  67     //get width height from source surface
 68  D3DSURFACE_DESC desc;  69     source->GetDesc(&desc);  70 
 71     //create rects for drawing
 72     RECT source_rect = {0, 0, (long)desc.Width, (long)desc.Height };  73     RECT dest_rect = { (long)x, (long)y, (long)x + desc.Width, (long)y + desc.Height};  74 
 75     //draw the source surface onto the dest
 76     d3ddev->StretchRect(source, &source_rect, dest, &dest_rect, D3DTEXF_NONE);  77 }  78 
 79 bool LoadSurface(string filename, D3DXIMAGE_INFO &info, LPDIRECT3DSURFACE9 &image)  80 {  81     //get width and height from bitmap file
 82     HRESULT result = D3DXGetImageInfoFromFile(filename.c_str(), &info);  83     if (result != D3D_OK)  84         return false;  85 
 86     //create surface
 87     result = d3ddev->CreateOffscreenPlainSurface(  88  info.Width, info.Height,  89  D3DFMT_X8R8G8B8,  90  D3DPOOL_DEFAULT,  91         &image,  92  NULL);  93     if (result != D3D_OK)  94         return false;  95 
 96     //load surface from file into newly created surface
 97     result = D3DXLoadSurfaceFromFile(  98         image,    //destination surface
 99         NULL,    //destination palette
100         NULL,    //destination rectangle
101  filename.c_str(), 102         NULL,    //source rectangle
103         D3DX_DEFAULT, //controls how image is filtered
104         D3DCOLOR_XRGB(0,0,0), 105         NULL);    //source image info
106     if (result != D3D_OK) 107         return false; 108 
109     return true; 110 } 111 
112 bool DirectInput_Init(HWND hwnd) 113 { 114     //initialize DirectInput object
115     HRESULT result = DirectInput8Create( 116  GetModuleHandle(NULL), 117  DIRECTINPUT_VERSION, 118  IID_IDirectInput8, 119         (void**)&dinput, 120  NULL); 121 
122     //initialize the keyboard
123     dinput->CreateDevice(GUID_SysKeyboard, &dikeyboard, NULL); 124     dikeyboard->SetDataFormat(&c_dfDIKeyboard); 125     dikeyboard->SetCooperativeLevel(hwnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND); 126     dikeyboard->Acquire(); 127 
128     //initialize the mouse
129     dinput->CreateDevice(GUID_SysMouse, &dimouse, NULL); 130     dimouse->SetDataFormat(&c_dfDIMouse); 131     dimouse->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); 132     dimouse->Acquire(); 133     
134     ShowCursor(true); 135 
136     return true; 137 } 138 
139 void DirectInput_Update() 140 { 141     //update mouse
142     dimouse->GetDeviceState(sizeof(mouse_state), (LPVOID)&mouse_state); 143 
144     //update keyboard
145     dikeyboard->GetDeviceState(sizeof(keys), (LPVOID)&keys); 146 } 147 
148 int Mouse_X() 149 { 150     return mouse_state.lX; 151 } 152 
153 int Mouse_Y() 154 { 155     return mouse_state.lY; 156 } 157 
158 //return mouse button state
159 int Mouse_Button(int button) 160 { 161     return mouse_state.rgbButtons[button] & 0x80; 162 } 163 
164 //return key press state
165 int Key_Down(int key) 166 { 167     return (keys[key] & 0x80); 168 } 169 
170 void DirectInput_Shutdown() 171 { 172     if (dikeyboard) 173  { 174         dikeyboard->Unacquire(); 175         dikeyboard->Release(); 176         dikeyboard = NULL; 177  } 178 
179     if (dimouse) 180  { 181         dimouse->Unacquire(); 182         dimouse->Release(); 183         dimouse = NULL; 184  } 185 } 186 
187 //load texture
188 LPDIRECT3DTEXTURE9 LoadTexture(string filename, D3DCOLOR transcolor) 189 { 190     LPDIRECT3DTEXTURE9 texture = NULL; 191     
192     //get width and height from bitmap file
193  D3DXIMAGE_INFO info; 194     HRESULT result = D3DXGetImageInfoFromFile(filename.c_str(), &info); 195     if (result != D3D_OK) 196         return NULL; 197 
198     //create the new texture by loading a bitmap image file
199     result = D3DXCreateTextureFromFileEx( 200         d3ddev, //Direct3D device object
201         filename.c_str(), //filename
202         info.Width, //image width
203         info.Height, //image height
204         1, //mip-map levels (1 for no chain)
205         D3DPOOL_DEFAULT, //the type of surface
206         D3DFMT_UNKNOWN, //surface format
207         D3DPOOL_DEFAULT, //memory class for the texture
208         D3DX_DEFAULT, //image filter
209         D3DX_DEFAULT, //mip filter
210         transcolor, //color key for transparency
211         &info, //bitmap file info
212         NULL, //color palette
213         &texture); //destination texture
214     if (result != D3D_OK) 215         return NULL; 216 
217     return texture; 218 } 219 
220 D3DXVECTOR2 GetBitmapSize(string filename) 221 { 222  D3DXIMAGE_INFO info; 223     D3DXVECTOR2 size = D3DXVECTOR2(0.0f, 0.0f); 224     HRESULT result = D3DXGetImageInfoFromFile(filename.c_str(), &info); 225     if (result == D3D_OK) 226         size = D3DXVECTOR2((float)info.Width, (float)info.Height); 227 
228     return size; 229 }

 

下面的Sprite_Draw_Frame是新增的第一个函数,用来绘制精灵表中的任何一帧。参数texture为整个图片;destx与desty构成的position表示把选定的区域rect绘制屏幕的什么位置上;framenum表示要绘制哪一帧,是个整数;framew与frameh表示每一帧的高宽;columns表示图片共有多少列。

 1 //draw sprite frame
 2 void Sprite_Draw_Frame(LPDIRECT3DTEXTURE9 texture, int destx, int desty,  3     int framenum, int framew, int frameh, int columns)  4 {  5     D3DXVECTOR3 position((float)destx, (float)desty, 0);  6     D3DCOLOR white = D3DCOLOR_XRGB(255, 255, 255);  7 
 8  RECT rect;  9     rect.left = (framenum % columns) * framew; 10     rect.top = (framenum / columns) * frameh; 11     rect.right = rect.left + framew; 12     rect.bottom = rect.top + frameh; 13 
14     spriteobj->Draw(texture, &rect, NULL, &position, white); 15 }

 

在上一个函数中有个关键参数framenum,表示要绘制哪一帧,如果根据时间变化来连续改变framenum就可以形成所需的精灵动画。下面这个函数就是关键,参数frame就代表要绘制哪一帧;startfame与endframe表示要绘制从第几帧到第几帧;direction表示每次跨越几帧,一般为1;starttime表示开始时间,这里要与延迟时间delay(毫秒)配合使用。下面的代码很简单,GetTickCount获取从操作系统启动到现在所经过的毫秒数。最后的两个if语句检查要绘制的帧是否存在。

 1 void Sprite_Animate(int &frame, int startframe, int endframe,  2     int direction, int &starttime, int delay)  3 {  4     if ((int)GetTickCount() > starttime + delay)  5  {  6         starttime = GetTickCount();  7 
 8         frame += direction;  9         if (frame > endframe) 10             frame = startframe; 11         if (frame < startframe) 12             frame = endframe; 13  } 14 }

 

下面是main.cpp文件,负责窗口消息处理,这里不做介绍

View Code
 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,添加了一个表示纹理的变量explosion。在函数Game_Init中,用LoadTexture加载图片到explosion。在Game_Run中,调用Sprite_Animate与Sprite_Draw_Frame函数来不断绘制纹理单元,形成爆炸效果。

 1 #include "DirectX.h"
 2 using namespace std;  3 
 4 const string APPTITLE = "AnimateSprite";  5 const int SCREENW = 1024;  6 const int SCREENH = 576;  7 
 8 LPDIRECT3DTEXTURE9 explosion = NULL;  9 int frame = 0; 10 int starttime = 0; 11 
12 //game initialization
13 bool Game_Init(HWND window) 14 { 15     if (!Direct3D_Init(window, SCREENW, SCREENH, false)) 16  { 17         MessageBox(0, "Error initialization Direct3D", "Error", 0); 18         return false; 19  } 20     if (!DirectInput_Init(window)) 21  { 22         MessageBox(0, "Error initialization DirectInput", "Error", 0); 23         return false; 24  } 25 
26     //load explosion sprite
27     explosion = LoadTexture("explosion_30_128.tga"); 28     if (!explosion) 29         return false; 30 
31     return true; 32 } 33 
34 //game update
35 void Game_Run(HWND window) 36 { 37     //make sure the Direct3D device is valid
38     if (!d3ddev) 39         return; 40 
41     //update input devices
42  DirectInput_Update(); 43 
44     //clear the scene
45     d3ddev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); 46 
47     //start rendering
48     if (d3ddev->BeginScene()) 49  { 50         //start drawing
51         spriteobj->Begin(D3DXSPRITE_ALPHABLEND); 52 
53         //animate and draw the sprite
54         Sprite_Animate(frame, 0, 29, 1, starttime, 30); 55         Sprite_Draw_Frame(explosion, 450, 200, frame, 128, 128, 6); 56 
57         //stop drawing
58         spriteobj->End(); 59 
60         //stop rendering
61         d3ddev->EndScene(); 62         d3ddev->Present(NULL, NULL, NULL, NULL); 63  } 64 
65     //escape key exits
66     if (Key_Down(DIK_ESCAPE)) 67         gameover = true; 68 } 69 
70 void Game_End() 71 { 72     //free memory
73     explosion->Release(); 74  DirectInput_Shutdown(); 75  Direct3D_Shutdown(); 76 }

 

最后贴出运行截图,源代码, 本文参考自游戏编程入门。

用D3DXSprite绘制精灵动画_第2张图片

 

你可能感兴趣的:(Sprite)