近日裏學習了關於win32編程的相關知識,利用這些知識製做了一款貪吃蛇小遊戲,具體細節仍是分模塊來敘述c++
前期準備:在網上找到一些貪吃蛇的遊戲素材圖片,以及具體的邏輯框圖編程
在正式寫功能以前,先把一系列環境配置好,配置環境整體來講分爲如下幾步:app
1 Hbitmap_BackGroup = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP1)); 2 Hbitmap_Apple = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP2)); 3 Hbitmap_SnackHead = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP3)); 4 Hbitmap_SnackHead_Up = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP5)); 5 Hbitmap_SnackHead_Down = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP7)); 6 Hbitmap_SnackHead_Left = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP6)); 7 Hbitmap_SnackHead_Right = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP4));
如今就正式進入編寫小遊戲的階段函數
先來分析一下貪吃蛇遊戲的功能學習
邏輯上仍是很清晰很簡單的,接下來就是按功能實現測試
第一個,顯示背景,首先一打開遊戲就會有背景,那麼只有重繪能實現,想要畫圖的話,必須獲取窗口環境句柄(HDC),用完再去釋放, 可是在win32中如何將個人背景位圖,貼到HDC當中呢,查詢後瞭解,在win32中貼圖是以像素點爲單位,一個像素點一個像素點的去傳輸到HDC當中,那麼就得建立一個兼容性的HDC,再爲兼容性DC選擇個人背景位圖,再分像素點進行傳輸ui
1 HDC hdc_compatible; 2 hdc_compatible = CreateCompatibleDC(hdc); //建立兼容性DC 3 SelectObject(hdc_compatible,Hbitmap_BackGroup); //爲兼容性DC選擇背景位圖 4 BitBlt(hdc,0,0,600,600,hdc_compatible,0,0,SRCCOPY); //按像素點進行傳輸 5 DeleteDC(hdc_compatible); //刪除兼容性DC 6 return;
第二個,顯示蛇,首先以蛇的圖片大小構建一個虛擬的座標網(也能夠不建,但像素點過小定位太麻煩),以我這個爲例背景圖是600*600的大小,蛇頭,蛇身,蘋果大小都是30*30大小,那麼就是長寬都爲0~19的座標網,蛇的長個很容易想到能夠用鏈表的添加進行實現,那麼就把蛇身作成一個雙向鏈表(爲何雙向,在移動的時候就明白了),在添加鏈表後,再進行先蛇頭後蛇身的貼圖,上下左右用枚舉類型spa
1 enum FX {UP,DOWN,LEFT,RIGHT};2 enum FX fx = RIGHT; 設計
1 void AddNode(int x,int y) 2 { 3 Snack *pTemp = (Snack *)malloc(sizeof(Snack)); 4 pTemp->x = x; 5 pTemp->y = y; 6 pTemp->pLast = NULL; 7 pTemp->pNext = NULL; 8 9 if(pHead == NULL) 10 { 11 pHead = pTemp; 12 } 13 else 14 { 15 pEnd->pNext = pTemp; 16 pTemp->pLast = pEnd; 17 } 18 pEnd = pTemp; 19 } 20 void ShowSnack(HDC hdc) 21 { 22 Snack *pMark = pHead->pNext; 23 HDC hdc_compatible; 24 hdc_compatible = CreateCompatibleDC(hdc); 25 switch (fx) 26 { 27 case UP: 28 SelectObject(hdc_compatible,Hbitmap_SnackHead_Up); 29 break; 30 case DOWN: 31 SelectObject(hdc_compatible,Hbitmap_SnackHead_Down); 32 break; 33 case LEFT: 34 SelectObject(hdc_compatible,Hbitmap_SnackHead_Left); 35 break; 36 case RIGHT: 37 SelectObject(hdc_compatible,Hbitmap_SnackHead_Right); 38 break; 39 default: 40 break; 41 } 42 BitBlt(hdc,pHead->x*30,pHead->y*30,30,30,hdc_compatible,0,0,SRCCOPY); 43 44 while(pMark) 45 { 46 SelectObject(hdc_compatible,Hbitmap_SnackHead); 47 BitBlt(hdc,pMark->x*30,pMark->y*30,30,30,hdc_compatible,0,0,SRCCOPY); 48 pMark = pMark->pNext; 49 } 50 DeleteDC(hdc_compatible); 51 }
第三個,顯示蘋果,直接貼圖便可code
1 void ShowApple(HDC hdc) 2 { 3 HDC hdc_compatible; 4 hdc_compatible = CreateCompatibleDC(hdc); 5 SelectObject(hdc_compatible,Hbitmap_Apple); 6 BitBlt(hdc,apple.x*30,apple.y*30,30,30,hdc_compatible,0,0,SRCCOPY); 7 DeleteDC(hdc_compatible); 8 }
第四個,移動,win32中有個定時器的功能,每隔一段時間就向窗口發送定時器消息,那麼蛇的座標只要挨個代替前一個貼圖就會實現移動的效果,可是問題就在因而從蛇頭向蛇尾去遍歷座標變化,仍是從蛇尾向蛇頭變化,在幾回嘗試後發現仍是蛇尾向蛇頭,由於若是蛇頭向蛇尾的話,蛇頭座標都改變了,而下一個蛇身就找不到蛇頭的地址,就會出現蛇頭蛇尾分家的狀況,這也就是爲何作成雙向鏈表的緣由
1 void Move() 2 { 3 Snack *pMark = pEnd; 4 while(pMark != pHead) 5 { 6 pMark->x = pMark->pLast->x; 7 pMark->y = pMark->pLast->y; 8 pMark = pMark->pLast; 9 } 10 switch (fx) 11 { 12 case UP: 13 pHead->y--; 14 break; 15 case DOWN: 16 pHead->y++; 17 break; 18 case LEFT: 19 pHead->x--; 20 break; 21 case RIGHT: 22 pHead->x++; 23 break; 24 } 25 }
第五個,吃蘋果,也就是當蛇頭座標和蘋果座標重合的時候表明吃到蘋果了,一個判斷就搞定了
1 BOOL IfEatApple() 2 { 3 if(pHead->x == apple.x && pHead->y == apple.y) 4 return TRUE; 5 return FALSE; 6 }
第六個,生成新蘋果,利用隨機數給蘋果生成一個新的座標,可是後期給室友測試的時候發現一個問題,這個蘋果座標的隨機數在蛇長長以後會隨到蛇身上,這個是須要改進的地方,因此就得每隨機一次就得判斷是否和蛇的全部座標重合,沒有的話,才把隨機的x,y拿出來去貼圖
void NewApple() { Snack *pMark = pHead; int x; int y; do { x = rand() % 18 + 1; y = rand() % 18 + 1; pMark = pHead; while(pMark) { if(pMark->x == x && pMark->y == y) break; pMark = pMark->pNext; } }while(pMark); apple.x = x; apple.y = y; }
第七個,蛇長個,添加鏈表便可,可是貼圖座標只要給到窗口之外,這樣不影響遊戲體驗
1 AddNode(-10,-10);
第八個,撞牆死亡,給蛇頭的座標規定一個界限便可
1 BOOL IfBumpWall() 2 { 3 if(pHead->x == 0 || pHead->x == 19 || pHead->y == 0 || pHead->y == 19) 4 return TRUE; 5 return FALSE; 6 }
第九個,自咬死亡,判斷蛇頭座標是否等於自身座標便可
1 BOOL IfEatSelf() 2 { 3 Snack *pMark = pHead->pNext; 4 while(pMark) 5 { 6 if(pMark->x == pHead->x && pMark->y == pHead->y) 7 return TRUE; 8 pMark = pMark->pNext; 9 } 10 return FALSE; 11 }
至此,功能函數就完成了,接下來就是在回調函數裏進行邏輯鏈接的過程了,在後期給多個朋友進行試玩測試的時候,也發現了很多的bug在此也一併寫出
1.鍵盤上下左右的鍵碼VK_加上對應的大寫英文
2.因爲貼圖是連續的,上一次貼圖沒法銷燬,那麼就得用一層背景圖,一層蛇圖,一層蘋果圖的方式,實現遊戲的實際效果
3.當蛇向右運行的時候,快速按下向上+向右的按鍵,會顯示遊戲結束,針對這個問題,是這樣分析的,在一個定時器週期內,出現了兩次按鍵反饋,也就會變成向左,那麼蛇就出現自咬的狀況,處理的辦法就是人爲的規定在一個定時器週期內只容許出現一次鍵盤消息,設置一個標記,沒進定時器的時候爲TURE,進過定時器爲FALSE,此時鍵盤內的第二次快速按下的消息就無效了
4.暴力玩法,當屏幕內蛇幾乎佔滿時,窗口會出現閃爍的問題,在進行查閱後,發現是由於重繪是有一個刷新週期的,個人全部貼圖沒在一個週期內貼完,屏幕就會出現閃爍的現象,要想處理這個bug,能夠用雙緩衝技術,也就是爲當前HDC建立一個兼容性HDC,再爲這個兼容性HDC,再建立一個兼容性HDC,兩個兼容性HDC之間進行屢次貼圖操做,那麼在一個重繪週期內,一次就把第一次的兼容性DC給貼上就能夠了,知足一個刷新週期內圖貼完的要求,具體實現的話,在之後深刻學習c++的過程當中繼續完善。
完整代碼以下(圖片素材在文件裏,有興趣的能夠自行下載):
1.貪吃蛇.rc
1 // Microsoft Visual C++ generated resource script. 2 // 3 #include "resource.h" 4 5 #define APSTUDIO_READONLY_SYMBOLS 6 ///////////////////////////////////////////////////////////////////////////// 7 // 8 // Generated from the TEXTINCLUDE 2 resource. 9 // 10 #include "afxres.h" 11 12 ///////////////////////////////////////////////////////////////////////////// 13 #undef APSTUDIO_READONLY_SYMBOLS 14 15 ///////////////////////////////////////////////////////////////////////////// 16 // 中文(簡體,中國) resources 17 18 #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) 19 LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED 20 21 #ifdef APSTUDIO_INVOKED 22 ///////////////////////////////////////////////////////////////////////////// 23 // 24 // TEXTINCLUDE 25 // 26 27 1 TEXTINCLUDE 28 BEGIN 29 "resource.h\0" 30 END 31 32 2 TEXTINCLUDE 33 BEGIN 34 "#include ""afxres.h""\r\n" 35 "\0" 36 END 37 38 3 TEXTINCLUDE 39 BEGIN 40 "\r\n" 41 "\0" 42 END 43 44 #endif // APSTUDIO_INVOKED 45 46 47 ///////////////////////////////////////////////////////////////////////////// 48 // 49 // Bitmap 50 // 51 52 IDB_BITMAP1 BITMAP "..\\she\\背景.bmp" //得用相對路徑 53 IDB_BITMAP2 BITMAP "..\\she\\蘋果.bmp" 54 IDB_BITMAP3 BITMAP "..\\she\\蛇身.bmp" 55 IDB_BITMAP4 BITMAP "..\\she\\蛇頭0.bmp" 56 IDB_BITMAP5 BITMAP "..\\she\\蛇頭1.bmp" 57 IDB_BITMAP6 BITMAP "..\\she\\蛇頭2.bmp" 58 IDB_BITMAP7 BITMAP "..\\she\\蛇頭3.bmp" 59 #endif // 中文(簡體,中國) resources 60 ///////////////////////////////////////////////////////////////////////////// 61 62 63 64 #ifndef APSTUDIO_INVOKED 65 ///////////////////////////////////////////////////////////////////////////// 66 // 67 // Generated from the TEXTINCLUDE 3 resource. 68 // 69 70 71 ///////////////////////////////////////////////////////////////////////////// 72 #endif // not APSTUDIO_INVOKED
2.resource.h
1 //{{NO_DEPENDENCIES}} 2 // Microsoft Visual C++ 生成的包含文件。 3 // 供 貪吃蛇.rc 使用 4 // 5 #define IDB_BITMAP1 101 6 #define IDB_BITMAP2 102 7 #define IDB_BITMAP3 103 8 #define IDB_BITMAP4 104 9 #define IDB_BITMAP5 105 10 #define IDB_BITMAP6 106 11 #define IDB_BITMAP7 107 12 13 // Next default values for new objects 14 // 15 #ifdef APSTUDIO_INVOKED 16 #ifndef APSTUDIO_READONLY_SYMBOLS 17 #define _APS_NEXT_RESOURCE_VALUE 108 18 #define _APS_NEXT_COMMAND_VALUE 40001 19 #define _APS_NEXT_CONTROL_VALUE 1001 20 #define _APS_NEXT_SYMED_VALUE 101 21 #endif 22 #endif
3.Snack.c
1 #include <Windows.h> 2 #include <time.h> 3 #include "resource.h" 4 5 typedef struct NODE 6 { 7 int x; 8 int y; 9 struct NODE *pLast; 10 struct NODE *pNext; 11 }Snack,Apple; 12 13 Snack *pHead; 14 Snack *pEnd; 15 Apple apple = {5,6,NULL,NULL}; 16 enum FX {UP,DOWN,LEFT,RIGHT}; 17 enum FX fx = RIGHT; 18 BOOL g_flag = TRUE; //設置標記,避免在一個定時器週期內有兩次動做 19 20 HBITMAP Hbitmap_BackGroup; 21 HBITMAP Hbitmap_Apple; 22 HBITMAP Hbitmap_SnackHead; 23 HBITMAP Hbitmap_SnackHead_Up; 24 HBITMAP Hbitmap_SnackHead_Down; 25 HBITMAP Hbitmap_SnackHead_Left; 26 HBITMAP Hbitmap_SnackHead_Right; 27 28 void ShowBackGround(HDC hdc); //顯示背景 29 void AddNode(int x,int y); //添加蛇身 30 void ShowSnack(HDC hdc); //顯示蛇 31 void Move(); //移動蛇 32 void ShowApple(HDC hdc); //顯示蘋果 33 BOOL IfEatApple(); //判斷是否吃到蘋果 34 void NewApple(); //隨機出現新蘋果 35 BOOL IfBumpWall(); //判斷是否撞牆 36 BOOL IfEatSelf(); //判斷是否自咬 37 38 LRESULT CALLBACK MyWNDPROC(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam); 39 40 int CALLBACK WinMain( 41 HINSTANCE hInstance, 42 HINSTANCE hPrevInstance, 43 LPSTR lpCmdLine, 44 int nCmdShow 45 ) 46 { 47 HWND hwnd; 48 MSG msg; 49 //1.窗口設計 50 WNDCLASSEX ex; 51 ex.style = (UINT)NULL; 52 ex.cbSize = sizeof(ex); 53 ex.cbClsExtra = 0; 54 ex.cbWndExtra = 0; 55 ex.hInstance = hInstance; 56 ex.hIcon = NULL; 57 ex.hCursor = NULL; 58 ex.hbrBackground = CreateSolidBrush(RGB(0,255,0)); 59 ex.hIconSm = NULL; 60 ex.lpfnWndProc = &MyWNDPROC; 61 ex.lpszMenuName = NULL; 62 ex.lpszClassName = "aa"; 63 64 Hbitmap_BackGroup = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP1)); 65 Hbitmap_Apple = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP2)); 66 Hbitmap_SnackHead = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP3)); 67 Hbitmap_SnackHead_Up = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP5)); 68 Hbitmap_SnackHead_Down = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP7)); 69 Hbitmap_SnackHead_Left = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP6)); 70 Hbitmap_SnackHead_Right = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP4)); 71 72 AddNode(5,5); 73 AddNode(4,5); 74 AddNode(3,5); 75 76 //2.註冊 77 RegisterClassEx(&ex); 78 //3.建立 79 hwnd = CreateWindow(ex.lpszClassName,"貪吃蛇",WS_OVERLAPPEDWINDOW,50,50,615,638,NULL,NULL,hInstance,NULL); 80 //4.顯示 81 ShowWindow(hwnd,SW_SHOW); 82 83 SetTimer(hwnd,1,120,NULL); 84 85 srand((unsigned int)time(0)); 86 87 //消息循環 88 while(GetMessage(&msg,NULL,0,0)) 89 { 90 TranslateMessage(&msg); 91 DispatchMessage(&msg); 92 } 93 94 return 0; 95 } 96 97 LRESULT CALLBACK MyWNDPROC(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) 98 { 99 HDC hdc; 100 PAINTSTRUCT paintstruct; 101 switch(message) 102 { 103 case WM_CLOSE: 104 PostQuitMessage(0); 105 break; 106 case WM_PAINT: 107 hdc = BeginPaint(hWnd,&paintstruct); 108 ShowBackGround(hdc); 109 ShowSnack(hdc); 110 ShowApple(hdc); 111 112 EndPaint(hWnd,&paintstruct); 113 break; 114 case WM_TIMER: 115 g_flag = TRUE; 116 Move(); 117 if(IfBumpWall() || IfEatSelf()) 118 { 119 KillTimer(hWnd,1); 120 MessageBox(hWnd,"GAME OVER","提示",MB_OK); 121 } 122 if(IfEatApple()) //判斷是否吃到蘋果 123 { 124 NewApple(); //隨機一個新蘋果 125 AddNode(-10,-10); //長個 126 } 127 hdc = GetDC(hWnd); 128 ShowBackGround(hdc); //層層覆蓋 129 ShowSnack(hdc); 130 ShowApple(hdc); 131 ReleaseDC(hWnd,hdc); 132 break; 133 case WM_KEYDOWN: 134 if(g_flag == TRUE) 135 { 136 switch(wParam) 137 { 138 case VK_UP: 139 if(fx != DOWN) 140 { 141 fx = UP; 142 } 143 break; 144 case VK_DOWN: 145 if(fx != UP) 146 { 147 fx = DOWN; 148 } 149 break; 150 case VK_LEFT: 151 if(fx != RIGHT) 152 { 153 fx = LEFT; 154 } 155 break; 156 case VK_RIGHT: 157 if(fx != LEFT) 158 { 159 fx = RIGHT; 160 } 161 break; 162 } 163 } 164 g_flag = FALSE; 165 hdc = GetDC(hWnd); 166 ShowBackGround(hdc); 167 ShowSnack(hdc); 168 ShowApple(hdc); 169 ReleaseDC(hWnd,hdc); 170 break; 171 } 172 173 return DefWindowProc(hWnd,message,wParam,lParam); 174 } 175 void ShowBackGround(HDC hdc) 176 { 177 HDC hdc_compatible; 178 hdc_compatible = CreateCompatibleDC(hdc); //建立兼容性DC 179 SelectObject(hdc_compatible,Hbitmap_BackGroup); //爲兼容性DC選擇背景位圖 180 BitBlt(hdc,0,0,600,600,hdc_compatible,0,0,SRCCOPY); //按像素點進行傳輸 181 DeleteDC(hdc_compatible); //刪除兼容性DC 182 return; 183 } 184 void AddNode(int x,int y) 185 { 186 Snack *pTemp = (Snack *)malloc(sizeof(Snack)); 187 pTemp->x = x; 188 pTemp->y = y; 189 pTemp->pLast = NULL; 190 pTemp->pNext = NULL; 191 192 if(pHead == NULL) 193 { 194 pHead = pTemp; 195 } 196 else 197 { 198 pEnd->pNext = pTemp; 199 pTemp->pLast = pEnd; 200 } 201 pEnd = pTemp; 202 } 203 void ShowSnack(HDC hdc) 204 { 205 Snack *pMark = pHead->pNext; 206 HDC hdc_compatible; 207 hdc_compatible = CreateCompatibleDC(hdc); 208 switch (fx) 209 { 210 case UP: 211 SelectObject(hdc_compatible,Hbitmap_SnackHead_Up); 212 break; 213 case DOWN: 214 SelectObject(hdc_compatible,Hbitmap_SnackHead_Down); 215 break; 216 case LEFT: 217 SelectObject(hdc_compatible,Hbitmap_SnackHead_Left); 218 break; 219 case RIGHT: 220 SelectObject(hdc_compatible,Hbitmap_SnackHead_Right); 221 break; 222 default: 223 break; 224 } 225 BitBlt(hdc,pHead->x*30,pHead->y*30,30,30,hdc_compatible,0,0,SRCCOPY); 226 227 while(pMark) 228 { 229 SelectObject(hdc_compatible,Hbitmap_SnackHead); 230 BitBlt(hdc,pMark->x*30,pMark->y*30,30,30,hdc_compatible,0,0,SRCCOPY); 231 pMark = pMark->pNext; 232 } 233 DeleteDC(hdc_compatible); 234 } 235 void Move() 236 { 237 Snack *pMark = pEnd; 238 while(pMark != pHead) 239 { 240 pMark->x = pMark->pLast->x; 241 pMark->y = pMark->pLast->y; 242 pMark = pMark->pLast; 243 } 244 switch (fx) 245 { 246 case UP: 247 pHead->y--; 248 break; 249 case DOWN: 250 pHead->y++; 251 break; 252 case LEFT: 253 pHead->x--; 254 break; 255 case RIGHT: 256 pHead->x++; 257 break; 258 } 259 } 260 void ShowApple(HDC hdc) 261 { 262 HDC hdc_compatible; 263 hdc_compatible = CreateCompatibleDC(hdc); 264 SelectObject(hdc_compatible,Hbitmap_Apple); 265 BitBlt(hdc,apple.x*30,apple.y*30,30,30,hdc_compatible,0,0,SRCCOPY); 266 DeleteDC(hdc_compatible); 267 } 268 BOOL IfEatApple() 269 { 270 if(pHead->x == apple.x && pHead->y == apple.y) 271 return TRUE; 272 return FALSE; 273 } 274 void NewApple() 275 { 276 Snack *pMark = pHead; 277 int x; 278 int y; 279 do 280 { 281 x = rand() % 18 + 1; 282 y = rand() % 18 + 1; 283 pMark = pHead; 284 while(pMark) 285 { 286 if(pMark->x == x && pMark->y == y) 287 break; 288 pMark = pMark->pNext; 289 } 290 }while(pMark); 291 apple.x = x; 292 apple.y = y; 293 } 294 BOOL IfBumpWall() 295 { 296 if(pHead->x == 0 || pHead->x == 19 || pHead->y == 0 || pHead->y == 19) 297 return TRUE; 298 return FALSE; 299 } 300 BOOL IfEatSelf() 301 { 302 Snack *pMark = pHead->pNext; 303 while(pMark) 304 { 305 if(pMark->x == pHead->x && pMark->y == pHead->y) 306 return TRUE; 307 pMark = pMark->pNext; 308 } 309 return FALSE; 310 }
2019-05-16 11:32:24 編程小菜鳥自我檢討,望大佬能多提意見和建議,謝謝!!!