鱼C论坛

 找回密码
 立即注册
查看: 5255|回复: 50

[技术交流] 学完第一季C写了个很简单的贪吃蛇嘿嘿嘿

[复制链接]
发表于 2020-3-14 15:04:52 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
学了小甲鱼的C第一季,写了一个贪吃蛇嘿嘿 ,下面是游戏效果图:
游戏界面.png 游戏结束界面.png
写出来就图一乐,为了纪念学完了C的第一季,其实代码语法很简单,逻辑也不难,感觉大概学完数组就可以写了;我的代码有点长,windows环境下运行,复制粘贴就可以玩,希望我们一起加油学,一起奋斗,怀挺!!!
代码附上,注释都写了,哪有不懂得或者有啥建议,欢迎大家讨论
  1. /**************************************************/
  2. /*作者:JZZC
  3. /*日期:2020.3.14
  4. /*可借鉴,可参考,祝君开心
  5. /**************************************************/

  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <windows.h>        //光标设置的API,构建gotoxy()函数有使用
  9. #include <time.h>        //食物随机
  10. #include <conio.h>        //按键监控,构建directionJudge()函数使用

  11. #define         MAPWIDTH         52        //地图宽
  12. #define         MAPHEIGHT         22        //地图高
  13. #define  BOUNDRY         78        //界面右边界
  14. #define         MAXLENGTH       522        //蛇最大身长
  15. #define  INITDIRECTION   'w'        //初始化移动方向
  16. #define  BUMP                 1        //碰撞
  17. #define  NOBUMP                 0        //未碰撞

  18. //变量声明
  19. struct        Food
  20. {
  21.         int x;
  22.         int y;
  23. }food;                                        //食物的结构体

  24. struct        Snake
  25. {
  26.         int length;
  27.         int speed;
  28.         int x[MAXLENGTH];
  29.         int y[MAXLENGTH];
  30. }snake;                                        //蛇的结构体

  31. unsigned int unused = (srand((unsigned int)time(NULL)), 0);//撒随机种子
  32. char moveDirection = INITDIRECTION;        //初始化移动方向
  33. int changeSnake = 0;                        //蛇身长度变化标志位
  34. int bumpFlag = 0;                        //碰撞标志位
  35. int score = 0;                                //得分

  36. //函数声明
  37. void initWall(void);                        //初始化围墙
  38. void gameExplain(void);                        //打印游戏说明
  39. void initSnake(void);                         //初始化一条蛇
  40. void creatFood(void);                        //生成食物
  41. void initGame(void);                        //游戏初始化(调用以上函数)

  42. void gotoxy(int x, int y);                //控制光标的函数,会用即可,都是库函数

  43. void directionJudge(void);                //接收按键键值,判断蛇移动方向
  44. int stateJudge(void);                        //计算出新的蛇坐标后,调用此函数判断是否发生碰撞
  45. void printSnake(void);                        //打印蛇,达到动态效果

  46. void gameOver(void);                        //打印结局

  47. //函数定义
  48. void gotoxy(int x, int y)
  49. {
  50.         //1.找到控制台窗口
  51.         HANDLE        handle = GetStdHandle(STD_OUTPUT_HANDLE);

  52.         //2.声明光标结构体
  53.         COORD coord;

  54.         //3.设置坐标
  55.         coord.X = x;
  56.         coord.Y = y;

  57.         //4.同步到控制台
  58.         SetConsoleCursorPosition(handle, coord);
  59. }

  60. void initWall(void)
  61. {
  62.         int i;
  63.         //1.打印竖起来的围墙
  64.         for (i = 0; i <= MAPHEIGHT; i++)
  65.         {
  66.                 gotoxy(0, i);
  67.                 printf("■");

  68.                 gotoxy(MAPWIDTH, i);
  69.                 printf("■");

  70.                 gotoxy(BOUNDRY, i);
  71.                 printf("■");
  72.         }
  73.         //2.打印上下两排围墙,注意 '■'占两个位置,所以要加2,后面不再强调
  74.         for (i = 0; i <= BOUNDRY; i += 2)
  75.         {
  76.                 gotoxy(i, 0);
  77.                 printf("■");
  78.                 gotoxy(i, MAPHEIGHT);
  79.                 printf("■");
  80.         }
  81. }

  82. void gameExplain(void)
  83. {
  84.         //打印游戏说明
  85.         int a = 0;
  86.         gotoxy(MAPWIDTH + 4, 2);
  87.         printf("游戏说明:W键向上,");
  88.         gotoxy(MAPWIDTH + 4, 3);
  89.         printf("A键向左,S键向下,");
  90.         gotoxy(MAPWIDTH + 4, 4);
  91.         printf("D键向右(不区分大小写)");

  92.         gotoxy(MAPWIDTH + 8, MAPHEIGHT / 2);
  93.         printf("当前分数:%03d", score);

  94.         gotoxy(MAPWIDTH + 8, MAPHEIGHT - 4);
  95.         printf("游戏作者:JZZC");
  96. }

  97. void initSnake(void)
  98. {
  99.         int i;
  100.         //画蛇
  101.         //1 初始化蛇的属性
  102.         snake.length = 3;
  103.         snake.speed = 180;

  104.         //2 画蛇头
  105.         snake.x[0] = MAPWIDTH / 2;
  106.         snake.y[0] = MAPHEIGHT / 2;
  107.         gotoxy(snake.x[0], snake.y[0]);
  108.         printf("⊙");

  109.         //3 画蛇身
  110.         for (i = 1; i < snake.length; i++)
  111.         {
  112.                 snake.x[i] = snake.x[i - 1] + 2;
  113.                 snake.y[i] = snake.y[i - 1];
  114.                 gotoxy(snake.x[i], snake.y[i]);
  115.                 printf("●");
  116.         }
  117. }

  118. void creatFood(void)
  119. {
  120.         int foodOnsnake;
  121.         int i = 0;
  122.         //注意产生的食物不能在蛇身上,横坐标也不能是奇数开头,
  123.         //读者可以想一下为什么要这样规定,想不懂欢迎追问^_^
  124.         do
  125.         {
  126.                 foodOnsnake = 0;
  127.                 food.x = rand() % (MAPWIDTH - 4) + 2;
  128.                 if (food.x % 2)
  129.                 {
  130.                         food.x -= 1;
  131.                 }
  132.                 food.y = rand() % (MAPHEIGHT - 2) + 1;

  133.                 for (i = 0; i < snake.length; i++)
  134.                 {
  135.                         if (food.x == snake.x[i] && food.y == snake.y[i])
  136.                         {
  137.                                 foodOnsnake = 1;
  138.                                 break;
  139.                         }
  140.                 }
  141.         } while (foodOnsnake);

  142.         gotoxy(food.x, food.y);
  143.         printf("◆");
  144. }

  145. void initGame(void)
  146. {
  147.         //1初始化围墙
  148.         initWall();
  149.         //2游戏说明
  150.         gameExplain();
  151.         //3初始化蛇
  152.         initSnake();
  153.         //4画出食物
  154.         creatFood();
  155. }

  156. void gameOver(void)
  157. {
  158.         //1.打印框高
  159.         for (int i = MAPHEIGHT / 4; i <= MAPHEIGHT / 4 * 3; i++)
  160.         {
  161.                 gotoxy(MAPWIDTH / 4, i);
  162.                 printf("■");
  163.                 gotoxy(MAPWIDTH / 4 * 3, i);
  164.                 printf("■");
  165.         }
  166.         //2.打印框宽
  167.         for (int i = MAPWIDTH / 4; i <= MAPWIDTH / 4 * 3; i += 2)
  168.         {
  169.                 gotoxy(i, MAPHEIGHT / 4);
  170.                 printf("■");
  171.                 gotoxy(i, MAPHEIGHT / 4 * 3);
  172.                 printf("■");
  173.         }
  174.         
  175.         //3.框内像素点清除(用两个空格清除)
  176.         for (int i = MAPHEIGHT / 4 + 1; i < MAPHEIGHT / 4 * 3; i++)
  177.         {
  178.                 for (int j = MAPWIDTH / 4 + 2; j < MAPWIDTH / 4 * 3; j += 2)
  179.                 {
  180.                         gotoxy(j, i);
  181.                         printf("  ");
  182.                 }                                
  183.         }
  184.         
  185.         //4.打印Game Over!
  186.         gotoxy(MAPWIDTH / 2 - 4, MAPHEIGHT / 2 - 2);
  187.         printf("Game Over!");
  188. }

  189. void directionJudge(void)
  190. {
  191.         char ch;
  192.         char lastkey = moveDirection;        //记录上次移动方向,防止蛇头往里缩(形象比喻)
  193.         //接收按键值
  194.         if (_kbhit())
  195.         {
  196.                 //清除标准输入流缓存
  197.                 fflush(stdin);
  198.                
  199.                 if ((ch = _getch()) == 'w' || ch == 'W' || ch == 'a' || ch == 'A' || ch == 's'
  200.                         || ch == 'S' || ch == 'd' || ch == 'D')
  201.                 {
  202.                         switch (ch)
  203.                         {
  204.                                 case 'w':
  205.                                 case 'W':
  206.                                         if (lastkey == 's' || lastkey == 'S')
  207.                                         {
  208.                                                 moveDirection = 's';
  209.                                                 break;
  210.                                         }
  211.                                         else
  212.                                         {
  213.                                                 moveDirection = 'w';
  214.                                                 break;
  215.                                         }
  216.                                 case 'a':
  217.                                 case 'A':
  218.                                         if (lastkey == 'd' || lastkey == 'D')
  219.                                         {
  220.                                                 moveDirection = 'd';
  221.                                                 break;
  222.                                         }
  223.                                         else
  224.                                         {
  225.                                                 moveDirection = 'a';
  226.                                                 break;
  227.                                         }
  228.                                 case 's':
  229.                                 case 'S':
  230.                                         if (lastkey == 'w' || lastkey == 'W')
  231.                                         {
  232.                                                 moveDirection = 'w';
  233.                                                 break;
  234.                                         }
  235.                                         else
  236.                                         {
  237.                                                 moveDirection = 's';
  238.                                                 break;
  239.                                         }
  240.                                 case 'd':
  241.                                 case 'D':
  242.                                         if (lastkey == 'a' || lastkey == 'A')
  243.                                         {
  244.                                                 moveDirection = 'a';
  245.                                                 break;
  246.                                         }
  247.                                         else
  248.                                         {
  249.                                                 moveDirection = 'd';
  250.                                                 break;
  251.                                         }
  252.                         }
  253.                 }
  254.         }
  255. }

  256. int stateJudge(void)
  257. {
  258.         int i;
  259.         
  260.         //1.判断是否撞到墙壁
  261.         if (snake.x[0] == 0 || snake.x[0] == MAPWIDTH || snake.y[0] == 0 || snake.y[0] == MAPHEIGHT)
  262.         {
  263.                 return BUMP;
  264.         }
  265.         
  266.         //2.判断是否撞到自己
  267.         for (i = 1; i < snake.length; i++)
  268.         {
  269.                 if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
  270.                 {
  271.                         return BUMP;
  272.                 }
  273.         }

  274.         return NOBUMP;
  275. }

  276. void printSnake(void)
  277. {
  278.         //未算出蛇身新坐标前,先记录下当前的蛇尾坐标。
  279.         int blockx = snake.x[snake.length - 1];
  280.         int blocky = snake.y[snake.length - 1];
  281.         static char lastkey;
  282.         
  283.         lastkey = moveDirection;
  284.         
  285.         //1.判断移动方向
  286.         directionJudge();
  287.         
  288.         //2.计算蛇身坐标(不包含蛇头)
  289.         for (int i = snake.length - 1; i > 0; i--)
  290.         {
  291.                 snake.x[i] = snake.x[i - 1];
  292.                 snake.y[i] = snake.y[i - 1];
  293.         }
  294.         //3.计算蛇头坐标
  295.         switch (moveDirection)
  296.         {
  297.                 case 'w':
  298.                 case 'W':
  299.                          snake.y[0] -= 1;
  300.                          break;
  301.                 case 'a':
  302.                 case 'A':
  303.                          snake.x[0] -= 2;
  304.                          break;
  305.                 case 's':
  306.                 case 'S':
  307.                          snake.y[0] += 1;
  308.                          break;
  309.                 case 'd':
  310.                 case 'D':
  311.                          snake.x[0] += 2;
  312.                          break;
  313.                 default: break;
  314.         }
  315.         
  316.         //4.判断是否发生碰撞,若产生碰撞会在main函数中直接跳出while(1),防止蛇“吃”墙
  317.         if (stateJudge() == 1)
  318.         {
  319.                 bumpFlag = 1;
  320.                 return;
  321.         }
  322.         
  323.         //5.未发生碰撞,打印蛇
  324.         gotoxy(snake.x[0], snake.y[0]);
  325.         printf("⊙");
  326.         for (int i = 1; i < snake.length; i++)
  327.         {
  328.                 gotoxy(snake.x[i], snake.y[i]);
  329.                 printf("●");
  330.         }
  331.         
  332.         //6.判断蛇头是否吃到食物        
  333.         if (snake.x[0] == food.x && snake.y[0] == food.y)
  334.         {
  335.                 snake.length += 1;
  336.                 changeSnake = 1;
  337.         }
  338.         else
  339.         {
  340.                 changeSnake = 0;
  341.         }
  342.         
  343.         //7.根据是否吃到食物判断蛇尾是保留还是用空格替代,已达到蛇身增长和蛇移动的效果
  344.         if (changeSnake == 0)
  345.         {
  346.                 gotoxy(blockx, blocky);
  347.                 printf("  ");
  348.         }
  349.         else
  350.         {
  351.                 snake.x[snake.length - 1] = blockx;
  352.                 snake.y[snake.length - 1] = blocky;
  353.                 score += 1;
  354.                 gotoxy(MAPWIDTH + 18, MAPHEIGHT / 2);
  355.                 printf("%03d", score);
  356.                 creatFood();
  357.         }
  358. }

  359. int main(void)
  360. {
  361.         initGame();
  362.         
  363.         while (1)
  364.         {
  365.                 //1.打印蛇
  366.                 printSnake();
  367.                
  368.                 //检测是否发生碰撞
  369.                 if (bumpFlag == 1)
  370.                 {
  371.                         break;
  372.                 }
  373.                
  374.                 //短暂停顿
  375.                 Sleep(snake.speed);
  376.         }
  377.         
  378.         gameOver();
  379.         getchar();
  380.         gotoxy(0, MAPHEIGHT +1);
  381.         return 0;
  382. }
