Windows游戏开发学习笔记之一

本人虽然一直从事图形图像方面的研究,但对游戏开发却了解甚少,上周幸运的拿到了网易游戏开发的实习offer,为了不让自己在实习的时候太水,决定在空余时间学习下游戏开发方面的知识。

买了本《Windows游戏编程大师技巧》,先依葫芦画瓢做个Demo,程序中游戏的主要逻辑还不太清楚,就当先了解下开发环境和大概的开发流程吧。

1、游戏Demo:FreakOut,打砖块游戏

2、开发环境:VS2010+XP+DirectX 9.0

下载和安装DirectX SDK开发包,在VS2010的属性管理器->VC++目录,配置DirectX SDK的include路径和lib路径,即告诉VS去什么地方寻找DirectX的头文件和库文件,如下图所示:

库目录

头文件目录

上两个图中lib路径和include路径放置的顺序很重要,其中lib路径放置在所有路径的第一位,include路径放置在下图这四个路径后的第一个路径,即如上图所示。

如果放置顺序有问题,编译时可能会出现许多莫名其妙的错误。《Windows游戏编程大师技巧》中提到原因是许多C++编译器自己带有旧版本的DirectX,编译器可能会在自己的INCLUDE\目录下找到旧版本的头文件,而这些头文件是错误的,所以要确认DirectX SDK目录放在搜索路径列表的第一位。

3、游戏程序组成:

freakout.cpp:游戏的主要逻辑

blackbox.cpp:游戏库

blackbox.h:游戏库的头文件

ddraw.lib:用于生成应用程序的DirectDraw导入库。需要添加进工程项目中的配置属性-->[链接器 LINKER]的[输入INPUT]中。

ddraw.dll:运行时的DirectDraw库,实际上含有通过ddraw.lib导入库调用DirectDraw界面函数的COM执行程序。不必为此担心,只要确认安装了DirectX运行时文件即可。

 

在VS中创建一个Win32的应用程序,添加下面主要源代码。

blackbox.h

View Code
 1 // BLACKBOX.H - Header file for demo game engine library
 2 
 3 // watch for multiple inclusions
 4 #ifndef BLACKBOX
 5 #define BLACKBOX
 6 
 7 // DEFINES ////////////////////////////////////////////////////
 8 
 9 // default screen size
10 #define SCREEN_WIDTH    640  // size of screen
11 #define SCREEN_HEIGHT   480
12 #define SCREEN_BPP      8    // bits per pixel
13 #define MAX_COLORS      256  // maximum colors
14 
15 // MACROS /////////////////////////////////////////////////////
16 
17 // these read the keyboard asynchronously
18 #define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
19 #define KEY_UP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
20 
21 // initializes a direct draw struct
22 #define DD_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); }
23 
24 // TYPES //////////////////////////////////////////////////////
25 
26 // basic unsigned types
27 typedef unsigned short USHORT;
28 typedef unsigned short WORD;
29 typedef unsigned char  UCHAR;
30 typedef unsigned char  BYTE;
31 
32 // EXTERNALS //////////////////////////////////////////////////
33 
34 extern LPDIRECTDRAW7         lpdd;                 // dd object
35 extern LPDIRECTDRAWSURFACE7  lpddsprimary;         // dd primary surface
36 extern LPDIRECTDRAWSURFACE7  lpddsback;            // dd back surface
37 extern LPDIRECTDRAWPALETTE   lpddpal;              // a pointer to the created dd palette
38 extern LPDIRECTDRAWCLIPPER   lpddclipper;          // dd clipper
39 extern PALETTEENTRY          palette[256];         // color palette
40 extern PALETTEENTRY          save_palette[256];    // used to save palettes
41 extern DDSURFACEDESC2        ddsd;                 // a direct draw surface description struct
42 extern DDBLTFX               ddbltfx;              // used to fill
43 extern DDSCAPS2              ddscaps;              // a direct draw surface capabilities struct
44 extern HRESULT               ddrval;               // result back from dd calls
45 extern DWORD                 start_clock_count;    // used for timing
46 
47 // these defined the general clipping rectangle
48 extern int min_clip_x,                             // clipping rectangle 
49            max_clip_x,                  
50            min_clip_y,     
51            max_clip_y;                  
52 
53 // these are overwritten globally by DD_Init()
54 extern int screen_width,                            // width of screen
55            screen_height,                           // height of screen
56            screen_bpp;                              // bits per pixel 
57 
58 // PROTOTYPES /////////////////////////////////////////////////
59 
60 // DirectDraw functions
61 int DD_Init(int width, int height, int bpp);
62 int DD_Shutdown(void);
63 LPDIRECTDRAWCLIPPER DD_Attach_Clipper(LPDIRECTDRAWSURFACE7 lpdds, int num_rects, LPRECT clip_list);
64 int DD_Flip(void);
65 int DD_Fill_Surface(LPDIRECTDRAWSURFACE7 lpdds,int color);
66 
67 // general utility functions
68 DWORD Start_Clock(void);
69 DWORD Get_Clock(void);
70 DWORD Wait_Clock(DWORD count);
71 
72 // graphics functions
73 int Draw_Rectangle(int x1, int y1, int x2, int y2, int color,LPDIRECTDRAWSURFACE7 lpdds=lpddsback);
74 
75 // gdi functions
76 int Draw_Text_GDI(char *text, int x,int y,COLORREF color, LPDIRECTDRAWSURFACE7 lpdds=lpddsback);
77 int Draw_Text_GDI(char *text, int x,int y,int color, LPDIRECTDRAWSURFACE7 lpdds=lpddsback);
78 
79 #endif

