1.分析
眾所周知,貪吃蛇游戲是一款經典的益智游戲,有PC和手機等多平台版本,既簡單又耐玩。該游戲通過控制蛇頭方向吃食物,從而使得蛇變得越來越長,蛇不能撞牆,也不能裝到自己,否則游戲結束。玩過貪吃蛇的朋友都知道這個小游戲有一圈“牆”、還有食物以及蛇,還有吃了食物之后的分數。所以通過C語言實現貪吃蛇,可以分為以下幾個模塊來實現:
a)編程實現“牆”
b)實現隨機食物的生成
c)蛇的構建
d)以上三部分都實現了之后,實現游戲運行的部分,包括蛇的移動、吃食物、判斷是否撞牆或者撞到自己。
e)游戲結束時的相關操作
2.具體思路
要通過C語言實現貪吃蛇,首先就要用C語言將貪吃蛇的相關信息描述出來。C語言提供了結構體類型,我們可以用結構體描述蛇的結點(結點的橫縱坐標)、運動狀態、總分數、每個食物的分數等等。
首先實現進入游戲的"welcome"界面,為了將“歡迎來到貪吃蛇游戲”打印到屏幕的中間,可以運用庫函數先設置窗口的大小(system("mode con cols=100 lines=30");將窗口設置為長100,寬30),再通過庫函數中的GetStdHandle函數、SetConsoleCursorPosition函數(把光標定位到我們需要的位置)將這句話打印到屏幕中央。
其次可以通過相同的方法將光標固定到我們需要的位置,用特殊字符‘■’打印實現牆(注意,‘■’在c中占用兩個字符,所以在設置循環條件時要格外注意)
接着通過編程實現蛇的移動,本文中蛇是通過單鏈表實現的,因此在實現蛇的移動時,如果蛇接下來走的位置(上下左右)為食物,將這個位置的用指針表示,可以將食物頭插到用_psnake指針維護的蛇身中,如果接下來的位置不是食物,同樣頭插到蛇身中,再將蛇的最后一個結點打印時輸出為空格字符,然后free最后一個節點。
3.代碼如下
snake.h:
1 #pragma once
2
3 #include<Windows.h>
4 #include<stdio.h>
5 #include<time.h>
6 #include<stdlib.h>
7
8 #define WALL "■"
9 #define FOOD "●"
10
11 #define INIT_X 20
12 #define INIT_Y 18
13 typedef struct SnakeNode 14 { 15 int x; 16 int y; 17 struct SnakeNode *next; 18 }SnakeNode, *pSnakeNode; 19
20 //蛇的方向
21 enum Diretion 22 { 23 UP, 24 DOWN, 25 LEFT, 26 RIGHT 27 }; 28
29 //狀態
30 enum GameState 31 { 32 OK, 33 NORMAL_END, 34 KILL_BY_WALL, 35 KILL_BY_SELF 36 }; 37 //游戲結構體
38 typedef struct Snake 39 { 40 pSnakeNode _psnake;//維護蛇身的指針
41 pSnakeNode _pFood;//維護食物的位置
42 int _TotalScore;//總分數
43 int _AddScore;//增加的分數
44 int _SleepTime;//休眠時間
45 enum Direction _Dir; 46 enum GameStatus _Status; 47 }Snake, *pSnake; 48
49 void GameStart(pSnake ps); 50 void WelcomeToGame();//歡迎界面函數
51 void SetPos(int x, int y); 52 void CreateMap();//創建地圖
53 void InitSnake(pSnake ps); 54 void CreateFood(pSnake ps);//創建食物
55 void GameRun(pSnake ps);//運行游戲
56 void pause();//暫停函數
57
58 void snakmove(pSnake ps); 59 int NextHasFood(pSnakeNode pn, pSnakeNode pf);//判斷下個結點是否為食物
60 void EatFood(pSnake ps,pSnakeNode pn);//吃食物
61 void NoFood(pSnake ps, pSnakeNode pn);//沒有食物
62 void KillBYWALL(pSnake ps); 63 void KillBySelf(pSnake ps); 64 void GameEnd(pSnake ps);
snake.c:
1 #include"snake.h"
2 void SetPos(int x, int y) 3 { 4 COORD pos = { 0 }; 5 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 6 pos.X = x; pos.Y = y; 7 SetConsoleCursorPosition(handle, pos);//把光標定位到我們需要的位置了
8 } 9 void WelcomeToGame()//歡迎界面函數
10 { 11 //設置窗口大小
12 system("mode con cols=100 lines=30"); 13 SetPos(40, 13);//分裝的函數用來定位光標
14 printf("歡迎來到貪吃蛇小游戲\n"); 15 SetPos(35, 25); 16 system("pause");//暫停
17 system("cls");//清空屏幕
18 SetPos(30, 9); 19 printf("用↑,↓,←,→來控制蛇的移動,F1為加速,F2為減速\n"); 20 SetPos(40, 11); 21 printf("加速獲得分數更多\n"); 22 SetPos(35, 25); 23 system("pause"); 24 system("cls"); 25 } 26
27 void CreateMap() 28 { 29 int i = 0; 30 //上
31 for (i = 0; i <= 58; i += 2) 32 { 33 SetPos(i, 0); 34 printf(WALL); 35 } 36 //下
37 for (i = 0; i <= 58; i += 2) 38 { 39 SetPos(i, 27); 40 printf(WALL); 41 } 42 //左
43 for (i = 1; i <= 26; i++) 44 { 45 SetPos(0, i); 46 printf(WALL); 47 } 48 //右
49 for (i = 1; i <= 26; i++) 50 { 51 SetPos(58, i); 52 printf(WALL); 53 } 54 } 55 pSnakeNode BuyNode() 56 { 57 pSnakeNode pRet = (pSnakeNode)malloc(sizeof(SnakeNode)); 58 if (pRet == NULL) 59 { 60 perror("BuyNode::malloc()"); 61 exit(EXIT_FAILURE); 62 } 63 pRet->x = 0; 64 pRet->y = 0; 65 pRet->next = NULL; 66 return pRet; 67 } 68 void InitSnake(pSnake ps) 69 { 70 pSnakeNode first = BuyNode();//創建蛇頭
71 pSnakeNode cur = NULL; 72 first->x = INIT_X; 73 first->y = INIT_Y; 74 int i = 0; 75 for (i = 1; i <= 4; i++) 76 { 77 cur = BuyNode();//創建蛇身
78 cur->x = first->x + 2; 79 cur->y = first->y; 80 cur->next = first; 81 first = cur; 82 } 83 //打印蛇
84 while (cur) 85 { 86 SetPos(cur->x, cur->y); 87 printf(FOOD); 88 cur = cur->next; 89 } 90 ps->_psnake = first; 91 } 92
93 void CreateFood(pSnake ps) 94 { 95 pSnakeNode pfood = BuyNode(); 96 pSnakeNode cur = ps->_psnake; 97 pfood->y = rand() % 26 + 1; 98 do
99 { 100 pfood->x = rand() % 55 + 2;//0-54,+2產生2-56
101 } while (pfood->x % 2 != 0); 102 while (cur) 103 { 104 if (cur->x == pfood->x&&cur->y == pfood->y) 105 { 106 CreateFood(ps); 107 } 108 cur = cur->next; 109 } 110 ps->_pFood = pfood; 111 SetPos(pfood->x, pfood->y); 112 printf(FOOD); 113 } 114
115 void GameStart(pSnake ps) 116 { 117 //打印歡迎界面
118 WelcomeToGame(); 119 //創建地圖,畫牆
120 CreateMap(); 121 //初始化蛇
122 InitSnake(ps); 123 //初始化食物
124 CreateFood(ps); 125 //游戲運行
126 ps->_AddScore = 10;//每次增加的分數
127 ps->_TotalScore = 0;//總分
128 ps->_Dir = RIGHT;//方向
129 ps->_SleepTime = 200;//0.2秒
130 ps->_Status = OK;//狀態
131 } 132
133 void pause()//暫停函數
134 { 135 while (1) 136 { 137 Sleep(100); 138 if (GetAsyncKeyState(VK_SPACE)) 139 { 140 return; 141 } 142 } 143 } 144
145 int NextHasFood(pSnakeNode pn, pSnakeNode pf) 146 { 147 return (pn->x == pf->x) && (pn->y == pf->y); 148 } 149
150 void EatFood(pSnake ps, pSnakeNode pn)//吃食物
151 { 152 pSnakeNode cur = NULL; 153 pn->next = ps->_psnake;//頭插
154 ps->_psnake = pn; 155 cur = ps->_psnake; 156 ps->_TotalScore += ps->_AddScore; 157 while (cur) 158 { 159 SetPos(cur->x, cur->y); 160 printf(FOOD); 161 cur = cur->next; 162 } 163 CreateFood(ps); 164 } 165
166 void NoFood(pSnake ps, pSnakeNode pn)//沒有食物
167 { 168 pSnakeNode cur = NULL; 169 pn->next = ps->_psnake;//頭插
170 ps->_psnake = pn; 171 cur = ps->_psnake; 172 while (cur->next->next) 173 { 174 SetPos(cur->x, cur->y); 175 printf(FOOD); 176 cur = cur->next; 177 } 178 SetPos(cur->x, cur->y); 179 printf(FOOD); 180 SetPos(cur->next->x, cur->next->y); 181 printf(" "); 182 free(cur->next); 183 cur->next = NULL; 184 } 185
186 void snakmove(pSnake ps)//蛇的移動
187 { 188 pSnakeNode pNextNode = BuyNode(); 189 SetPos(65, 8); 190 printf("總分:%d", ps->_TotalScore); 191 SetPos(65, 9); 192 printf("每個食物的得分:%d ", ps->_AddScore); 193 //上
194 switch (ps->_Dir) 195 { 196 case UP: 197 { 198 pNextNode->x = ps->_psnake->x; 199 pNextNode->y = ps->_psnake->y - 1; 200 if (NextHasFood(pNextNode,ps->_pFood)) 201 { 202 //下一個節點是食物
203 EatFood(ps,pNextNode); 204 } 205 else
206 { 207 NoFood(ps,pNextNode); 208 } 209 break; 210 } 211 case DOWN: 212 { 213 pNextNode->x = ps->_psnake->x; 214 pNextNode->y = ps->_psnake->y + 1; 215 if (NextHasFood(pNextNode, ps->_pFood)) 216 { 217 //下一個節點是食物
218 EatFood(ps, pNextNode); 219 } 220 else
221 { 222 NoFood(ps, pNextNode); 223 } 224 break; 225 } 226 case LEFT: 227 { 228 pNextNode->x = ps->_psnake->x-2; 229 pNextNode->y = ps->_psnake->y; 230 if (NextHasFood(pNextNode, ps->_pFood)) 231 { 232 //下一個節點是食物
233 EatFood(ps, pNextNode); 234 } 235 else
236 { 237 NoFood(ps, pNextNode); 238 } 239 break; 240 } 241 case RIGHT: 242 { 243 pNextNode->x = ps->_psnake->x+2; 244 pNextNode->y = ps->_psnake->y ; 245 if (NextHasFood(pNextNode, ps->_pFood)) 246 { 247 //下一個節點是食物
248 EatFood(ps, pNextNode); 249 } 250 else
251 { 252 NoFood(ps, pNextNode); 253 } 254 break; 255 } 256 default: 257 { 258 break; 259 } 260 } 261 } 262
263 void KillBYWALL(pSnake ps) 264 { 265 if ((ps->_psnake->x == 0) ||
266 (ps->_psnake->x == 58) ||
267 (ps->_psnake->y == 0) ||
268 (ps->_psnake->y == 27)) 269 { 270 ps->_Status = KILL_BY_WALL; 271 } 272 } 273
274 void KillBySelf(pSnake ps) 275 { 276 pSnakeNode tnext = ps->_psnake->next; 277 while (tnext) 278 { 279 if ((tnext->x == ps->_psnake->x) && (tnext->y == ps->_psnake->y)) 280 { 281 ps->_Status = KILL_BY_SELF; 282 return; 283 } 284 tnext = tnext->next; 285 } 286
287 } 288 void PrintHelpInfo() 289 { 290 SetPos(65, 11); 291 printf("用↑,↓,←,→來控制蛇的移動"); 292 SetPos(65, 12); 293 printf("F1為加速,F2為減速\n"); 294 SetPos(65, 13); 295 printf("加速獲得分數更多\n"); 296 SetPos(65, 14); 297 printf("按空格暫停游戲\n"); 298
299 } 300
301 void GameEnd(pSnake ps) 302 { 303 pSnakeNode cur = ps->_psnake; 304 SetPos(25, 14); 305 if (ps->_Status == NORMAL_END) 306 { 307 SetPos(70, 20); 308 printf("游戲正常接結束\n"); 309 SetPos(70, 21); 310 } 311 else if (ps->_Status == KILL_BY_SELF) 312 { 313 SetPos(70, 20); 314 printf("蛇撞到自己了\n"); 315 SetPos(70, 21); 316 } 317 else if (ps->_Status == KILL_BY_WALL) 318 { 319 SetPos(70, 20); 320 printf("撞到牆了\n"); 321 SetPos(70, 21); 322 } 323 while (cur) 324 { 325 pSnakeNode del = cur; 326 cur = cur->next; 327 free(del); 328 del = NULL; 329 } 330 ps->_psnake = NULL; 331 free(ps->_pFood); 332 ps->_pFood = NULL; 333 } 334
335 void GameRun(pSnake ps) 336 { 337 PrintHelpInfo(); 338 do
339 { 340 //確定方向
341 if (GetAsyncKeyState(VK_UP)&&(ps->_Dir!=DOWN)) 342 { 343 ps->_Dir = UP; 344 } 345 else if (GetAsyncKeyState(VK_DOWN) && ps->_Dir != UP) 346 { 347 ps->_Dir = DOWN; 348 } 349 else if (GetAsyncKeyState(VK_LEFT) && ps->_Dir != RIGHT) 350 { 351 ps->_Dir = LEFT; 352 } 353 else if (GetAsyncKeyState(VK_RIGHT) && ps->_Dir != LEFT) 354 { 355 ps->_Dir = RIGHT; 356 } 357 else if (GetAsyncKeyState(VK_SPACE)) 358 { 359 //暫停游戲
360 pause(); 361 } 362 else if (GetAsyncKeyState(VK_ESCAPE)) 363 { 364 //結束游戲
365 ps->_Status = NORMAL_END; 366 break; 367 } 368 else if (GetAsyncKeyState(VK_F1)) 369 { 370 //加速
371 if (ps->_SleepTime >= 40) 372 { 373 ps->_SleepTime -= 20; 374 ps->_AddScore += 2; 375 } 376 } 377 else if (GetAsyncKeyState(VK_F2)) 378 { 379 //減速
380 if (ps->_SleepTime <= 300) 381 { 382 ps->_SleepTime += 20; 383 ps->_AddScore -= 2; 384 } 385 if (ps->_SleepTime > 300) 386 { 387 ps->_AddScore = 1;//不能一直減
388 } 389 } 390 Sleep(ps->_SleepTime);//睡眠 391 //蛇的移動
392 snakmove(ps); 393 KillBYWALL(ps); 394 KillBySelf(ps); 395 } while (ps->_Status == OK); 396 }
test.c:
1 #include"snake.h"
2
3 void test() 4 { 5 srand((unsigned)time(NULL)); 6 Snake snake = { 0 };//創建貪吃蛇
7 GameStart(&snake); 8 GameRun(&snake); 9 GameEnd(&snake); 10 } 11
12 int main() 13 { 14 test(); 15 system("pause"); 16 return 0; 17 }