复制代码


评分

参与人数 7荣誉 +28 鱼币 +28 贡献 +18 收起 理由
金装小学生 + 5 + 5 + 1 感谢楼主无私奉献!
liuzhengyuan + 1 很有意思
juhugufudu + 1 + 2 + 2 无条件支持楼主!
XYcarpenter + 5 + 5 + 3
白昼 + 5 + 5 + 3 无条件支持楼主!
不二如是 + 6 + 6 + 6 鱼C有你更精彩^_^
qiuyouzhi + 5 + 5 + 3 鼓励一下~~~

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-3-15 00:12:06 | 显示全部楼层
赞一个!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-15 06:32:04 | 显示全部楼层
厉害厉害!大佬
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-15 22:22:48 | 显示全部楼层
严重性        代码        说明        项目        文件        行        禁止显示状态
错误        LNK2019        无法解析的外部符号 _WinMain@16,该符号在函数 "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) 中被引用        Snake        c:\Users\tciswangbo\documents\visual studio 2015\Projects\Snake\Snake\MSVCRTD.lib(exe_winmain.obj)        1       
这个是什么问题啊
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-15 22:23:28 | 显示全部楼层
unsigned int unused = (srand((unsigned int)time(NULL)), 0);//撒随机种子 ,,这一行显示错误常量不能使用函数调用
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-16 08:44:22 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-16 08:45:01 | 显示全部楼层

