今天突發奇想想用C++實現一個貪吃蛇小游戲,無奈C++沒有自帶的GUI框架,蒟蒻博主也不會用C++做GUI,於是只能在黑乎乎的命令行中完成這個游戲了(qwq)。
貪吃蛇游戲還是比較簡單的,就用C++的基礎知識和一點個Windows的api就可以開發完成了,這里就稍微講一下思路吧。
總共是GameInit.cpp,Snake.cpp,Food.cpp,Display.cpp,GameStart.cpp 這5個文件。
GameInit是游戲的初始設置類,負責設置游戲窗口大小,設置光標,初始化隨機種子等等初始化工作。

1 #include"pch.h" 2 #include "GmaeInit.h" 3 #include<Windows.h> 4 #include<random> 5 #include<ctime> 6 #include <conio.h> 7 8 using namespace std; 9 10 GameInit::GameInit(int s) : speed(s) { 11 //設置游戲窗口大小 12 char buffer[32]; 13 sprintf_s(buffer, "mode con cols=%d lines=%d", windowWidth, windowHeight); 14 system(buffer); 15 16 //隱藏光標 17 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 18 CONSOLE_CURSOR_INFO CursorInfo; 19 GetConsoleCursorInfo(handle, &CursorInfo);//獲取控制台光標信息 20 CursorInfo.bVisible = false; //隱藏控制台光標 21 SetConsoleCursorInfo(handle, &CursorInfo);//設置控制台光標狀態 22 //初始化隨機數種子 23 srand((unsigned int)time(0)); 24 }
Snake是控制蛇的類,包括監聽鼠標對蛇的方向做出改變,以及在地圖上移動蛇兩個功能。當然在移動中非常重要的是,判斷蛇是否撞到邊界,是否撞到自己等等。
這里講一下移動應該怎么做,首先蛇的身體是用list來存儲,移動其實很簡單分兩種情況:①沒吃到食物,那么就是蛇頭朝目標方向移動一個作為新的蛇頭加入身體list,然后把蛇的尾部刪除。②那么如果吃到了食物該怎么辦呢?想一想,欸沒錯就是不把蛇尾刪除就是了(相當於增上了一格)。

1 #include"pch.h" 2 #include"Snake.h" 3 #include"GmaeInit.h" 4 #include<iostream> 5 #include<unordered_set> 6 #include<Windows.h> 7 #include <conio.h> 8 9 using namespace std; 10 11 Snake::Snake() { 12 oldDirection = 1; 13 newDirection = -1; 14 body.push_back(point{ 40, 15 }); 15 body.push_back(point{ 39,15 }); 16 body.push_back(point{ 38,15 }); 17 body.push_back(point{ 37, 15 }); 18 body.push_back(point{ 36, 15 }); 19 } 20 21 //監聽鍵盤 22 void Snake::listen_key_borad() { 23 char ch; 24 if (_kbhit()) //kbhit 非阻塞函數 25 { 26 ch = _getch(); //使用 getch 函數獲取鍵盤輸入 27 switch (ch) 28 { 29 case 'w': 30 case 'W': 31 if (oldDirection == 2) break; 32 newDirection = 0; 33 break; 34 case 's': 35 case 'S': 36 if (oldDirection == 0) break; 37 newDirection = 2; 38 break; 39 case 'a': 40 case 'A': 41 if (oldDirection == 1) break; 42 newDirection = 3; 43 break; 44 case 'd': 45 case 'D': 46 if (oldDirection == 3) break; 47 newDirection = 1; 48 break; 49 } 50 } 51 } 52 53 bool Snake::movePoint(point& pt,int dir) { 54 point newpt = pt; 55 newpt.x += dx[dir]; 56 newpt.y += dy[dir]; 57 if (newpt.x<=1 || newpt.x>=GameInit::windowWidth || newpt.y<=1 || newpt.y>=GameInit::windowHeight) return 0; 58 unordered_set<int> snakebody; 59 for (auto pt : body) snakebody.insert(pt.x*1000+pt.y); 60 if (snakebody.count(newpt.x*1000+newpt.y)) return 0; 61 pt = newpt; 62 return 1; 63 } 64 65 bool Snake::moveHead() { 66 if (newDirection == -1) newDirection = oldDirection; 67 68 point newpt = body.front(); 69 if (movePoint(newpt,newDirection) == 0) return 0; 70 body.push_front(newpt); 71 72 oldDirection = newDirection; newDirection = -1; 73 return 1; 74 } 75 76 bool Snake::moveHeadAndTail() { 77 if (newDirection == -1) newDirection = oldDirection; 78 79 body.pop_back(); 80 point newpt = body.front(); 81 if (movePoint(newpt,newDirection) == 0) return 0; 82 body.push_front(newpt); 83 84 oldDirection = newDirection; newDirection = -1; 85 return 1; 86 }
Food是食物類,只有生成食物只一個主要功能(當然生成還要避免生成在蛇身上)。