blackbox.cpp

View Code
  1 // BLACKBOX.CPP - Game Engine 
  2  
  3 // INCLUDES ///////////////////////////////////////////////////
  4 
  5 #define WIN32_LEAN_AND_MEAN  // make sure all macros are included
  6 
  7 
  8 #include <windows.h>         // include important windows stuff
  9 #include <windowsx.h> 
 10 #include <mmsystem.h>
 11 
 12 #include <iostream>        // include important C/C++ stuff
 13 #include <conio.h>
 14 #include <stdlib.h>
 15 #include <malloc.h>
 16 #include <memory.h>
 17 #include <string.h>
 18 #include <stdarg.h>
 19 #include <stdio.h>
 20 #include <math.h>
 21 #include <io.h>
 22 #include <fcntl.h>
 23 
 24 #include <ddraw.h>           // directX includes
 25 #include "blackbox.h"        // game library includes
 26 using namespace std;
 27                                    
 28 // DEFINES ////////////////////////////////////////////////////
 29 
 30 // TYPES //////////////////////////////////////////////////////
 31 
 32 // PROTOTYPES /////////////////////////////////////////////////
 33 
 34 // EXTERNALS //////////////////////////////////////////////////
 35 
 36 extern HWND main_window_handle; // save the window handle
 37 extern HINSTANCE main_instance; // save the instance
 38 
 39 // GLOBALS ////////////////////////////////////////////////////
 40 
 41 LPDIRECTDRAW7         lpdd         = NULL;   // dd object
 42 LPDIRECTDRAWSURFACE7  lpddsprimary = NULL;   // dd primary surface
 43 LPDIRECTDRAWSURFACE7  lpddsback    = NULL;   // dd back surface
 44 LPDIRECTDRAWPALETTE   lpddpal      = NULL;   // a pointer to the created dd palette
 45 LPDIRECTDRAWCLIPPER   lpddclipper  = NULL;   // dd clipper
 46 PALETTEENTRY          palette[256];          // color palette
 47 PALETTEENTRY          save_palette[256];     // used to save palettes
 48 DDSURFACEDESC2        ddsd;                  // a direct draw surface description struct
 49 DDBLTFX               ddbltfx;               // used to fill
 50 DDSCAPS2              ddscaps;               // a direct draw surface capabilities struct
 51 HRESULT               ddrval;                // result back from dd calls
 52 DWORD                 start_clock_count = 0; // used for timing
 53 
 54 // these defined the general clipping rectangle
 55 int min_clip_x = 0,                          // clipping rectangle 
 56     max_clip_x = SCREEN_WIDTH-1,
 57     min_clip_y = 0,
 58     max_clip_y = SCREEN_HEIGHT-1;
 59 
 60 // these are overwritten globally by DD_Init()
 61 int screen_width  = SCREEN_WIDTH,            // width of screen
 62     screen_height = SCREEN_HEIGHT,           // height of screen
 63     screen_bpp    = SCREEN_BPP;              // bits per pixel
 64 
 65 // FUNCTIONS //////////////////////////////////////////////////
 66 
 67 int DD_Init(int width, int height, int bpp)
 68 {
 69 // this function initializes directdraw
 70 int index; // looping variable
 71 
 72 // create object and test for error
 73 if (DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL)!=DD_OK)
 74    return(0);
 75 
 76 // set cooperation level to windowed mode normal
 77 if (lpdd->SetCooperativeLevel(main_window_handle,
 78            DDSCL_ALLOWMODEX | DDSCL_FULLSCREEN | 
 79            DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT)!=DD_OK)
 80     return(0);
 81 
 82 // set the display mode
 83 if (lpdd->SetDisplayMode(width,height,bpp,0,0)!=DD_OK)
 84    return(0);
 85 
 86 // set globals
 87 screen_height = height;
 88 screen_width  = width;
 89 screen_bpp    = bpp;
 90 
 91 // Create the primary surface
 92 memset(&ddsd,0,sizeof(ddsd));
 93 ddsd.dwSize = sizeof(ddsd);
 94 ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
 95 
 96 // we need to let dd know that we want a complex 
 97 // flippable surface structure, set flags for that
 98 ddsd.ddsCaps.dwCaps = 
 99   DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
