小言_互联网的博客

C语言贪吃蛇(详解)——链表实现

297人阅读  评论(0)

贪吃蛇设计思路:

屏幕坐标:

 

拓展功能:

1.F1,F2控制加速减速  空格暂停游戏  Esc退出

2.加速每个食物得分更高

 

 

先打印出游戏界面,还有初始化蛇,蛇的节点用字符串★表示,游戏背景用■表示,因为这些字符串占两个字节的宽度,所以每次x,y坐标的对应关系是x=y*2。在相应位置打印出蛇,初始化蛇为五个节点

初始化蛇头的移动方向为右,根据按键来确定蛇的移动状态,要是吃到食物,就让食物为蛇头,然后随机生成食物,

 

 

重点模块:

 

蛇移动的实现:

      定义一个结构体,里面放节点坐标,和next指针。

蛇每次移动是通过用户按键方向来确定下一个蛇头节点的x,y坐标,新建一个节点赋给下一个坐标,在这个坐标打出蛇的图标,找到尾节点,将尾节点打印成背景图标,再将节点释放,这样蛇就动了一下,以此重复,蛇就可以了动态移动。

 

食物的实现:

     定义一个食物节点,也是一个类似于蛇的结构体指针,通过随机数生成坐标,注意不能在墙上和蛇身上。

用户按键的检测:可以使用wasd的字符来确定方向,但这里我们还需要使用空格,ESC键,上下左右键,所以直接一点,通过GetAsyncKeyState( )函数检测用户输入的按键,需要引用头文件conio.h,_getch()函数用来检测当前是否有按键输入,有的话返回非0的数。

