当前位置:   article > 正文

C语言——贪吃蛇小游戏_c语言制作贪吃蛇

c语言制作贪吃蛇

目录

一、ncurse

1.1 为什么需要用ncurse:

1.2 ncurse的输入输出:

1.2.1 如何使用ncurse:

1.2.2 编译ncurse的程序:

1.2.3 测试输入一个按键ncurse的响应速度:

1.3 ncurse上下左右键获取:

1.3.1 如何查看宏定义的.h文件:

1.3.2 ncurse上下左右键获取:

二、地图规划

2.1 地图规划算法显示第一行:

2.2 实现贪吃蛇完整地图:

2.3 优化贪吃蛇地图:

三、显示贪吃蛇身子

3.1 显示贪吃蛇身子的一个节点:

3.2 显示贪吃蛇完整身子:

3.3 显示贪吃蛇完整身子改进:

四、贪吃蛇移动

4.1 按下▶贪吃蛇向右移动:

4.2 贪吃蛇撞墙重新开始: 

4.3 贪吃蛇脱缰自由向右行走

五、Linux线程引入

5.1 贪吃蛇方向移动和刷新界面一起实现面临的问题:

5.2 线程的基本用法:

5.3 线程demo案例:

5.4 使用线程解决贪吃蛇方向移动和刷新界面一起实现面临的问题:

六、贪吃蛇跑起来

6.1 实现贪吃蛇四方向的风骚走位:

6.2 用绝对值方式来解决不合理的走位:

6.3 贪吃蛇吃饭了(食物的位置是随机的):

七、项目代码


  • 项目运行环境:Linux,基于Ncurse图形库的C语言小游戏

  • 项目的目的和意义:起到承上启下的作用,对于前面学的C语言的基础和数据结构链表做一个比较好的巩固,对于后面的Linux系统编程的开发做铺垫

  • 项目基础要求:C语言基础、Linux基本操作

    1. /*项目步骤*/
    2. 1)选择ncurses库的原因
    3. 在进行贪吃蛇游戏时,贪吃蛇的行进方向需要你按下上下左右键进行操控,如果使用C语言自带的函数,例如:scanf或者getchar之类的,需要你按下回车键,程序才能进行响应,而这显然是十分不方便的,但是ncurses库就很好的解决了这个问题。ncurses库自带的函数getch就能实现迅速便捷的贪吃蛇方向响应。
    4. 2)ncurses库的基本入门
    5. 对于该项目而言,ncurses库我们不需要进行过于深入的学习,只需要知道一些基本的函数使用即可。下列程序中的函数,就是一个基于ncurses库的基本代码框架。
    6. #include <curses.h>
    7. int main()
    8. {
    9. initscr();//ncurse界面的初始化函数
    10. printw("this is a curses window\n");//在ncurse模式下的printf
    11. getch();//等待用户的输入,如果没有这句话,程序就退出了,看不到运行的结果,也就是无法看到上面那句话
    12. endwin();//程序退出,恢复shell终端的显示,如果没有这句话,shell终端字乱码,坏掉
    13. return 0;
    14. }
    15. 3)贪吃蛇地图的整体规划
    16. 整个贪吃蛇地图的大小将它设置成一个20*20的近似正方形,使用"|"来表示左右边框,使用"--"来表示上下边框。
    17. 4)实现贪吃蛇第一个节点的显示
    18. 5)显示贪吃蛇的完整身子
    19. 注意,在这里,我们设置两个全局变量,struct Snake *head和struct Snake *tail,一个指向贪吃蛇的头,一个指向贪吃蛇的尾。在将第一个节点打印完后,将尾指向头,即:head = tail。每一次节点的添加,我们调用一个单独的函数去执行,并其使用尾插法实现。
    20. 6)实现贪吃蛇的右移
    21. 贪吃蛇的移动,整体来说就是链表节点的删除与添加。我们首先实现贪吃蛇的右移,每当按键按下时,贪吃蛇右移一格,即左侧的头结点删除head = head->next,右侧再次添加一个新的节点。新节点的坐标应该是行不变,列加一。注意:不要忘记清楚垃圾节点。
    22. 7)实现贪吃蛇的撞墙死亡
    23. 将贪吃蛇的尾节点坐标进行判断,判断其是否达到边界坐标。满足条件时,将贪吃蛇重新初始化。注意:不要忘记清楚垃圾节点。
    24. 8)实现贪吃蛇的自由行走
    25. 在这里,我们发现了一个问题,地图需要实时刷新进行贪吃蛇位置的变更,这是一个while(1)的死循环,而获取键值也是一个实时读取的操作,因此也是一个while(1)死循环,代码执行逻辑上出现了问题,所以我们引入了线程的概念。
    26. 9)了解什么是线程
    27. 10)用线程解决上述问题,实现贪吃蛇的分骚走位
    28. 开辟两个线程,一个用来执行地图刷新操作,一个用来获取键值。
    29. pthread_create(&t1,NULL,refreshScreen,NULL);
    30. pthread_create(&t2,NULL,changeDir,NULL);
    31. 11)解决贪吃蛇的不合理走位
    32. 在这里,我们使用绝对值法来解决问题,abs函数的作用便是取绝对值,我们将上下左右键,两两对应,宏定义为1-1,2-2之类的数就能成功解决问题。
    33. 12)实现贪吃蛇食物的打印
    34. 13)实现食物的随机出现
    35. 取随机数,C语言有一个自带的函数可以解决这个问题,rand()函数可以实现随机取数,我们只要再对它进行取余操作,便可以防止食物出现在地图以外的位置。
    36. 14)实现贪吃蛇咬到自己结束游戏,重新开始的操作
    37. 当贪吃蛇的尾节点与自身除尾巴节点以外的其他节点进行比较后,若行列数相同,则初始化整个贪吃蛇,注意:不要忘记垃圾节点的清除(我们可以在每次贪吃蛇初始化之前进行这个操作)。

    一、ncurse

    1.1 为什么需要用ncurse:

  • 因为的按键响应牛逼哄哄

  • 1.2 ncurse的输入输出

  • ncurse用的最多的地方是在Linux内核编译之前的内核配置

  • 1.2.1 如何使用ncurse:

1.2.2 编译ncurse的程序:

1.2.3 测试输入一个按键ncurse的响应速度:
  1. 1 #include <curses.h>
  2. 2
  3. 3 int main()
  4. 4 {
  5. 5 char c;
  6. 6
  7. 7 initscr();
  8. 8 c = getch();
  9. 9 printw("you Input :%c\n",c);
  10. 10 getch();
  11. 11 endwin();
  12. 12 return 0;
  13. 13 }
  • 使用ncurse的好处是:按下一个按键不需要按下回车,直接就可以输出c的值,和我们C语言的其他输入函数好用

1.3 ncurse上下左右键获取:

1.3.1 如何查看宏定义的.h文件:

vi /usr/include/curses.h    //查看宏定义.h文件的指令
:q                            //退出查看

1.3.2 ncurse上下左右键获取:

  1. 1 #include <curses.h>
  2. 2
  3. 3 int main()
  4. 4 {
  5. 5 int key;
  6. 6
  7. 7 initscr();
  8. 8 keypad(stdscr,1); //这个函数允许使用功能键,例如:F1、F2、方向键等功能键。几乎所有的交互式程序都需要使用功能 键,因为绝大多数用户界面主要用方向键进行操作。使用keypad(stdscr,TURE)就为“标准屏幕”(stdscr)激活了功能键。
  9. 9
  10. 10 while(1){
  11. 11 key = getch();
  12. 12 switch(key){
  13. 13 case KEY_DOWN:
  14. 14 printw("DOWN\n");
  15. 15 break;
  16. 16 case KEY_UP:
  17. 17 printw("up\n");
  18. 18 break;
  19. 19 case KEY_LEFT:
  20. 20 printw("LEFT\n");
  21. 21 break;
  22. 22 case KEY_RIGHT:
  23. 23 printw("RIGHT\n");
  24. 24 break;
  25. 25 }
  26. 26
  27. 27
  28. 28 }
  29. 29 endwin();
  30. 30 return 0;
  31. 31 }

  • 我们按下上下左右▲▼◀▶之后,可以获取到上下左右的打印信息

二、地图规划