100 
101 // set the backbuffer count to 1
102 ddsd.dwBackBufferCount = 1;
103 
104 // create the primary surface
105 lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL);
106 
107 // query for the backbuffer i.e the secondary surface
108 ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
109 lpddsprimary->GetAttachedSurface(&ddscaps,&lpddsback);
110 
111 // create and attach palette
112 
113 // create palette data
114 // clear all entries defensive programming
115 memset(palette,0,256*sizeof(PALETTEENTRY));
116 
117 // create a R,G,B,GR gradient palette
118 for (index=0; index < 256; index++)
119     {
120     // set each entry
121     if (index < 64) 
122         palette[index].peRed = index*4; 
123     else           // shades of green
124     if (index >= 64 && index < 128) 
125         palette[index].peGreen = (index-64)*4;
126     else           // shades of blue
127     if (index >= 128 && index < 192) 
128        palette[index].peBlue = (index-128)*4;
129     else           // shades of grey
130     if (index >= 192 && index < 256) 
131         palette[index].peRed = palette[index].peGreen = 
132         palette[index].peBlue = (index-192)*4;
133     
134     // set flags
135     palette[index].peFlags = PC_NOCOLLAPSE;
136     } // end for index
137 
138 // now create the palette object
139 if (lpdd->CreatePalette(DDPCAPS_8BIT | DDPCAPS_INITIALIZE | DDPCAPS_ALLOW256,
140                          palette,&lpddpal,NULL)!=DD_OK)
141    return(0);
142 
143 // attach the palette to the primary
144 if (lpddsprimary->SetPalette(lpddpal)!=DD_OK)
145    return(0);
146 
147 // clear out both primary and secondary surfaces
148 DD_Fill_Surface(lpddsprimary,0);
149 DD_Fill_Surface(lpddsback,0);
150 
151 // attach a clipper to the screen
152 RECT screen_rect = {0,0,screen_width,screen_height};
153 lpddclipper = DD_Attach_Clipper(lpddsback,1,&screen_rect);
154 
155 // return success
156 return(1);
157 } // end DD_Init
158 
159 ///////////////////////////////////////////////////////////////
160 
161 int DD_Shutdown(void)
162 {
163 // this function release all the resources directdraw
164 // allocated, mainly to com objects
165 
166 // release the clipper first
167 if (lpddclipper)
168     lpddclipper->Release();
169 
170 // release the palette
171 if (lpddpal)
172    lpddpal->Release();
173 
174 // release the secondary surface
175 if (lpddsback)
176     lpddsback->Release();
177 
178 // release the primary surface
179 if (lpddsprimary)
180    lpddsprimary->Release();
181 
182 // finally, the main dd object
183 if (lpdd)
184     lpdd->Release();
185 
186 // return success
187 return(1);
188 } // end DD_Shutdown
189 
190 ///////////////////////////////////////////////////////////////   
191 
192 LPDIRECTDRAWCLIPPER DD_Attach_Clipper(LPDIRECTDRAWSURFACE7 lpdds,
193                                       int num_rects,
194                                       LPRECT clip_list)
195 
196 {
197 // this function creates a clipper from the sent clip list and attaches
198 // it to the sent surface
199 
200 int index;                         // looping var
201 LPDIRECTDRAWCLIPPER lpddclipper;   // pointer to the newly created dd clipper
202 LPRGNDATA region_data;             // pointer to the region data that contains
203                                    // the header and clip list
204 
205 // first create the direct draw clipper
206 if ((lpdd->CreateClipper(0,&lpddclipper,NULL))!=DD_OK)
207    return(NULL);
208 
209 // now create the clip list from the sent data
210 
211 // first allocate memory for region data
212 region_data = (LPRGNDATA)malloc(sizeof(RGNDATAHEADER)+num_rects*sizeof(RECT));
213 
214 // now copy the rects into region data
215 memcpy(region_data->Buffer, clip_list, sizeof(RECT)*num_rects);
216 
217 // set up fields of header
218 region_data->rdh.dwSize          = sizeof(RGNDATAHEADER);
219 region_data->rdh.iType           = RDH_RECTANGLES;
220 region_data->rdh.nCount          = num_rects;
221 region_data->rdh.nRgnSize        = num_rects*sizeof(RECT);
222 
223 region_data->rdh.rcBound.left    =  64000;
224 region_data->rdh.rcBound.top     =  64000;
225 region_data->rdh.rcBound.right   = -64000;
226 region_data->rdh.rcBound.bottom  = -64000;
227 
228 // find bounds of all clipping regions
229 for (index=0; index<num_rects; index++)
230     {
231     // test if the next rectangle unioned with the current bound is larger
232     if (clip_list[index].left < region_data->rdh.rcBound.left)
233        region_data->rdh.rcBound.left = clip_list[index].left;
234 
235     if (clip_list[index].right > region_data->rdh.rcBound.right)
236        region_data->rdh.rcBound.right = clip_list[index].right;
237 
238     if (clip_list[index].top < region_data->rdh.rcBound.top)
239        region_data->rdh.rcBound.top = clip_list[index].top;
240 
241     if (clip_list[index].bottom > region_data->rdh.rcBound.bottom)
242        region_data->rdh.rcBound.bottom = clip_list[index].bottom;
243 
244     } // end for index
245 
246 // now we have computed the bounding rectangle region and set up the data
247 // now let's set the clipping list
248 
249 if ((lpddclipper->SetClipList(region_data, 0))!=DD_OK)
250    {
251    // release memory and return error
252    free(region_data);
253    return(NULL);
254    } // end if
255 
256 // now attach the clipper to the surface
257 if ((lpdds->SetClipper(lpddclipper))!=DD_OK)
258    {
259    // release memory and return error
260    free(region_data);
261    return(NULL);
262    } // end if
263 
264 // all is well, so release memory and send back the pointer to the new clipper
265 free(region_data);
266 return(lpddclipper);
267 
268 } // end DD_Attach_Clipper
269 
270 ///////////////////////////////////////////////////////////////
271    
272 int DD_Flip(void)
273 {
274 // this function flip the primary surface with the secondary surface
275 
276 // flip pages
277 while(lpddsprimary->Flip(NULL, DDFLIP_WAIT)!=DD_OK);
278 
279 // flip the surface
280 
281 // return success
282 return(1);
283 
284 } // end DD_Flip
285 
286 ///////////////////////////////////////////////////////////////
287 
288 DWORD Start_Clock(void)
289 {
290 // this function starts the clock, that is, saves the current
291 // count, use in conjunction with Wait_Clock()
292 
293 return(start_clock_count = Get_Clock());
294 
295 } // end Start_Clock
296 
297 ///////////////////////////////////////////////////////////////
298 
299 DWORD Get_Clock(void)
300 {
301 // this function returns the current tick count
302 
303 // return time
304 return(GetTickCount());
305 
306 } // end Get_Clock
307 
308 ///////////////////////////////////////////////////////////////
309 
310 DWORD Wait_Clock(DWORD count)
311 {
312 // this function is used to wait for a specific number of clicks
313 // since the call to Start_Clock
314 
315 while((Get_Clock() - start_clock_count) < count);
316 return(Get_Clock());
317 
318 } // end Wait_Clock
319 
320 ///////////////////////////////////////////////////////////////
321 
322 int DD_Fill_Surface(LPDIRECTDRAWSURFACE7 lpdds,int color)
323 {
324 DDBLTFX ddbltfx; // this contains the DDBLTFX structure
325 
326 // clear out the structure and set the size field 
327 DD_INIT_STRUCT(ddbltfx);
328 
329 // set the dwfillcolor field to the desired color
330 ddbltfx.dwFillColor = color; 
331 
332 // ready to blt to surface
333 lpdds->Blt(NULL,       // ptr to dest rectangle
334            NULL,       // ptr to source surface, NA            
335            NULL,       // ptr to source rectangle, NA
336            DDBLT_COLORFILL | DDBLT_WAIT | DDBLT_ASYNC,   // fill and wait                   
337            &ddbltfx);  // ptr to DDBLTFX structure
338 
339 // return success
340 return(1);
341 } // end DD_Fill_Surface
342 
343 ///////////////////////////////////////////////////////////////   
344 
345 int Draw_Rectangle(int x1, int y1, int x2, int y2, int color,
346                    LPDIRECTDRAWSURFACE7 lpdds)
347 {
348 // this function uses directdraw to draw a filled rectangle
349 
350 DDBLTFX ddbltfx; // this contains the DDBLTFX structure
351 RECT fill_area;  // this contains the destination rectangle
352 
353 // clear out the structure and set the size field 
354 DD_INIT_STRUCT(ddbltfx);
355 
356 // set the dwfillcolor field to the desired color
357 ddbltfx.dwFillColor = color; 
358 
359 // fill in the destination rectangle data (your data)
360 fill_area.top    = y1;
361 fill_area.left   = x1;
362 fill_area.bottom = y2+1;
363 fill_area.right  = x2+1;
364 
365 // ready to blt to surface, in this case blt to primary
366 lpdds->Blt(&fill_area, // ptr to dest rectangle
367            NULL,       // ptr to source surface, NA            
368            NULL,       // ptr to source rectangle, NA
369            DDBLT_COLORFILL | DDBLT_WAIT | DDBLT_ASYNC,   // fill and wait                   
370            &ddbltfx);  // ptr to DDBLTFX structure
371 
372 // return success
373 return(1);
374 
375 } // end Draw_Rectangle
376 
377 ///////////////////////////////////////////////////////////////
378 
379 int Draw_Text_GDI(char *text, int x,int y,int color, LPDIRECTDRAWSURFACE7 lpdds)
380 {
381 // this function draws the sent text on the sent surface 
382 // using color index as the color in the palette
383 
384 HDC xdc; // the working dc
385 
386 // get the dc from surface
387 if (lpdds->GetDC(&xdc)!=DD_OK)
388    return(0);
389 
390 // set the colors for the text up
391 SetTextColor(xdc,RGB(palette[color].peRed,palette[color].peGreen,palette[color].peBlue) );
392 
393 // set background mode to transparent so black isn't copied
394 SetBkMode(xdc, TRANSPARENT);
395 
396 // draw the text a
397 TextOut(xdc,x,y,text,strlen(text));
398 
399 // release the dc
400 lpdds->ReleaseDC(xdc);
401 
402 // return success
403 return(1);
404 } // end Draw_Text_GDI
405 
406 ///////////////////////////////////////////////////////////////

