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 }