你也可以的!加油
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-16 08:54:50 | 显示全部楼层
tciswangbo 发表于 2020-3-15 22:23
unsigned int unused = (srand((unsigned int)time(NULL)), 0);//撒随机种子 ,,这一行显示错误常量不能使 ...

那把这一行剪切到main函数的第一行,就行了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-16 08:55:49 | 显示全部楼层
tciswangbo 发表于 2020-3-15 22:22
严重性        代码        说明        项目        文件        行        禁止显示状态
错误        LNK2019        无法解析的外部符号 _WinMain@16,该符号在函 ...

这个问题我也不知道诶,我的电脑上DEVC++和Visual Studio都可以用诶
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-17 10:20:51 | 显示全部楼层
只能说天赋太强了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2020-3-17 13:56:19 | 显示全部楼层

靴靴夸奖,嘻嘻
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-17 14:04:10 | 显示全部楼层
jzzc 发表于 2020-3-17 13:56
靴靴夸奖,嘻嘻

楼主大几了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-17 14:10:54 | 显示全部楼层
代码值得参考。
不过
1这可不是学完第一季C能写的代码。
2没有编程功底不可能写出这样的代码。
3从你的回复中 了解到你用编译器DEVC++和Visual Studio;所以你有一定编程功底的。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-17 14:22:07 | 显示全部楼层