1 #include"pch.h" 2 #include<iostream> 3 #include<random> 4 #include<cstdlib> 5 #include"Food.h" 6 #include"GmaeInit.h" 7 #include"Snake.h" 8 9 using namespace std; 10 11 bool Food::EatFood(const list<point>& body) { 12 if (x == body.front().x && y == body.front().y) return 1; 13 else return 0; 14 } 15 16 bool Food::FoodOnBody(int tmpx,int tmpy,const list<point>& body) { 17 for (auto pt : body) 18 if (pt.x == tmpx && pt.y == tmpy) return 1; 19 return 0; 20 } 21 22 void Food::CreateFood(const list<point>& body) { 23 int tmpx,tmpy; 24 do { 25 tmpx = rand() % GameInit::windowWidth + 1; 26 tmpy = rand() % GameInit::windowHeight + 1; 27 } while (FoodOnBody(tmpx, tmpy, body)); 28 this->x = tmpx; 29 this->y = tmpy; 30 }
Display是一個比較關鍵的類,它負責地圖上各個資源的展示,包括蛇身體,食物,地圖邊界,當然也包括各個資源的銷毀。
這個函數要用到一個比較有趣的Window API:SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c); 這個函數能夠移動命令行光標位置,然后我們就能在那個位置輸出符號!!!

1 #include"pch.h" 2 #include"Display.h" 3 #include"GmaeInit.h" 4 #include"Snake.h" 5 #include<iostream> 6 #include<Windows.h> 7 #include <conio.h> 8 9 using namespace std; 10 11 void Display::printBorder() { 12 system("cls"); 13 int n = GameInit::windowHeight; 14 int m = GameInit::windowWidth; 15 for (int i = 1; i <= n; i++) { 16 for (int j = 1; j <= m; j++) { 17 if (i == 1 || i==n || j == 1 || j == m) cout << "#"; else cout << " "; 18 } 19 cout << endl; 20 } 21 } 22 23 //將光標移動到x,y位置 24 void Display::gotoxy(int x, int y) { 25 COORD c; 26 c.X = x; c.Y = y; 27 SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c); 28 } 29 30 void Display::printSnake(const list<point> &body) { 31 for (auto pt : body) { 32 gotoxy(pt.x, pt.y); 33 cout << "*"; 34 } 35 } 36 37 void Display::destroySnake(const list<point> &body) { 38 for (auto pt : body) { 39 gotoxy(pt.x, pt.y); 40 cout << " "; 41 } 42 } 43 44 void Display::printFood(int x, int y) { 45 gotoxy(x, y); 46 cout << "@"; 47 } 48 49 void Display::destroyFood(int x, int y) { 50 gotoxy(x, y); 51 cout << " "; 52 } 53 54 void Display::clean() { 55 for (int i = 32; i <= 50; i++) { 56 gotoxy(i, 16); 57 cout << " "; 58 } 59 } 60 61 void Display::gameover() { 62 string over = "游戲結束"; 63 for (int i = 32; i <= 40; i++) { 64 gotoxy(i, 16); 65 cout << over[i-32]; 66 } 67 }
這只是個心血來潮做的小demo,讀者很容易能看懂,也很容易為它擴展各種功能:例如計分板,根據玩家選難度加快蛇移動速度,加入經典的無邊界地圖玩法等等。
游戲項目Github地址:https://github.com/Clno1/Blockade 。改一改玩一玩很是很歡樂的,哈哈哈。