2.1 地图规划算法显示第一行:

  1. #include <curses.h>
  2. void initNcurse()
  3. {
  4. initscr();
  5. keypad(stdscr,1);
  6. }
  7. void gamPic()
  8. {
  9. int hang;
  10. int lie;
  11. for(hang=0; hang<20; hang++){
  12. if(hang == 0){
  13. for(lie=0; lie<20; lie++){
  14. printw("--");
  15. }
  16. printw("\n");
  17. for(lie=0; lie<=20; lie++){
  18. if(lie == 0 || lie == 20){
  19. printw("|");
  20. }else{
  21. printw(" ");
  22. }
  23. }
  24. }
  25. }
  26. }
  27. int main()
  28. {
  29. initNcurse(); //初始化Ncurse
  30. gamPic(); //地图规划显示第一行
  31. getch();
  32. endwin();
  33. return 0;
  34. }

2.2 实现贪吃蛇完整地图:

  1. #include <curses.h>
  2. void initNcurse()
  3. {
  4. initscr();
  5. keypad(stdscr,1);
  6. }
  7. void gamPic()
  8. {
  9. int hang;
  10. int lie;
  11. for(hang=0; hang<20; hang++){
  12. if(hang == 0){
  13. for(lie=0; lie<20; lie++){
  14. printw("--");
  15. }
  16. printw("\n");
  17. for(lie=0; lie<=20; lie++){
  18. if(lie == 0 || lie == 20){
  19. printw("|");
  20. }else{
  21. printw(" ");
  22. }
  23. }
  24. printw("\n");
  25. }
  26. if(hang>0 && hang<=19) {
  27. for(lie=0; lie<=20; lie++){
  28. if(lie == 0 || lie == 20){
  29. printw("|");
  30. }else{
  31. printw(" ");
  32. }
  33. }
  34. printw("\n");
  35. }
  36. if(hang == 19){
  37. for(lie=0; lie<20; lie++){
  38. printw("--");
  39. }
  40. printw("\n");
  41. printw("By ShiYaHao!\n");
  42. }
  43. }
  44. }
  45. int main()
  46. {
  47. initNcurse(); //初始化Ncurse
  48. gamPic(); //实现贪吃蛇地图
  49. getch();
  50. endwin();
  51. return 0;
  52. }

2.3 优化贪吃蛇地图:

  1. #include <curses.h>
  2. void initNcurse()
  3. {
  4. initscr();
  5. keypad(stdscr,1);
  6. }
  7. void gamPic()
  8. {
  9. int hang;
  10. int lie;
  11. for(hang=0; hang<20; hang++){
  12. if(hang == 0){ //第0行打“--”
  13. for(lie=0; lie<20; lie++){
  14. printw("--");
  15. }
  16. printw("\n");
  17. }
  18. if(hang>=0 && hang<=19) { //第0行-19行的第0列和第20列打“|”
  19. for(lie=0; lie<=20; lie++){
  20. if(lie == 0 || lie == 20){
  21. printw("|");
  22. }else{
  23. printw(" ");
  24. }
  25. }
  26. printw("\n");
  27. }
  28. if(hang == 19){ //第19行打“--”
  29. for(lie=0; lie<20; lie++){
  30. printw("--");
  31. }
  32. printw("\n");
  33. printw("By ShiYaHao!\n"); //作者
  34. }
  35. }
  36. }
  37. int main()
  38. {
  39. initNcurse();
  40. gamPic();
  41. getch();
  42. endwin();
  43. return 0;
  44. }
  45. //实现的贪吃蛇地图和上面一样,只不过是优化了一下代码

三、显示贪吃蛇身子