刚毕业,工作是嵌入式行业的研发岗,要用到c语言,但大学全荒废了,趁着春节跟着小甲鱼学了一遍C,学完想做点东西,就拿贪吃蛇练手了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-17 14:28:30 | 显示全部楼层
jzzc 发表于 2020-3-17 14:22
刚毕业,工作是嵌入式行业的研发岗,要用到c语言,但大学全荒废了,趁着春节跟着小甲鱼学了一遍C,学完想 ...

请问楼主考研了吗?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-17 14:33:31 | 显示全部楼层
ba21 发表于 2020-3-17 14:10
代码值得参考。
不过
1这可不是学完第一季C能写的代码。

1.如果说全靠自己想的话,学完第一季写出这个贪吃蛇确实难,但是如果有参考的话就会有醍醐灌顶的感觉,就比如说自己封装一个gotoxy(); 这个函数内容倒是简单,重要的是这个思想;还有这个程序里面甚至都没有用到指针,最最新的可能也就是结构体了,感觉语法确实不难啊
2.编程功底的话,自己还是太菜了,在一些单片机小项目里全是分支结构和for循环,可能逻辑能力会强一些,但是在这之前复杂的语法没有学过;
3.visual studio 是搜相关问题的时候看大佬们都在用,感觉界面很炫酷,就搜了一下是怎么下载的哈哈哈,(确实很炫酷)。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-17 14:34:22 | 显示全部楼层
最后的魁拔 发表于 2020-3-17 14:28
请问楼主考研了吗?

考研失利了,当时没有打算二战,就趁着春招找了个工作
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-17 15:10:09 | 显示全部楼层
狠人
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-3-17 15:14:07 | 显示全部楼层
看了楼主写的不错 程序跑下来也能运行,好好消化 谢谢分享
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-17 15:40:18 | 显示全部楼层
zgh15618511320 发表于 2020-3-17 15:14
看了楼主写的不错 程序跑下来也能运行,好好消化 谢谢分享

嗯嗯 哪里不懂的话可以在这里留言
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-7-3 09:06

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表