蛇的变速:还是通过GetAsyncKeyState( )函数检测F1,F2,键来确定蛇移动一次Sleep()函数中的毫秒数,这样就实现了蛇的加速减速。而且蛇每移动一次,不必使用system(“cls")来清屏,通过在循环中snakemove()执行一次,Sleep()函数执行一次,就实现了动态移动。

 

 

 

实现效果:

 

完整代码:


  
  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include<stdio.h>
  3. #include<time.h>
  4. #include<windows.h>
  5. #include<stdlib.h>
  6. #include<conio.h> //接收键盘输入输出
  7. #define U 1
  8. #define D 2
  9. #define L 3
  10. #define R 4 //蛇的状态,U:上 ;D:下;L:左 R:右
  11. /*******定 义 全 局 变 量 *******/
  12. typedef struct snake //蛇身的一个节点
  13. {
  14. int x;
  15. int y;
  16. struct snake *next;
  17. }snake;
  18. int score= 0,add= 10; //总得分与每次吃食物得分
  19. int status,sleeptime= 200; //蛇前进状态,每次运行的时间间隔
  20. snake *head, *food; //蛇头指针,食物指针
  21. snake *q; //遍历蛇的时候用到的指针
  22. int endgamestatus= 0; //游戏结束的情况,1:撞到墙;2:咬到自己;3:主动退出游戏。
  23. HANDLE hOut; //控制台句柄
  24. /*******函 数 声 明 *******/
  25. void gotoxy(int x,int y); //设置光标位置
  26. int color(int c); //更改文字颜色
  27. void welcometogame(); //开始界面
  28. void createMap(); //绘制地图
  29. void scoreandtips(); //游戏界面右侧的得分和小提示
  30. void initsnake(); //初始化蛇身,画蛇身
  31. void createfood(); //创建并随机出现食物
  32. int biteself(); //判断是否咬到了自己
  33. void cantcrosswall(); //设置蛇撞墙的情况
  34. void speedup(); //加速
  35. void speeddown(); //减速
  36. void snakemove(); //控制蛇前进方向
  37. void keyboardControl(); //控制键盘按键
  38. void Lostdraw(); //游戏结束界面
  39. void endgame(); //游戏结束
  40. void choose(); //游戏失败之后的选择
  41. void explation(); //游戏说明
  42. /**
  43. * 设置光标位置
  44. */
  45. void gotoxy(int x,int y)
  46. {
  47. COORD c;
  48. c.X=x;
  49. c.Y=y;
  50. SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c);
  51. }
  52. /**
  53. * 文字颜色函数
  54. */
  55. int color(int c)
  56. {
  57. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //更改文字颜色
  58. return 0;
  59. }
  60. //开始界面
  61. void welcometogame()
  62. {
  63. int n;
  64. gotoxy( 43, 10);
  65. color( 11);
  66. printf( "贪 吃 蛇 大 作 战");
  67. color( 12);
  68. gotoxy( 25, 22);
  69. printf( "1.开始游戏");
  70. gotoxy( 45, 22);
  71. printf( "2.游戏说明");
  72. gotoxy( 65, 22);
  73. printf( "3.退出游戏");
  74. gotoxy( 40, 27);
  75. color( 3);
  76. printf( "请选择 1 2 3:");
  77. color( 14);
  78. scanf( "%d", &n); //输入选项
  79. switch (n)
  80. {
  81. case 1:
  82. system( "cls");
  83. createMap(); //创建地图
  84. initsnake(); //初始化蛇身
  85. createfood(); //创建食物
  86. keyboardControl(); //按键控制
  87. break;
  88. case 2:
  89. explation(); //游戏说明函数
  90. break;
  91. case 3:
  92. exit( 0); //退出游戏
  93. break;
  94. default:
  95. color( 12);
  96. gotoxy( 40, 28);
  97. printf( "请输入1—3之间的数!");
  98. _getch(); //输入任意键
  99. system( "cls"); //清屏
  100. welcometogame();
  101. }
  102. }
  103. //创建地图
  104. void createMap()
  105. {
  106. int i,j;
  107. for(i= 0;i< 58;i+= 2) //打印上下边框
  108. {
  109. gotoxy(i, 0);
  110. color( 5);
  111. printf( "□");
  112. gotoxy(i, 26);
  113. printf( "□");
  114. }
  115. for(i= 1;i< 26;i++) //打印左右边框
  116. {
  117. gotoxy( 0,i);
  118. printf( "□");
  119. gotoxy( 56,i);
  120. printf( "□");
  121. }
  122. for(i = 2;i< 56;i+= 2) //打印中间网格
  123. {
  124. for(j = 1;j< 26;j++)
  125. {
  126. gotoxy(i,j);
  127. color( 3);
  128. printf( "■");
  129. }
  130. }
  131. }
  132. // 游戏界面右侧的得分和小提示
  133. void scoreandtips()
  134. {
  135. gotoxy( 64, 8);
  136. color( 14);
  137. printf( "得分:%d ",score);
  138. gotoxy( 64, 14);
  139. printf( "每个食物得分:%d分",add);
  140. gotoxy( 64, 16);
  141. printf( "不能穿墙,不能咬到自己");
  142. gotoxy( 64, 18);
  143. printf( "用↑ ↓ ← →分别控制蛇的移动");
  144. gotoxy( 64, 20);
  145. printf( "F1 为加速,F2 为减速");
  146. gotoxy( 64, 22);
  147. printf( "space:暂停游戏");
  148. gotoxy( 64, 24);
  149. printf( "ESC :退出游戏");
  150. }
  151. //初始化蛇身,画蛇身
  152. void initsnake()
  153. {
  154. snake *tail;
  155. int i;
  156. tail=(snake*) malloc( sizeof(snake)); //从蛇尾开始,头插法,以x,y设定开始的位置
  157. tail->x= 24; //蛇的初始位置(24,5)
  158. tail->y= 5;
  159. tail->next= NULL;
  160. for(i= 1;i<= 4;i++) //设置蛇身,长度为5
  161. {
  162. head=(snake*) malloc( sizeof(snake)); //初始化蛇头
  163. head->next=tail; //蛇头的下一位为蛇尾
  164. head->x= 24+ 2*i; //设置蛇头位置
  165. head->y= 5;
  166. tail=head; //蛇头变成蛇尾,然后重复循环
  167. }
  168. while(tail!= NULL) //从头到尾,输出蛇身
  169. {
  170. gotoxy(tail->x,tail->y);
  171. color( 14);
  172. printf( "★"); //输出蛇身,蛇身使用★组成
  173. tail=tail->next; //蛇头输出完毕,输出蛇头的下一位,一直输出到蛇尾
  174. }
  175. }
  176. /**
  177. * 随机出现食物
  178. */
  179. void createfood()
  180. {
  181. snake *food_1;
  182. srand(( unsigned)time( NULL)); //初始化随机数
  183. food_1=(snake*) malloc( sizeof(snake)); //初始化food_1
  184. while((food_1->x% 2)!= 0) //保证其为偶数,使得食物能与蛇头对其,然后食物会出现在网格线上
  185. {
  186. food_1->x=rand()% 52+ 2; //食物随机出现
  187. }
  188. food_1->y=rand()% 24+ 1;
  189. q=head;
  190. while(q->next== NULL)
  191. {
  192. if(q->x==food_1->x && q->y==food_1->y) //判断蛇身是否与食物重合
  193. {
  194. free(food_1); //如果蛇身和食物重合,那么释放食物指针
  195. createfood(); //重新创建食物
  196. }
  197. q=q->next;
  198. }
  199. gotoxy(food_1->x,food_1->y);
  200. food=food_1;
  201. color( 12);
  202. printf( "●"); //输出食物
  203. }
  204. /**
  205. * 判断是否咬到了自己
  206. */
  207. int biteself()
  208. {
  209. snake *self; //定义self为蛇身上的一个节点
  210. self=head->next; //self是蛇头之外的蛇身上的节点
  211. while(self!= NULL)
  212. {
  213. if(self->x==head->x && self->y==head->y) //如果self和蛇身上的节点重合
  214. {
  215. return 1; //返回1
  216. }
  217. self=self->next;
  218. }
  219. return 0;
  220. }
  221. /**
  222. * 设置蛇撞墙的情况
  223. */
  224. void cantcrosswall()
  225. {
  226. if(head->x== 0 || head->x== 56 ||head->y== 0 || head->y== 26) //如果蛇头碰到了墙壁
  227. {
  228. endgamestatus= 1; //返回第一种情况
  229. endgame(); //出现游戏结束界面
  230. }
  231. }
  232. /**
  233. * 加速,蛇吃到食物会自动提速,并且按F1会加速
  234. */
  235. void speedup()
  236. {
  237. if(sleeptime>= 50)
  238. {
  239. sleeptime=sleeptime -10;
  240. add=add+ 2;
  241. }
  242. }
  243. /**
  244. * 加速,按F2会减速
  245. */
  246. void speeddown()
  247. {
  248. if(sleeptime< 350) //如果时间间隔小于350
  249. {
  250. sleeptime=sleeptime+ 30; //时间间隔加上30
  251. add=add -2; //每吃一次食物的得分减2
  252. }
  253. }
  254. /**
  255. * 控制方向 问题:为什么要设置status,而不使用前两章中接收键盘按键的方法
  256. */
  257. void snakemove() //蛇前进,上U,下D,左L,右R
  258. {
  259. snake * nexthead;
  260. cantcrosswall();
  261. nexthead=(snake*) malloc( sizeof(snake)); //为下一步开辟空间
  262. if(status==U)
  263. {
  264. nexthead->x=head->x; //向上前进时,x坐标不动,y坐标-1
  265. nexthead->y=head->y -1;
  266. nexthead->next=head;
  267. head=nexthead;
  268. q=head; //指针q指向蛇头
  269. if(nexthead->x==food->x && nexthead->y==food->y) //如果下一个有食物 下一个位置的坐标和食物的坐标相同
  270. {
  271. while(q!= NULL)
  272. {
  273. gotoxy(q->x,q->y);
  274. color( 14);
  275. printf( "★"); //原来食物的位置,从●换成★
  276. q=q->next; //指针q指向的蛇身的下一位也执行循环里的操作
  277. }
  278. score=score+add; //吃了一个食物,在总分上加上食物的分
  279. speedup();
  280. createfood(); //创建食物
  281. }
  282. else
  283. {
  284. while(q->next->next!= NULL) //如果没遇到食物
  285. {
  286. gotoxy(q->x,q->y);
  287. color( 14);
  288. printf( "★"); //蛇正常往前走,输出当前位置的蛇身
  289. q=q->next; //继续输出整个蛇身
  290. }
  291. gotoxy(q->next->x,q->next->y); //经过上面的循环,q指向蛇尾,蛇尾的下一位,就是蛇走过去的位置
  292. color( 3);
  293. printf( "■");
  294. free(q->next); //进行输出■之后,释放指向下一位的指针
  295. q->next= NULL; //指针下一位指向空
  296. }
  297. }
  298. if(status==D)
  299. {
  300. nexthead->x=head->x; //向下前进时,x坐标不动,y坐标+1
  301. nexthead->y=head->y+ 1;
  302. nexthead->next=head;
  303. head=nexthead;
  304. q=head;
  305. if(nexthead->x==food->x && nexthead->y==food->y) //有食物
  306. {
  307. while(q!= NULL)
  308. {
  309. gotoxy(q->x,q->y);
  310. color( 14);
  311. printf( "★");
  312. q=q->next;
  313. }
  314. score=score+add;
  315. speedup();
  316. createfood();
  317. }
  318. else //没有食物
  319. {
  320. while(q->next->next!= NULL)
  321. {
  322. gotoxy(q->x,q->y);
  323. color( 14);
  324. printf( "★");
  325. q=q->next;
  326. }
  327. gotoxy(q->next->x,q->next->y);
  328. color( 3);
  329. printf( "■");
  330. free(q->next);
  331. q->next= NULL;
  332. }
  333. }
  334. if(status==L)
  335. {
  336. nexthead->x=head->x -2; //向左前进时,x坐标向左移动-2,y坐标不动
  337. nexthead->y=head->y;
  338. nexthead->next=head;
  339. head=nexthead;
  340. q=head;
  341. if(nexthead->x==food->x && nexthead->y==food->y) //有食物
  342. {
  343. while(q!= NULL)
  344. {
  345. gotoxy(q->x,q->y);
  346. color( 14);
  347. printf( "★");
  348. q=q->next;
  349. }
  350. score=score+add;
  351. speedup();
  352. createfood();
  353. }
  354. else //没有食物
  355. {
  356. while(q->next->next!= NULL)
  357. {
  358. gotoxy(q->x,q->y);
  359. color( 14);
  360. printf( "★");
  361. q=q->next;
  362. }
  363. gotoxy(q->next->x,q->next->y);
  364. color( 3);
  365. printf( "■");
  366. free(q->next);
  367. q->next= NULL;
  368. }
  369. }
  370. if(status==R)
  371. {
  372. nexthead->x=head->x+ 2; //向右前进时,x坐标向右移动+2,y坐标不动
  373. nexthead->y=head->y;
  374. nexthead->next=head;
  375. head=nexthead;
  376. q=head;
  377. if(nexthead->x==food->x && nexthead->y==food->y) //有食物
  378. {
  379. while(q!= NULL)
  380. {
  381. gotoxy(q->x,q->y);
  382. color( 14);
  383. printf( "★");
  384. q=q->next;
  385. }
  386. score=score+add;
  387. speedup();
  388. createfood();
  389. }
  390. else //没有食物
  391. {
  392. while(q->next->next!= NULL)
  393. {
  394. gotoxy(q->x,q->y);
  395. color( 14);
  396. printf( "★");
  397. q=q->next;
  398. }
  399. gotoxy(q->next->x,q->next->y);
  400. color( 3);
  401. printf( "■");
  402. free(q->next);
  403. q->next= NULL;
  404. }
  405. }
  406. if(biteself()== 1) //判断是否会咬到自己
  407. {
  408. endgamestatus= 2;
  409. endgame();
  410. }
  411. }
  412. /**
  413. * 控制键盘按键
  414. */
  415. void keyboardControl()
  416. {
  417. status=R; //初始蛇向右移动
  418. while( 1)
  419. {
  420. scoreandtips();
  421. if(GetAsyncKeyState(VK_UP) && status!=D) //GetAsyncKeyState函数用来判断函数调用时指定虚拟键的状态
  422. {
  423. status=U; //如果蛇不是向下前进的时候,按上键,执行向上前进操作
  424. }
  425. else if(GetAsyncKeyState(VK_DOWN) && status!=U) //如果蛇不是向上前进的时候,按下键,执行向下前进操作
  426. {
  427. status=D;
  428. }
  429. else if(GetAsyncKeyState(VK_LEFT)&& status!=R) //如果蛇不是向右前进的时候,按左键,执行向左前进
  430. {
  431. status=L;
  432. }
  433. else if(GetAsyncKeyState(VK_RIGHT)&& status!=L) //如果蛇不是向左前进的时候,按右键,执行向右前进
  434. {
  435. status=R;
  436. }
  437. if(GetAsyncKeyState(VK_SPACE)) //按暂停键,执行pause暂停函数
  438. {
  439. while( 1)
  440. {
  441. Sleep( 300); //sleep()函数,头文件#include <unistd.h> 另进程暂停,知道达到里面设定的参数的时间。
  442. if(GetAsyncKeyState(VK_SPACE)) //按空格键暂停
  443. {
  444. break;
  445. }
  446. }
  447. }
  448. else if(GetAsyncKeyState(VK_ESCAPE))
  449. {
  450. endgamestatus= 3; //按esc键,直接到结束界面
  451. break;
  452. }
  453. else if(GetAsyncKeyState(VK_F1)) //按F1键,加速
  454. {
  455. speedup();
  456. }
  457. else if(GetAsyncKeyState(VK_F2)) //按F2键,减速
  458. {
  459. speeddown();
  460. }
  461. Sleep(sleeptime);
  462. snakemove();
  463. }
  464. }
  465. /*
  466. * 游戏说明
  467. */
  468. void explation()
  469. {
  470. //int i,j = 1;
  471. system( "cls");
  472. // color(13);
  473. // gotoxy(44,3);
  474. // printf("游戏说明");
  475. // color(2);
  476. // for (i = 6; i <= 22; i++) //输出上下边框===
  477. //{
  478. // for (j = 20; j <= 75; j++) //输出左右边框||
  479. // {
  480. // gotoxy(j, i);
  481. // if (i == 6 || i == 22) printf("=");
  482. // else if (j == 20 || j == 75) printf("||");
  483. // }
  484. //}
  485. color( 3);
  486. gotoxy( 30, 8);
  487. printf( "1. 不能穿墙,不能咬到自己");
  488. color( 10);
  489. gotoxy( 30, 11);
  490. printf( "2. 用↑.↓.←.→分别控制蛇的移动");
  491. color( 14);
  492. gotoxy( 30, 14);
  493. printf( "3. F1 为加速,F2 为减速");
  494. color( 11);
  495. gotoxy( 30, 17);
  496. printf( "4. 按空格键暂停游戏,再按空格键继续");
  497. color( 4);
  498. gotoxy( 30, 20);
  499. printf( "5. ESC :退出游戏.space:暂停游戏");
  500. _getch(); //按任意键返回主界面
  501. system( "cls");
  502. welcometogame();
  503. }
  504. /**
  505. * 结束游戏
  506. */
  507. void endgame()
  508. {
  509. system( "cls");
  510. if(endgamestatus== 1)
  511. {
  512. //Lostdraw();
  513. gotoxy( 43, 9);
  514. color( 12);
  515. printf( "GAME OVER !");
  516. }
  517. else if(endgamestatus== 2)
  518. {
  519. //Lostdraw();
  520. gotoxy( 43, 9);
  521. color( 12);
  522. printf( "GAME OVER !");
  523. }
  524. else if(endgamestatus== 3)
  525. {
  526. //Lostdraw();
  527. gotoxy( 40, 9);
  528. color( 12);
  529. printf( "已结束游戏。");
  530. }
  531. gotoxy( 43, 12);
  532. color( 13);
  533. printf( "你的得分是 %d",score);
  534. choose();
  535. }
  536. /**
  537. * 边框下面的分支选项
  538. */
  539. void choose()
  540. {
  541. int n;
  542. gotoxy( 25, 23);
  543. color( 12);
  544. printf( "Continue ------ 1");
  545. gotoxy( 52, 23);
  546. printf( "Exit ------ 2");
  547. gotoxy( 45, 25);
  548. color( 11);
  549. printf( "选择: ");
  550. scanf( "%d", &n);
  551. switch (n)
  552. {
  553. case 1:
  554. system( "cls"); //清屏
  555. score= 0; //分数归零
  556. sleeptime= 200; //设定初始速度
  557. add = 10; //使add设定为初值,吃一个食物得分10,然后累加
  558. welcometogame();
  559. break;
  560. case 2:
  561. exit( 0); //退出游戏
  562. break;
  563. default:
  564. gotoxy( 35, 27);
  565. color( 12);
  566. printf( " 输入有误 重新输入 !");
  567. system( "pause >nul");
  568. endgame();
  569. choose();
  570. break;
  571. }
  572. }
  573. /**
  574. * 失败界面
  575. */
  576. void Lostdraw()
  577. {
  578. system( "cls");
  579. int i;
  580. gotoxy( 45, 2);
  581. color( 6);
  582. printf( "\\\\\\|///");
  583. gotoxy( 43, 3);
  584. printf( "\\\\");
  585. gotoxy( 47, 3);
  586. color( 15);
  587. printf( ".-.-");
  588. gotoxy( 54, 3);
  589. color( 6);
  590. printf( "//");
  591. gotoxy( 44, 4);
  592. color( 14);
  593. printf( "(");
  594. gotoxy( 47, 4);
  595. color( 15);
  596. printf( ".@.@");
  597. gotoxy( 54, 4);
  598. color( 14);
  599. printf( ")");
  600. gotoxy( 17, 5);
  601. color( 11);
  602. printf( "+------------------------");
  603. gotoxy( 35, 5);
  604. color( 14);
  605. printf( "oOOo");
  606. gotoxy( 39, 5);
  607. color( 11);
  608. printf( "----------");
  609. gotoxy( 48, 5);
  610. color( 14);
  611. printf( "(_)");
  612. gotoxy( 51, 5);
  613. color( 11);
  614. printf( "----------");
  615. gotoxy( 61, 5);
  616. color( 14);
  617. printf( "oOOo");
  618. gotoxy( 65, 5);
  619. color( 11);
  620. printf( "-----------------+");
  621. for(i = 6;i<= 19;i++) //竖边框
  622. {
  623. gotoxy( 17,i);
  624. printf( "|");
  625. gotoxy( 82,i);
  626. printf( "|");
  627. }
  628. gotoxy( 17, 20);
  629. printf( "+---------------------------------");
  630. gotoxy( 52, 20);
  631. color( 14);
  632. printf( "☆☆☆〃");
  633. gotoxy( 60, 20);
  634. color( 11);
  635. printf( "----------------------+");
  636. }
  637. /**
  638. * 主函数
  639. */
  640. int main()
  641. {
  642. system( "mode con cols=100 lines=30"); //设置控制台的宽高
  643. //printsnake();
  644. welcometogame();
  645. keyboardControl();
  646. endgame();
  647. return 0;
  648. }

 

 

 


转载:https://blog.csdn.net/yc1515707718/article/details/80461396
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场