freakout.cpp

View Code
  1 // FREAKOUT.CPP - break game demo
  2 
  3 // INCLUDES ///////////////////////////////////////////////////
  4 
  5 #define WIN32_LEAN_AND_MEAN // include all macros
  6 #define INITGUID            // include all GUIDs 
  7 
  8 #include <windows.h>        // include important windows stuff
  9 #include <windowsx.h> 
 10 #include <mmsystem.h>
 11 
 12 #include <iostream>       // include important C/C++ stuff
 13 #include <conio.h>
 14 #include <stdlib.h>
 15 #include <malloc.h>
 16 #include <memory.h>
 17 #include <string.h>
 18 #include <stdarg.h>
 19 #include <stdio.h>
 20 #include <math.h>
 21 #include <io.h>
 22 #include <fcntl.h>
 23 
 24 #include <ddraw.h>          // directX includes
 25 #include "blackbox.h"       // game library includes
 26 using namespace std;
 27 
 28 // DEFINES ////////////////////////////////////////////////////
 29 
 30 // defines for windows 
 31 #define WINDOW_CLASS_NAME "WIN3DCLASS"  // class name
 32 
 33 #define WINDOW_WIDTH            640     // size of window
 34 #define WINDOW_HEIGHT           480
 35 
 36 // states for game loop
 37 #define GAME_STATE_INIT         0
 38 #define GAME_STATE_START_LEVEL  1
 39 #define GAME_STATE_RUN          2
 40 #define GAME_STATE_SHUTDOWN     3
 41 #define GAME_STATE_EXIT         4 
 42 
 43 // block defines
 44 #define NUM_BLOCK_ROWS          6
 45 #define NUM_BLOCK_COLUMNS       8
 46 
 47 #define BLOCK_WIDTH             64
 48 #define BLOCK_HEIGHT            16
 49 #define BLOCK_ORIGIN_X          8
 50 #define BLOCK_ORIGIN_Y          8
 51 #define BLOCK_X_GAP             80
 52 #define BLOCK_Y_GAP             32
 53 
 54 // paddle defines
 55 #define PADDLE_START_X          (SCREEN_WIDTH/2 - 16)
 56 #define PADDLE_START_Y          (SCREEN_HEIGHT - 32);
 57 #define PADDLE_WIDTH            32
 58 #define PADDLE_HEIGHT           8
 59 #define PADDLE_COLOR            191
 60 
 61 // ball defines
 62 #define BALL_START_Y            (SCREEN_HEIGHT/2)
 63 #define BALL_SIZE                4
 64 
 65 // PROTOTYPES /////////////////////////////////////////////////
 66 
 67 // game console
 68 int Game_Init(void *parms=NULL);
 69 int Game_Shutdown(void *parms=NULL);
 70 int Game_Main(void *parms=NULL);
 71 
 72 // GLOBALS ////////////////////////////////////////////////////
 73 
 74 HWND main_window_handle  = NULL; // save the window handle
 75 HINSTANCE main_instance  = NULL; // save the instance
 76 int game_state           = GAME_STATE_INIT; // starting state
 77 
 78 int paddle_x = 0, paddle_y = 0; // tracks position of paddle
 79 int ball_x   = 0, ball_y   = 0; // tracks position of ball
 80 int ball_dx  = 0, ball_dy  = 0; // velocity of ball
 81 int score    = 0;               // the score
 82 int level    = 1;               // the current level
 83 int blocks_hit = 0;             // tracks number of blocks hit
 84 
 85 // this contains the game grid data   
 86 
 87 UCHAR blocks[NUM_BLOCK_ROWS][NUM_BLOCK_COLUMNS];     
 88 
 89 // FUNCTIONS //////////////////////////////////////////////////
 90 
 91 LRESULT CALLBACK WindowProc(HWND hwnd, 
 92                             UINT msg, 
 93                             WPARAM wparam, 
 94                             LPARAM lparam)
 95 {
 96 // this is the main message handler of the system
 97 PAINTSTRUCT    ps;           // used in WM_PAINT
 98 HDC            hdc;       // handle to a device context
 99 
100 // what is the message 
101 switch(msg)
102     {    
103     case WM_CREATE: 
104         {
105         // do initialization stuff here
106         return(0);
107         } break;
108 
109     case WM_PAINT:
110          {
111          // start painting
112          hdc = BeginPaint(hwnd,&ps);
113 
114          // the window is now validated 
115 
116          // end painting
117          EndPaint(hwnd,&ps);
118          return(0);
119         } break;
120 
121     case WM_DESTROY: 
122         {
123         // kill the application            
124         PostQuitMessage(0);
125         return(0);
126         } break;
127 
128     default:break;
129 
130     } // end switch
131 
132 // process any messages that we didn't take care of 
133 return (DefWindowProc(hwnd, msg, wparam, lparam));
134 
135 } // end WinProc
136 
137 // WINMAIN ////////////////////////////////////////////////////
138 
139 int WINAPI WinMain(    HINSTANCE hinstance,
140                     HINSTANCE hprevinstance,
141                     LPSTR lpcmdline,
142                     int ncmdshow)
143 {
144 // this is the winmain function
145 
146 WNDCLASS winclass;    // this will hold the class we create
147 HWND     hwnd;        // generic window handle
148 MSG         msg;        // generic message
149 HDC      hdc;       // generic dc
150 PAINTSTRUCT ps;     // generic paintstruct
151 
152 // first fill in the window class stucture
153 winclass.style            = CS_DBLCLKS | CS_OWNDC | 
154                           CS_HREDRAW | CS_VREDRAW;
155 winclass.lpfnWndProc    = WindowProc;
156 winclass.cbClsExtra        = 0;
157 winclass.cbWndExtra        = 0;
158 winclass.hInstance        = hinstance;
159 winclass.hIcon            = LoadIcon(NULL, IDI_APPLICATION);
160 winclass.hCursor        = LoadCursor(NULL, IDC_ARROW);
161 winclass.hbrBackground    = (HBRUSH)GetStockObject(BLACK_BRUSH);
162 winclass.lpszMenuName    = NULL; 
163 winclass.lpszClassName    = WINDOW_CLASS_NAME;
164 
165 // register the window class
166 if (!RegisterClass(&winclass))
167     return(0);
168 
169 // create the window, note the use of WS_POPUP
170 if (!(hwnd = CreateWindow(WINDOW_CLASS_NAME,    // class
171              "WIN3D Game Console",    // title
172              WS_POPUP | WS_VISIBLE,
173              0,0,                    // initial x,y
174                 GetSystemMetrics(SM_CXSCREEN),  // intial width
175              GetSystemMetrics(SM_CYSCREEN),  // initial height
176              NULL,        // handle to parent 
177              NULL,        // handle to menu
178              hinstance,// instance
179              NULL)))    // creation parms
180 return(0);
181 
182 // hide mouse
183 ShowCursor(FALSE);
184 
185 // save the window handle and instance in a global
186 main_window_handle = hwnd;
187 main_instance      = hinstance;
188 
189 // perform all game console specific initialization
190 Game_Init();
191 
192 // enter main event loop
193 while(1)
194     {
195     if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
196         { 
197         // test if this is a quit
198         if (msg.message == WM_QUIT)
199            break;
200     
201         // translate any accelerator keys
202         TranslateMessage(&msg);
203 
204         // send the message to the window proc
205         DispatchMessage(&msg);
206         } // end if
207     
208     // main game processing goes here
209     Game_Main();
210 
211     } // end while
212 
213 // shutdown game and release all resources
214 Game_Shutdown();
215 
216 // show mouse
217 ShowCursor(TRUE);
218 
219 // return to Windows like this
220 return(msg.wParam);
221 
222 } // end WinMain
223 
224 // T3DX GAME PROGRAMMING CONSOLE FUNCTIONS ////////////////////
225 
226 int Game_Init(void *parms)
227 {
228 // this function is where you do all the initialization 
229 // for your game
230 
231 
232 // return success
233 return(1);
234 
235 } // end Game_Init
236 
237 ///////////////////////////////////////////////////////////////
238 
239 int Game_Shutdown(void *parms)
240 {
241 // this function is where you shutdown your game and
242 // release all resources that you allocated
243 
244 
245 // return success
246 return(1);
247 
248 } // end Game_Shutdown
249 
250 ///////////////////////////////////////////////////////////////
251 
252 void Init_Blocks(void)
253 {
254 // initialize the block field
255 for (int row=0; row < NUM_BLOCK_ROWS; row++)
256     for (int col=0; col < NUM_BLOCK_COLUMNS; col++)
257          blocks[row][col] = row*16+col*3+16;
258 
259 } // end Init_Blocks
260 
261 ///////////////////////////////////////////////////////////////
262 
263 void Draw_Blocks(void)
264 {
265 // this function draws all the blocks in row major form
266 int x1 = BLOCK_ORIGIN_X, // used to track current position
267     y1 = BLOCK_ORIGIN_Y; 
268 
269 // draw all the blocks
270 for (int row=0; row < NUM_BLOCK_ROWS; row++)
271     {    
272     // reset column position
273     x1 = BLOCK_ORIGIN_X;
274 
275     // draw this row of blocks
276     for (int col=0; col < NUM_BLOCK_COLUMNS; col++)
277         {
278         // draw next block (if there is one)
279         if (blocks[row][col]!=0)
280             {
281             // draw block     
282             Draw_Rectangle(x1-4,y1+4,
283                  x1+BLOCK_WIDTH-4,y1+BLOCK_HEIGHT+4,0);
284 
285             Draw_Rectangle(x1,y1,x1+BLOCK_WIDTH,
286                  y1+BLOCK_HEIGHT,blocks[row][col]);
287             } // end if
288 
289         // advance column position
290         x1+=BLOCK_X_GAP;
291         } // end for col
292 
293     // advance to next row position
294     y1+=BLOCK_Y_GAP;
295 
296     } // end for row
297 
298 } // end Draw_Blocks
299 
300 ///////////////////////////////////////////////////////////////
301 
302 void Process_Ball(void)
303 {
304 // this function tests if the ball has hit a block or the paddle
305 // if so, the ball is bounced and the block is removed from 
306 // the playfield note: very cheesy collision algorithm :)
307 
308 // first test for ball block collisions
309 
310 // the algorithm basically tests the ball against each 
311 // block's bounding box this is inefficient, but easy to 
312 // implement, later we'll see a better way
313 
314 int x1 = BLOCK_ORIGIN_X, // current rendering position
315     y1 = BLOCK_ORIGIN_Y; 
316 
317 int ball_cx = ball_x+(BALL_SIZE/2),  // computer center of ball
318     ball_cy = ball_y+(BALL_SIZE/2);
319 
320 // test of the ball has hit the paddle
321 if (ball_y > (SCREEN_HEIGHT/2) && ball_dy > 0)
322    {
323    // extract leading edge of ball
324    int x = ball_x+(BALL_SIZE/2);
325    int y = ball_y+(BALL_SIZE/2);
326 
327    // test for collision with paddle
328    if ((x >= paddle_x && x <= paddle_x+PADDLE_WIDTH) &&
329        (y >= paddle_y && y <= paddle_y+PADDLE_HEIGHT))
330        {
331        // reflect ball
332        ball_dy=-ball_dy;
333 
334        // push ball out of paddle since it made contact
335        ball_y+=ball_dy;
336 
337        // add a little english to ball based on motion of paddle
338        if (KEY_DOWN(VK_RIGHT))
339           ball_dx-=(rand()%3);
340        else
341        if (KEY_DOWN(VK_LEFT))
342           ball_dx+=(rand()%3);
343        else
344           ball_dx+=(-1+rand()%3);
345        
346        // test if there are no blocks, if so send a message
347        // to game loop to start another level
348        if (blocks_hit >= (NUM_BLOCK_ROWS*NUM_BLOCK_COLUMNS))
349           {
350           game_state = GAME_STATE_START_LEVEL;
351           level++;
352           } // end if
353 
354        // make a little noise
355        MessageBeep(MB_OK);
356 
357        // return
358        return; 
359 
360        } // end if
361 
362    } // end if
363 
364 // now scan thru all the blocks and see of ball hit blocks
365 for (int row=0; row < NUM_BLOCK_ROWS; row++)
366     {    
367     // reset column position
368     x1 = BLOCK_ORIGIN_X;
369 
370     // scan this row of blocks
371     for (int col=0; col < NUM_BLOCK_COLUMNS; col++)
372         {
373         // if there is a block here then test it against ball
374         if (blocks[row][col]!=0)
375            {
376            // test ball against bounding box of block
377            if ((ball_cx > x1) && (ball_cx < x1+BLOCK_WIDTH) &&     
378                (ball_cy > y1) && (ball_cy < y1+BLOCK_HEIGHT))
379                {
380                // remove the block
381                blocks[row][col] = 0; 
382 
383                // increment global block counter, so we know 
384                // when to start another level up
385                blocks_hit++;
386 
387                // bounce the ball
388                ball_dy=-ball_dy;
389 
390                // add a little english
391                ball_dx+=(-1+rand()%3);
392 
393                // make a little noise
394                MessageBeep(MB_OK);
395 
396                // add some points
397                score+=5*(level+(abs(ball_dx)));
398 
399                // that's it -- no more block
400                return;
401 
402                } // end if  
403 
404            } // end if
405 
406         // advance column position
407         x1+=BLOCK_X_GAP;
408         } // end for col
409 
410     // advance to next row position
411     y1+=BLOCK_Y_GAP;
412 
413     } // end for row
414 
415 } // end Process_Ball
416 
417 ///////////////////////////////////////////////////////////////
418 
419 int Game_Main(void *parms)
420 {
421 // this is the workhorse of your game it will be called
422 // continuously in real-time this is like main() in C
423 // all the calls for you game go here!
424 
425 char buffer[80]; // used to print text
426 
427 // what state is the game in? 
428 if (game_state == GAME_STATE_INIT)
429     {
430     // initialize everything here graphics
431     DD_Init(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP);
432 
433     // seed the random number generator
434     // so game is different each play
435     srand(Start_Clock());
436 
437     // set the paddle position here to the middle bottom
438     paddle_x = PADDLE_START_X;
439     paddle_y = PADDLE_START_Y;
440 
441     // set ball position and velocity
442     ball_x = 8+rand()%(SCREEN_WIDTH-16);
443     ball_y = BALL_START_Y;
444     ball_dx = -4 + rand()%(8+1);
445     ball_dy = 6 + rand()%2;
446 
447     // transition to start level state
448     game_state = GAME_STATE_START_LEVEL;
449 
450     } // end if 
451 ////////////////////////////////////////////////////////////////
452 else
453 if (game_state == GAME_STATE_START_LEVEL)
454     {
455     // get a new level ready to run
456 
457     // initialize the blocks
458     Init_Blocks();
459 
460     // reset block counter
461     blocks_hit = 0;
462 
463     // transition to run state
464     game_state = GAME_STATE_RUN;
465 
466     } // end if
467 ///////////////////////////////////////////////////////////////
468 else
469 if (game_state == GAME_STATE_RUN)
470     {
471     // start the timing clock
472     Start_Clock();
473 
474     // clear drawing surface for the next frame of animation
475     Draw_Rectangle(0,0,SCREEN_WIDTH-1, SCREEN_HEIGHT-1,200);
476 
477     // move the paddle
478     if (KEY_DOWN(VK_RIGHT))
479        {
480        // move paddle to right
481        paddle_x+=8;
482  
483        // make sure paddle doesn't go off screen
484        if (paddle_x > (SCREEN_WIDTH-PADDLE_WIDTH))
485           paddle_x = SCREEN_WIDTH-PADDLE_WIDTH;
486 
487        } // end if
488     else
489     if (KEY_DOWN(VK_LEFT))
490        {
491        // move paddle to right
492        paddle_x-=8;
493  
494        // make sure paddle doesn't go off screen
495        if (paddle_x < 0)
496           paddle_x = 0;
497 
498        } // end if
499 
500     // draw blocks
501     Draw_Blocks();
502 
503     // move the ball
504     ball_x+=ball_dx;
505     ball_y+=ball_dy;
506 
507     // keep ball on screen, if the ball hits the edge of 
508     // screen then bounce it by reflecting its velocity
509     if (ball_x > (SCREEN_WIDTH - BALL_SIZE) || ball_x < 0) 
510        {
511        // reflect x-axis velocity
512        ball_dx=-ball_dx;
513 
514        // update position 
515        ball_x+=ball_dx;
516        } // end if
517 
518     // now y-axis
519     if (ball_y < 0) 
520        {
521        // reflect y-axis velocity
522        ball_dy=-ball_dy;
523 
524        // update position 
525        ball_y+=ball_dy;
526        } // end if
527    else 
528    // penalize player for missing the ball
529    if (ball_y > (SCREEN_HEIGHT - BALL_SIZE))
530        {
531        // reflect y-axis velocity
532        ball_dy=-ball_dy;
533 
534        // update position 
535        ball_y+=ball_dy;
536 
537        // minus the score
538        score-=100;
539 
540        } // end if
541 
542     // next watch out for ball velocity getting out of hand
543     if (ball_dx > 8) ball_dx = 8;
544     else
545     if (ball_dx < -8) ball_dx = -8;    
546 
547     // test if ball hit any blocks or the paddle
548     Process_Ball();
549 
550     // draw the paddle and shadow
551     Draw_Rectangle(paddle_x-8, paddle_y+8, 
552                    paddle_x+PADDLE_WIDTH-8, 
553                    paddle_y+PADDLE_HEIGHT+8,0);
554 
555     Draw_Rectangle(paddle_x, paddle_y, 
556                    paddle_x+PADDLE_WIDTH, 
557                    paddle_y+PADDLE_HEIGHT,PADDLE_COLOR);
558 
559     // draw the ball
560     Draw_Rectangle(ball_x-4, ball_y+4, ball_x+BALL_SIZE-4, 
561                    ball_y+BALL_SIZE+4, 0);
562     Draw_Rectangle(ball_x, ball_y, ball_x+BALL_SIZE, 
563                    ball_y+BALL_SIZE, 255);
564 
565     // draw the info
566     sprintf(buffer,"F R E A K O U T           Score %d             Level %d",score,level);
567     Draw_Text_GDI(buffer, 8,SCREEN_HEIGHT-16, 127);
568     
569     // flip the surfaces
570     DD_Flip();
571 
572     // sync to 33ish fps
573     Wait_Clock(30);
574 
575     // check of user is trying to exit
576     if (KEY_DOWN(VK_ESCAPE))
577        {
578        // send message to windows to exit
579        PostMessage(main_window_handle, WM_DESTROY,0,0);
580 
581        // set exit state
582        game_state = GAME_STATE_SHUTDOWN;
583 
584        } // end if
585 
586     } // end if
587 ///////////////////////////////////////////////////////////////
588 else
589 if (game_state == GAME_STATE_SHUTDOWN)
590    {
591    // in this state shut everything down and release resources
592    DD_Shutdown();
593 
594    // switch to exit state
595    game_state = GAME_STATE_EXIT;
596 
597    } // end if
598 
599 // return success
600 return(1);
601 
602 } // end Game_Main
603 
604 ///////////////////////////////////////////////////////////////

编译链接时,如果出现error LNK2019: 无法解析的外部符号 __imp__CreateWindowEx等,说明连接时缺少了windows的某些库,主要因为在重定位过程中,链接器会去查找由所有输入目标文件的符号表组成的全局符号表,找到相应的符号进行重定位,如果没有找到相应的符号,则链接器会报符号未定义错误。

编译链接没问题后,游戏运行结果图如下:

 

 

 

posted @ 2013-04-25 21:08  阳光守望者  阅读(726)  评论(0编辑  收藏  举报