3.1 显示贪吃蛇身子的一个节点:

  1. #include <curses.h>
  2. struct Snake
  3. {
  4. int hang;
  5. int lie;
  6. struct Snake *next;
  7. };
  8. struct Snake node1 = {2,2,NULL};
  9. void initNcurse()
  10. {
  11. initscr();
  12. keypad(stdscr,1);
  13. }
  14. void gamPic()
  15. {
  16. int hang;
  17. int lie;
  18. for(hang=0; hang<20; hang++){
  19. if(hang == 0){
  20. for(lie=0; lie<20; lie++){
  21. printw("--");
  22. }
  23. printw("\n");
  24. }
  25. if(hang>=0 && hang<=19) {
  26. for(lie=0; lie<=20; lie++){
  27. if(lie == 0 || lie == 20){
  28. printw("|");
  29. }else if(node1.hang == hang && node1.lie == lie){
  30. printw("[]");
  31. }else{
  32. printw(" ");
  33. }
  34. }
  35. printw("\n");
  36. }
  37. if(hang == 19){
  38. for(lie=0; lie<20; lie++){
  39. printw("--");
  40. }
  41. printw("\n");
  42. printw("By ShiYaHao!\n");
  43. }
  44. }
  45. }
  46. int main()
  47. {
  48. initNcurse();
  49. gamPic();
  50. getch();
  51. endwin();
  52. return 0;
  53. }

3.2 显示贪吃蛇完整身子:

  1. #include <curses.h>
  2. struct Snake
  3. {
  4. int hang;
  5. int lie;
  6. struct Snake *next;
  7. };
  8. struct Snake node1 = {2,2,NULL};
  9. struct Snake node2 = {2,3,NULL};
  10. struct Snake node3 = {2,4,NULL};
  11. struct Snake node4 = {2,5,NULL};
  12. void initNcurse()
  13. {
  14. initscr();
  15. keypad(stdscr,1);
  16. }
  17. int hasSnakeNode(int i, int j)
  18. {
  19. struct Snake *p = &node1;
  20. while(p != NULL){
  21. if(p->hang == i && p->lie == j){
  22. return 1;
  23. }
  24. p = p->next;
  25. }
  26. return 0;
  27. }
  28. void gamPic()
  29. {
  30. int hang;
  31. int lie;
  32. for(hang=0; hang<20; hang++){
  33. if(hang == 0){
  34. for(lie=0; lie<20; lie++){
  35. printw("--");
  36. }
  37. printw("\n");
  38. }
  39. if(hang>=0 && hang<=19) {
  40. for(lie=0; lie<=20; lie++){
  41. if(lie == 0 || lie == 20){
  42. printw("|");
  43. }else if(hasSnakeNode(hang,lie)){
  44. printw("[]");
  45. }else{
  46. printw(" ");
  47. }
  48. }
  49. printw("\n");
  50. }
  51. if(hang == 19){
  52. for(lie=0; lie<20; lie++){
  53. printw("--");
  54. }
  55. printw("\n");
  56. printw("By ShiYaHao!\n");
  57. }
  58. }
  59. }
  60. int main()
  61. {
  62. initNcurse();
  63. node1.next = &node2;
  64. node2.next = &node3;
  65. node3.next = &node4;
  66. gamPic();
  67. getch();
  68. endwin();
  69. return 0;
  70. }

3.3 显示贪吃蛇完整身子改进:

  1. #include <curses.h>
  2. #include <stdlib.h>
  3. struct Snake
  4. {
  5. int hang;
  6. int lie;
  7. struct Snake *next;
  8. };
  9. struct Snake *head = NULL; //指向链表头
  10. struct Snake *tail = NULL; //指向链表尾
  11. void initNcurse()
  12. {
  13. initscr();
  14. keypad(stdscr,1);
  15. }
  16. int hasSnakeNode(int i, int j)
  17. {
  18. struct Snake *p = head;
  19. while(p != NULL){
  20. if(p->hang == i && p->lie == j){
  21. return 1;
  22. }
  23. p = p->next;
  24. }
  25. return 0;
  26. }
  27. void gamPic() //地图规划
  28. {
  29. int hang;
  30. int lie;
  31. for(hang=0; hang<20; hang++){
  32. if(hang == 0){
  33. for(lie=0; lie<20; lie++){
  34. printw("--");
  35. }
  36. printw("\n");
  37. }
  38. if(hang>=0 && hang<=19) {
  39. for(lie=0; lie<=20; lie++){
  40. if(lie == 0 || lie == 20){
  41. printw("|");
  42. }else if(hasSnakeNode(hang,lie)){
  43. printw("[]");
  44. }else{
  45. printw(" ");
  46. }
  47. }
  48. printw("\n");
  49. }
  50. if(hang == 19){
  51. for(lie=0; lie<20; lie++){
  52. printw("--");
  53. }
  54. printw("\n");
  55. printw("By ShiYaHao!\n");
  56. }
  57. }
  58. }
  59. void addNode()
  60. {
  61. struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake)); //创建新节点
  62. if(new == NULL){
  63. printw("malloc error\n");
  64. }
  65. new->hang = tail->hang; //新节点的行等于链表尾的行
  66. new->lie = tail->lie+1; //新节点的行等于链表尾的列+1
  67. new->next = NULL;
  68. tail->next = new; //从链表尾部插入新节点
  69. tail = new; //新节点当作尾部
  70. }
  71. void initSnake()
  72. {
  73. head = (struct Snake *)malloc(sizeof(struct Snake)); //创建链表头
  74. if(head == NULL){
  75. printw("malloc error\n");
  76. }
  77. head->hang = 2;
  78. head->lie = 2;
  79. head->next = NULL;
  80. tail = head; //第一个节点链表头和链表尾是一样的
  81. addNode(); //调用一次代表增加一个节点
  82. addNode();
  83. addNode();
  84. }
  85. int main()
  86. {
  87. initNcurse();
  88. initSnake();
  89. gamPic();
  90. getch();
  91. endwin();
  92. return 0;
  93. }

