每天一個(gè)C語(yǔ)言小項(xiàng)目,提升你的編程能力!
貪吃蛇游戲大家都玩過,它的玩法也很簡(jiǎn)單:用游戲按鍵上下左右控制蛇的方向,尋找吃的東西,每吃一口就能得到一定的積分,而且蛇的身子會(huì)越吃越長(zhǎng),身子越長(zhǎng)玩的難度就越大,不能碰墻,不能咬到自己的身體,更不能咬自己的尾巴,等到了一定的分?jǐn)?shù),就能過關(guān),然后繼續(xù)玩下一關(guān)。
不過我們今天要做的貪吃蛇就不是單人本了,你可以理解為C語(yǔ)言貪吃蛇的雙人模式——貪吃蛇游戲的雙人對(duì)戰(zhàn)版。
游戲雙方分別控制藍(lán)色和紅色兩條小蛇的前進(jìn),碰壁或咬到蛇身體算輸。
這個(gè)對(duì)戰(zhàn)版的貪吃蛇游戲網(wǎng)上有不少源代碼,這個(gè)代碼的特點(diǎn)就是為兩個(gè)游戲者分別增加了命令隊(duì)列,以實(shí)現(xiàn)更舒服的控制。
本項(xiàng)目編譯環(huán)境:Visual Studio 2019/2022,EasyX插件
代碼展示:
1.定義變量和游戲元素
#include#include #include #include #include using namespace std; #define WIDTH 64 // 游戲區(qū)域網(wǎng)格寬度 #define HEIGHT 48 // 游戲區(qū)域網(wǎng)格高度 #define ITEMSIZE 10 // 游戲元素大小 #define CMD_A_UP 0x1 // 控制命令:游戲者 A 向上 #define CMD_A_DOWN 0x2 // 控制命令:游戲者 A 向下 #define CMD_A_LEFT 0x4 // 控制命令:游戲者 A 向左 #define CMD_A_RIGHT 0x8 // 控制命令:游戲者 A 向右 #define CMD_B_UP 0x10 // 控制命令:游戲者 B 向上 #define CMD_B_DOWN 0x20 // 控制命令:游戲者 B 向下 #define CMD_B_LEFT 0x40 // 控制命令:游戲者 B 向左 #define CMD_B_RIGHT 0x80 // 控制命令:游戲者 B 向右 #define CMD_QUIT 0x100 // 控制命令:退出游戲 // 定義游戲元素 enum ITEM { EMPTY = 0, WALL, PLAYER_A, PLAYER_B, PLAYER_DEAD, PLAYER_A_NEXT, PLAYER_B_NEXT }; // 全局變量 ITEM g_world[WIDTH][HEIGHT]; // 保存游戲區(qū) POINT g_player_a; // 游戲者 A 的坐標(biāo) POINT g_player_b; // 游戲者 B 的坐標(biāo) POINT g_offset_a; // 游戲者 A 的移動(dòng)偏移方向 POINT g_offset_b; // 游戲者 B 的移動(dòng)偏移方向
2.繪制游戲元素
void DrawItem(int x, int y, ITEM item) { switch(item) { case EMPTY: setfillcolor(BLACK); break; case WALL: setfillcolor(LIGHTGRAY); break; case PLAYER_A: setfillcolor(BLUE); break; case PLAYER_B: setfillcolor(RED); break; case PLAYER_DEAD: setfillcolor(MAGENTA); break; } bar(x * ITEMSIZE + 1, y * ITEMSIZE + 1, (x + 1) * ITEMSIZE - 2, (y + 1) * ITEMSIZE - 2); g_world[x][y] = item; }
3.初始化游戲(將游戲地圖和蛇給繪制出來)
void init() { int x, y; // 繪制墻壁 for(x = 0; x < WIDTH; x++) { DrawItem(x, 0, WALL); DrawItem(x, HEIGHT - 1, WALL); } for(y = 1; y < HEIGHT - 1; y++) { DrawItem(0, y, WALL); DrawItem(WIDTH - 1, y, WALL); } // 繪制游戲區(qū)域 for (x = 1; x < WIDTH - 1; x++) for (y = 1; y < HEIGHT - 1; y++) DrawItem(x, y, EMPTY); // 隨機(jī)產(chǎn)生兩個(gè)游戲者的位置(至少間隔 5 格) do { g_player_a.x = rand() % (WIDTH - 6) + 3; g_player_a.y = rand() % (HEIGHT - 6) + 3; g_player_b.x = rand() % (WIDTH - 6) + 3; g_player_b.y = rand() % (HEIGHT - 6) + 3; }while ( (g_player_b.x - g_player_a.x) * (g_player_b.x - g_player_a.x) + (g_player_b.y - g_player_a.y) * (g_player_b.y - g_player_b.x) <= 25); // 畫出兩個(gè)游戲者的位置 DrawItem(g_player_a.x, g_player_a.y, PLAYER_A); DrawItem(g_player_b.x, g_player_b.y, PLAYER_B); // 隨機(jī)產(chǎn)生兩個(gè)游戲者的移動(dòng)方向 // 該方法的原理詳見:http://www.easyx.cn/skills/View.aspx?id=115 int n; n = (rand() % 4) * 2 + 1; g_offset_a.x = n / 3 - 1; g_offset_a.y = n % 3 - 1; n = (rand() % 4) * 2 + 1; g_offset_b.x = n / 3 - 1; g_offset_b.y = n % 3 - 1; // 繪制 Player A 空心方塊提示移動(dòng)方向 int tx = g_player_a.x + g_offset_a.x; int ty = g_player_a.y + g_offset_a.y; setcolor(BLUE); rectangle(tx * ITEMSIZE + 1, ty * ITEMSIZE + 1, (tx + 1) * ITEMSIZE - 2, (ty + 1) * ITEMSIZE - 2); // 繪制 Player B 空心方塊提示移動(dòng)方向 tx = g_player_b.x + g_offset_b.x; ty = g_player_b.y + g_offset_b.y; setcolor(RED); rectangle(tx * ITEMSIZE + 1, ty * ITEMSIZE + 1, (tx + 1) * ITEMSIZE - 2, (ty + 1) * ITEMSIZE - 2); // 按確定開始游戲 MessageBox(GetHWnd(), _T("對(duì)戰(zhàn)貪吃蛇 游戲說明: ") _T("游戲目標(biāo):兩條蛇,先碰到墻壁或碰到任何蛇的身體就算輸。 ") _T("Player A 使用 A S D W 控制藍(lán)色小蛇移動(dòng)方向。 ") _T("Player B 使用上下左右控制紅色小蛇移動(dòng)方向。 ") _T("點(diǎn)“確定”按鈕開始游戲。"), _T("游戲說明"), MB_OK | MB_ICONINFORMATION); }
4.獲取游戲雙方(用戶)的按鍵指令
int GetCmd() { // 定義兩個(gè)用戶的命令隊(duì)列 static queuePLAYER_A_CMD; static queue PLAYER_B_CMD; // 定義每次返回的命令 int cmd = 0; // 處理按鍵 while(_kbhit()) { switch(_getch()) { case 27: cmd = CMD_QUIT; break; case 'W': case 'w': if (PLAYER_A_CMD.size() < 16) PLAYER_A_CMD.push(CMD_A_UP); break; case 'S': case 's': if (PLAYER_A_CMD.size() < 16) PLAYER_A_CMD.push(CMD_A_DOWN); break; case 'A': case 'a': if (PLAYER_A_CMD.size() < 16) PLAYER_A_CMD.push(CMD_A_LEFT); break; case 'D': case 'd': if (PLAYER_A_CMD.size() < 16) PLAYER_A_CMD.push(CMD_A_RIGHT); break; case ?0 : case 0xE0: switch(_getch()) { case 72: if (PLAYER_B_CMD.size() < 16) PLAYER_B_CMD.push(CMD_B_UP); break; case 80: if (PLAYER_B_CMD.size() < 16) PLAYER_B_CMD.push(CMD_B_DOWN); break; case 75: if (PLAYER_B_CMD.size() < 16) PLAYER_B_CMD.push(CMD_B_LEFT); break; case 77: if (PLAYER_B_CMD.size() < 16) PLAYER_B_CMD.push(CMD_B_RIGHT); break; } } } // 讀取 Player A 的命令 int c = 0; while(!PLAYER_A_CMD.empty()) { c = PLAYER_A_CMD.front(); PLAYER_A_CMD.pop(); if ((c == CMD_A_UP || c == CMD_A_DOWN) ?&& g_offset_a.x != 0) break; if ((c == CMD_A_LEFT || c == CMD_A_RIGHT) && g_offset_a.y != 0) break; } if (c != 0) cmd |= c; // 讀取 Player B 的命令 c = 0; while(!PLAYER_B_CMD.empty()) { c = PLAYER_B_CMD.front(); PLAYER_B_CMD.pop(); if ((c == CMD_B_UP || c == CMD_B_DOWN) ?&& g_offset_b.x != 0) break; if ((c == CMD_B_LEFT || c == CMD_B_RIGHT) && g_offset_b.y != 0) break; } if (c != 0) cmd |= c; // 返回命令 return cmd; }
5.處理用戶指令
bool DealCmd(int cmd) { if ((cmd & CMD_A_UP) && g_offset_a.x != 0) { g_offset_a.x = 0; g_offset_a.y = -1; } if ((cmd & CMD_A_DOWN) && g_offset_a.x != 0) { g_offset_a.x = 0; g_offset_a.y = 1; } if ((cmd & CMD_A_LEFT) && g_offset_a.y != 0) { g_offset_a.x = -1; g_offset_a.y = 0; } if ((cmd & CMD_A_RIGHT) && g_offset_a.y != 0) { g_offset_a.x = 1; g_offset_a.y = 0; } if ((cmd & CMD_B_UP) && g_offset_b.x != 0) { g_offset_b.x = 0; g_offset_b.y = -1; } if ((cmd & CMD_B_DOWN) && g_offset_b.x != 0) { g_offset_b.x = 0; g_offset_b.y = 1; } if ((cmd & CMD_B_LEFT) && g_offset_b.y != 0) { g_offset_b.x = -1; g_offset_b.y = 0; } if ((cmd & CMD_B_RIGHT) && g_offset_b.y != 0) { g_offset_b.x = 1; g_offset_b.y = 0; } if (cmd & CMD_QUIT) if (MessageBox(GetHWnd(), _T("您要退出游戲嗎?"), _T("游戲暫停"), MB_OKCANCEL) == IDOK) return false; return true; }
6.判斷蛇的狀態(tài)&游戲結(jié)束后的彈窗選擇
bool DealGame() { // Player A、B 前進(jìn) g_player_a.x += g_offset_a.x; g_player_a.y += g_offset_a.y; g_player_b.x += g_offset_b.x; g_player_b.y += g_offset_b.y; // 判斷 Player A、B 的生死狀態(tài) bool dead_a = false, dead_b = false, dead_ab = false; if (g_player_a.x == g_player_b.x && g_player_a.y == g_player_b.y) { DrawItem(g_player_a.x, g_player_a.y, PLAYER_DEAD); dead_ab = true; } else if (g_world[g_player_a.x][g_player_a.y] != EMPTY) { DrawItem(g_player_a.x, g_player_a.y, PLAYER_DEAD); dead_a = true; } else if (g_world[g_player_b.x][g_player_b.y] != EMPTY) { DrawItem(g_player_b.x, g_player_b.y, PLAYER_DEAD); dead_b = true; } else { DrawItem(g_player_a.x, g_player_a.y, PLAYER_A); DrawItem(g_player_b.x, g_player_b.y, PLAYER_B); return true; } // 判斷是否要重新開始 bool restart = false; if (dead_ab || (dead_a && dead_b)) restart = MessageBox(GetHWnd(), _T("Player A 和 Player B 都死了。 要再來一局嗎?"), _T("GAME OVER"), MB_YESNO | MB_ICONINFORMATION) == IDYES; else if (dead_a) restart = MessageBox(GetHWnd(), _T("Player A 死了。 要再來一局嗎?"), _T("GAME OVER"), MB_YESNO | MB_ICONINFORMATION) == IDYES; else if(dead_b) restart = MessageBox(GetHWnd(), _T("Player B 死了。 要再來一局嗎?"), _T("GAME OVER"), MB_YESNO | MB_ICONINFORMATION) == IDYES; if (restart) { init(); return true; } else return false; }
7.補(bǔ)上入口函數(shù)
void main() { initgraph(640, 480); srand((unsigned)time(NULL)); // 初始化 init(); // 游戲主循環(huán) while(true) { int cmd = GetCmd(); // 獲取用戶命令 if (!DealCmd(cmd)) break; // 處理命令 if (!DealGame()) break; // 處理游戲 Sleep(200); // 延時(shí) } // 關(guān)閉繪圖窗口 closegraph(); }
大家趕緊去動(dòng)手試試吧!
審核編輯:湯梓紅
-
游戲
+關(guān)注
關(guān)注
2文章
742瀏覽量
26313 -
C語(yǔ)言
+關(guān)注
關(guān)注
180文章
7604瀏覽量
136713 -
編程
+關(guān)注
關(guān)注
88文章
3614瀏覽量
93698 -
Visual
+關(guān)注
關(guān)注
0文章
252瀏覽量
34251 -
代碼
+關(guān)注
關(guān)注
30文章
4780瀏覽量
68539
原文標(biāo)題:C語(yǔ)言項(xiàng)目:貪吃蛇游戲(雙人模式)!詳細(xì)思路+源碼分享
文章出處:【微信號(hào):cyuyanxuexi,微信公眾號(hào):C語(yǔ)言編程學(xué)習(xí)基地】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論