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("欢迎来到贪吃蛇小游戏
");
15 SetPos(35, 25);
16 system("pause");//暂停
17 system("cls");//清空屏幕
18 SetPos(30, 9);
19 printf("用↑,↓,←,→来控制蛇的移动,F1为加速,F2为减速
");
20 SetPos(40, 11);
21 printf("加速获得分数更多
");
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为减速
");
294 SetPos(65, 13);
295 printf("加速获得分数更多
");
296 SetPos(65, 14);
297 printf("按空格暂停游戏
");
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("游戏正常接结束
");
309 SetPos(70, 21);
310 }
311 else if (ps->_Status == KILL_BY_SELF)
312 {
313 SetPos(70, 20);
314 printf("蛇撞到自己了
");
315 SetPos(70, 21);
316 }
317 else if (ps->_Status == KILL_BY_WALL)
318 {
319 SetPos(70, 20);
320 printf("撞到墙了
");
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 }