四、贪吃蛇移动

4.1 按下▶贪吃蛇向右移动:

  1. #include <curses.h>
  2. #include <stdlib.h>
  3. struct Snake
  4. {
  5. int hang;
  6. int lie;
  7. struct Snake *next;
  8. };
  9. struct Snake *head = NULL;
  10. struct Snake *tail = NULL;
  11. void initNcurse()
  12. {
  13. initscr();
  14. keypad(stdscr,1);
  15. }
  16. int hasSnakeNode(int i, int j)
  17. {
  18. struct Snake *p = head;
  19. while(p != NULL){
  20. if(p->hang == i && p->lie == j){
  21. return 1;
  22. }
  23. p = p->next;
  24. }
  25. return 0;
  26. }
  27. void gamPic()
  28. {
  29. int hang;
  30. int lie;
  31. move(0,0);
  32. for(hang=0; hang<20; hang++){
  33. if(hang == 0){
  34. for(lie=0; lie<20; lie++){
  35. printw("--");
  36. }
  37. printw("\n");
  38. }
  39. if(hang>=0 && hang<=19) {
  40. for(lie=0; lie<=20; lie++){
  41. if(lie == 0 || lie == 20){
  42. printw("|");
  43. }else if(hasSnakeNode(hang,lie)){
  44. printw("[]");
  45. }else{
  46. printw(" ");
  47. }
  48. }
  49. printw("\n");
  50. }
  51. if(hang == 19){
  52. for(lie=0; lie<20; lie++){
  53. printw("--");
  54. }
  55. printw("\n");
  56. printw("By ShiYaHao!\n");
  57. }
  58. }
  59. }
  60. void addNode()
  61. {
  62. struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));
  63. new->hang = tail->hang;
  64. new->lie = tail->lie+1;
  65. new->next = NULL;
  66. tail->next = new;
  67. tail = new;
  68. }
  69. void initSnake()
  70. {
  71. head = (struct Snake *)malloc(sizeof(struct Snake));
  72. if(head == NULL){
  73. printw("malloc error\n");
  74. }
  75. head->hang = 2;
  76. head->lie = 2;
  77. head->next = NULL;
  78. tail = head;
  79. addNode();
  80. addNode();
  81. addNode();
  82. addNode();
  83. }
  84. void deletNode()
  85. {
  86. struct Snake *p;
  87. p = head;
  88. head = head->next;
  89. free(p);
  90. }
  91. void moveSnake()
  92. {
  93. addNode(); //增加一个节点
  94. deletNode(); //删除头节点
  95. }
  96. int main()
  97. {
  98. int con;
  99. initNcurse();
  100. initSnake();
  101. gamPic();
  102. while(1){
  103. con = getch(); //con获取键值
  104. if(con == KEY_RIGHT){ //如果是右键
  105. moveSnake(); //向右移动
  106. gamPic(); //必须刷新一下界面,否则看不到
    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/463454
    推荐阅读
    相关标签