本人虽然一直从事图形图像方面的研究,但对游戏开发却了解甚少,上周幸运的拿到了网易游戏开发的实习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
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
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
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的某些库,主要因为在重定位过程中,链接器会去查找由所有输入目标文件的符号表组成的全局符号表,找到相应的符号进行重定位,如果没有找到相应的符号,则链接器会报符号未定义错误。
编译链接没问题后,游戏运行结果图如下: