目录

  • 一、Ncurse图形库
  • 二、重要步骤
    • 1、准备工作
    • 2、打印输出游戏界面(地图、蛇身、食物)
    • 3、蛇的移动及食物生成
    • 4、游戏界面刷新
    • 5、扫描键入的值判断方向
    • 6、多个while并存(线程解决)
  • 三、注意事项及完整代码
  • 四、运行效果

一、Ncurse图形库

ncurses(new curses)是一个程序库,它提供了API,可以允许程序员编写独立于终端的基于文本的用户界面。它是一个虚拟终端中的“类GUI”应用软件工具箱。它还优化了屏幕刷新方法,以减少使用远程shell时遇到的延迟。

在ubuntu系统上安装库文件:

apt-get install libncurses5-dev

如何使用ncurse?

#include <curses.h>int main()
{initscr();//ncurse 界面的初始化函数printw("This is curses window\n");//在ncurse模式下的printfgetch();//等待用户输入,如果没有这句话,程序就退出了,看不到运行的结果,也就是看不到上面那句话endwin();//程序退出,调用改函数来恢复shell终端的显示,如果没有这句话,shell终端字乱码,坏掉return 0;
}

贪吃蛇项目中还会用到的一些ncurses库中的函数(代码如下):

#include<curses.h>  // Linux下图形界面库头文件
noecho();           // 不回显用户输入的内容
keypad();           // 允许用户终端的键盘,允许getch()函数获取功能键
move(x,y);          //move(int x, int y);移动光标到x,y
refresh();          //更新终端屏幕

为什么要用ncurse?

ncurse是终端下实现简单图形界面的不二选择,按键响应快速。

编译curses文件

gcc xxx.c -lcurses

ncurse的上下左右键

vi /usr/include/curses.h 可查看ncurse的宏定义

二、重要步骤

1、准备工作

准备一些初始化函数以及用到的全局变量。

创建蛇身节点结构体和结构体变量食物food,并且定义全局变量key,dir保存值:

//定义贪吃蛇节点结构体
struct Snake
{int hang;//行int lie;//列struct Snake *next;//下一个节点
};struct Snake *head = NULL;//定义蛇尾(链表头)
struct Snake *tail = NULL;//定义蛇头(链表尾)
int key;//记录键入的值 changeDirection()函数中使用到
int dir;//记录方向的值 addNode()函数中使用到struct Snake food;//定义结构体变量食物

封装一些ncurse界面的初始函数:

//函数封装初始化Ncurse界面
void initNcurse()
{initscr();//ncurse界面的初始化函数keypad(stdscr,TRUE);//从标准stdscr中接受功能键,TRUE代表是否接收noecho();//大多数的交互式应用程序在初始化时会调用noecho()函数,用于在进行控制操作时不显示输入的控制字符。
}

2、打印输出游戏界面(地图、蛇身、食物)

设计游戏界面样式,运用for循环嵌套打印输出游戏地图、蛇身以及食物。

先准备两个函数:
int judgeFood(int i,int j);//判断蛇头是否吃到食物
int judgeSnakeNode(int i,int j);//判断地图上的点是否是蛇身节点
如果传过来的i和j与行和列相等,则通过gamePic()打印输出。

//判断地图上的点是否有食物
int judgeFood(int i,int j)
{if(food.hang == i && food.lie == j){return 1;}return 0;
}//判断地图上的点是否是蛇身节点
int judgeSnakeNode(int i,int j)
{struct Snake *p;p = head;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}

游戏界面设计(20X20):
食物“##”;
蛇身“[]”;
地图第1行就是hang=0;

gamePic()打印输出游戏界面:

//打印输出游戏界面
void gamePic()
{int hang;int lie;move(0,0);//将光标定位到0行0列for(hang=0;hang<=19;hang++){if(hang == 0){printw("           Gluttonous snake\n");for(lie=0;lie<=20;lie++){printw("--");}printw("\n");        } if(hang >= 0 || hang <= 19){for(lie=0;lie<=21;lie++){if(lie == 0 || lie == 21){printw("|");}else if(judgeSnakeNode(hang,lie)){//判断蛇身节点,打印蛇身printw("[]");}else if(judgeFood(hang,lie)){//判断食物节点,打印食物printw("##");}else{printw("  ");}                 }printw("\n");}if(hang == 19){for(lie=0;lie<=20;lie++){printw("--");}printw("\n");printw("By Apibro,food.hang=%d,food.lie=%d\n",food.hang+1,food.lie);//打印作者及当前食物坐标(hang+1)}}
}

3、蛇的移动及食物生成

首先要初始化蛇身initSnake(),蛇需要方向,下面再阐述,蛇(蛇头就是链表尾tail)的移动通过moveSnake()来实现,移动过程中通过addNode()在蛇头增加一节点(尾插法),与此同时在蛇尾删除一节点deleteNode(),移动过程中如果吃到食物(judgeFood()判断是否吃到食物)则增加一节,增加节点通过addNode()来实现,食物的随机生成通过initFoot()来实现,移动过程中如果撞墙或则蛇身(judgeSnakeDeath()判断蛇是否死亡)则死亡,游戏重新开始。

蛇的移动:

//蛇的移动(蛇头增加新节点,删除蛇尾节点)
void moveSnake()
{addNode();//增加新节点if(judgeFood(tail->hang,tail->lie)){initFoot();//随机食物生成}else{deleteNode();//删除蛇尾(头结点)}if(judgeSnakeDeath()){initSnake();//死亡,重新初始化蛇身,游戏重新开始}
}

其余需要用到的函数:

//随机食物生成
void initFoot()
{int x = rand()%20+1;int y = rand()%20+1;x = x-1;//为了第0行显示成第1行food.hang = x;food.lie = y;
}//增加新节点(通过方向判断加在哪)
void addNode()
{struct Snake *new = (struct Snake*)malloc(sizeof(struct Snake));switch(dir){//根据键入的值来增加节点,修改tail所指结点中hang,lie并将修改后的值赋值给新节点 case UP:new->hang = tail->hang-1;new->lie = tail->lie;break;case DOWN:new->hang = tail->hang+1;new->lie = tail->lie;break;case LEFT:new->hang = tail->hang;new->lie = tail->lie-1;break;case RIGHT:new->hang = tail->hang;new->lie = tail->lie+1;break;}new->next = NULL;tail->next = new;tail = new;
}//初始化蛇身
void initSnake()
{struct Snake *p;dir = RIGHT;//蛇头初始方向为RIGHT while(head != NULL){//判断蛇是否为空,清理内存 p = head;head = head->next;free(p);}initFoot();//随机食物生成head = (struct Snake*)malloc(sizeof(struct Snake));head->hang = 1;head->lie = 2;head->next = NULL;tail = head;//初始时尾指针指向头addNode();addNode();addNode();//增加3节点,设置初始蛇身长度
}//删除蛇尾(头结点)
void deleteNode()
{struct Snake *p;p = head;head = head->next;free(p);//释放p=head原先头节点
}//判断蛇是否死亡
int judgeSnakeDeath()
{struct Snake *p;p = head;if(tail->hang < 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 21){//当tail节点中数据到达最大边界return 1return 1;}while(p->next != NULL){if(p->hang == tail->hang && p->lie == tail->lie){//当tail节点中数据与蛇身一致时return 1 return 1;}p = p->next;}return 0;
}

4、游戏界面刷新

游戏界面固定,按回车才会变,所以需要不停打印刷新,通过moveSnack()和gamePic()函数就实现游戏界面的刷新,使贪吃蛇动起来,但此时方向固定为初始方向。

封装刷新界面函数refreshInterface();如下:

//界面刷新
void* refreshInterface()
{   while(1){moveSnake();//移动gamePic();//打印输出refresh();//更新终端屏幕usleep(100000);//控制蛇的运动速度,以微秒为单位,100毫秒睡眠一次,执行挂起不动}
}

5、扫描键入的值判断方向

有了键盘输入,就可以改变蛇的初始方向,dir可以改变,蛇也就有了方向。

宏定义及封装函数如下:

#define UP    1
#define DOWN  -1
#define LEFT  2
#define RIGHT -2//通过绝对值判断相反方向不触发
void turn(int direction)
{if(abs(dir) != abs(direction)){dir = direction;}
}//扫描键入的值判断方向
void* changeDirection()
{while(1){key = getch();switch(key){case KEY_DOWN:turn(DOWN);break;case KEY_UP:turn(UP);break;case KEY_LEFT:turn(LEFT);break;case KEY_RIGHT:turn(RIGHT);break;}}
}

6、多个while并存(线程解决)

在贪吃蛇运动过程中,我们需要改变蛇的移动方向,这是就需要不停扫描键盘输入的值来判断方向,同时还需要不停的刷新界面,为了多个while循环并存这里需要引入linux线程。

线程的基本用法:
参考网页:Linux多线程相关的函数
在Linux中使用线程

#include <pthread.h>  // 头文件pthread_t:当前Linux中可理解为:typedef  unsigned long int  pthread_t;
如:pthread_t t1;  //多线程定义pthread_create(&t1,NULL,refreshInterface,NULL);
参数1:传出参数,保存系统为我们分配好的线程ID
参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。
参数4:线程主函数执行期间所使用的参数,如要传多个参数, 可以用结构封装。使用多线程的函数必须返回指针型,如void *refreshInterface()注:gcc xxx.c -lcurses -lpthead  //编译需要连接pthead库

三、注意事项及完整代码

注意事项:

在ubuntu低版本中运行不容易乱码,在高版本中gcc时,删去原先a.out多编译几次,并等待一段时间,也不会出现乱码。

完整代码:

#include <curses.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>#define UP    1
#define DOWN  -1
#define LEFT  2
#define RIGHT -2//定义贪吃蛇节点结构体
struct Snake
{int hang;//行int lie;//列struct Snake *next;//下一个节点
};struct Snake *head = NULL;//定义蛇尾(链表头)
struct Snake *tail = NULL;//定义蛇头(链表尾)
int key;//记录键入的值 changeDirection()函数中使用到
int dir;//记录方向的值 addNode()函数中使用到struct Snake food;//定义结构体变量食物 //函数封装初始化Ncurse界面
void initNcurse()
{initscr();//ncurse界面的初始化函数keypad(stdscr,TRUE);//从标准stdscr中接受功能键,TRUE代表是否接收noecho();//大多数的交互式应用程序在初始化时会调用noecho()函数,用于在进行控制操作时不显示输入的控制字符。
}//随机食物生成
void initFoot()
{int x = rand()%20+1;int y = rand()%20+1;x = x-1;//为了第0行显示成第1行food.hang = x;food.lie = y;
}//判断地图上的点是否有食物
int judgeFood(int i,int j)
{if(food.hang == i && food.lie == j){return 1;}return 0;
}//判断地图上的点是否是蛇身节点
int judgeSnakeNode(int i,int j)
{struct Snake *p;p = head;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}//打印输出游戏界面
void gamePic()
{int hang;int lie;move(0,0);//将光标定位到0行0列for(hang=0;hang<=19;hang++){if(hang == 0){printw("           Gluttonous snake\n");for(lie=0;lie<=20;lie++){printw("--");}printw("\n");        } if(hang >= 0 || hang <= 19){for(lie=0;lie<=21;lie++){if(lie == 0 || lie == 21){printw("|");}else if(judgeSnakeNode(hang,lie)){//判断蛇身节点,打印蛇身printw("[]");}else if(judgeFood(hang,lie)){//判断食物节点,打印食物printw("##");}else{printw("  ");}                 }printw("\n");}if(hang == 19){for(lie=0;lie<=20;lie++){printw("--");}printw("\n");printw("By Apibro,food.hang=%d,food.lie=%d\n",food.hang+1,food.lie);//打印作者及当前食物坐标}}
}//增加新节点(通过方向判断加在哪)
void addNode()
{struct Snake *new = (struct Snake*)malloc(sizeof(struct Snake));switch(dir){//根据键入的值来增加节点,修改tail所指结点中hang,lie并将修改后的值赋值给新节点 case UP:new->hang = tail->hang-1;new->lie = tail->lie;break;case DOWN:new->hang = tail->hang+1;new->lie = tail->lie;break;case LEFT:new->hang = tail->hang;new->lie = tail->lie-1;break;case RIGHT:new->hang = tail->hang;new->lie = tail->lie+1;break;}new->next = NULL;tail->next = new;tail = new;
}//初始化蛇身
void initSnake()
{struct Snake *p;dir = RIGHT;//蛇头初始方向为RIGHT while(head != NULL){//判断蛇是否为空,清理内存 p = head;head = head->next;free(p);}initFoot();//随机食物生成head = (struct Snake*)malloc(sizeof(struct Snake));head->hang = 1;head->lie = 2;head->next = NULL;tail = head;//初始时尾指针指向头addNode();addNode();addNode();//增加3节点,设置初始蛇身长度
}//删除蛇尾(头结点)
void deleteNode()
{struct Snake *p;p = head;head = head->next;free(p);//释放p=head原先头节点
}//判断蛇是否死亡
int judgeSnakeDeath()
{struct Snake *p;p = head;if(tail->hang < 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 21){//当tail节点中数据到达最大边界return 1return 1;}while(p->next != NULL){if(p->hang == tail->hang && p->lie == tail->lie){//当tail节点中数据与蛇身一致时return 1 return 1;}p = p->next;}return 0;
}//蛇的移动(蛇头增加新节点,删除蛇尾节点)
void moveSnake()
{addNode();//增加新节点if(judgeFood(tail->hang,tail->lie)){initFoot();//随机食物生成}else{deleteNode();//删除蛇尾(头结点)}if(judgeSnakeDeath()){initSnake();//死亡,重新初始化蛇身,游戏重新开始}
}//界面刷新
void* refreshInterface()
{   while(1){moveSnake();//移动gamePic();//打印输出refresh();//更新终端屏幕usleep(100000);//控制蛇的运动速度,以微秒为单位,100毫秒睡眠一次,执行挂起不动}
}//通过绝对值判断相反方向不触发
void turn(int direction)
{if(abs(dir) != abs(direction)){dir = direction;}
}//扫描键入的值判断方向
void* changeDirection()
{while(1){key = getch();switch(key){case KEY_DOWN:turn(DOWN);break;case KEY_UP:turn(UP);break;case KEY_LEFT:turn(LEFT);break;case KEY_RIGHT:turn(RIGHT);break;}}
}int main()
{pthread_t t1;pthread_t t2;initNcurse();initSnake();gamePic();pthread_create(&t1, NULL, refreshInterface, NULL);pthread_create(&t2, NULL, changeDirection, NULL);while(1);getch();endwin();return 0;
}

四、运行效果

运行效果链接:https://v.douyin.com/6ooTQgt/

最后谢谢阅读,笔者乃小白,如有错误之处还请指正。

【Linux】基于Ncurse图形库的贪吃蛇(C语言)相关推荐

  1. 基于Linux ncurses图形库的贪吃蛇小游戏

    达者为先  师者之意 基于Linux ncurses图形库的贪吃蛇小游戏 前言 1 ncurses库程序的标准模式 2 ncurses库的常用函数 3 基于Linux ncurses图形库的贪吃蛇小游 ...

  2. 基于51单片机的贪吃蛇小程序(8*8LED点阵实现)by_jy

    ** 基于51单片机的贪吃蛇小程序(8*8LED点阵实现)by_jy ** 一直很想写一个贪吃蛇的小程序,这两天终于抽空完成了,这里把我的思路分享给大家,仅供参考! 代码如下: 先放段主函数压压惊 v ...

  3. java毕业设计——基于java+J2ME的贪吃蛇游戏设计与实现(毕业论文+程序源码)——贪吃蛇游戏

    基于java+J2ME的贪吃蛇游戏设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+J2ME的贪吃蛇游戏设计与实现,文章末尾附有本毕业设计的论文和源码下载地址哦.需要下载开题报告P ...

  4. bfs算法 c语言,基于BFS算法的贪吃蛇(一)----基本架构

    先上成品图: 承接简单贪吃蛇C语言版高级版(一):基本框架搭建,上一篇文章是基于二维地图设计的基本框架,但是在后续的编写中发现在简单的模型下使用二维地图会让一个语句非常的长,耐读性不强.但是在复杂的情 ...

  5. C语言贪吃蛇游戏代码,贪吃蛇C语言代码实现大全

    一.C语言贪吃蛇代码实现前言 设计贪吃蛇游戏的主要目的是让大家夯实C语言基础,训练编程思维,培养解决问题的思路,领略多姿多彩的C语言. 贪吃蛇是非常经典的一款游戏,本次我们模拟在控制台实现贪吃蛇游戏, ...

  6. c语言课程设计贪吃蛇报告,贪吃蛇C语言课程设计报告.doc

    贪吃蛇C语言课程设计报告 贪吃蛇游戏设计 姓名: 张力 学号: 完成日期: 月 日 一.设计目的 贪吃蛇游戏程序功能的实现主要是建立在二维数组和图形界面的应用上,通过编写游戏程序能够熟悉掌握相关数组和 ...

  7. 贪吃蛇c语言 游戏中会出现障碍物,贪吃蛇C语言游戏代码.doc

    贪吃蛇C语言游戏代码 //开始编写游戏需要的类 #include //EasyX_2011惊蛰版(绘图库) #include #include #include #define UP1 #define ...

  8. 贪吃蛇_C语言_链表实现_SCAU课程设计

    个人大一下课程设计作品,本文中直接附上源代码(通过visual stdio 2019 实现  ): 课题:贪吃蛇(链表实现~) 语言:C/C++ 运行环境(软件):Visual Stdio 2019( ...

  9. linux环境下Ncurses实现贪吃蛇游戏

    游戏说明: linux环境下基于Ncurses图形库的C语言小游戏. Ncurses介绍: Ncurses(new curses)是一套编程库,它提供了一系列的函数以便使用者调用它们去生成基于文本的用 ...

最新文章

  1. java 解释型和编译型
  2. 怎么从github上下载一个vue项目在本地运行
  3. 背水一战 Windows 10 (10) - 资源: StaticResource, ThemeResource
  4. Python (2) 除法
  5. Python-sys.argv[]用法
  6. 使用Jenkins时,如果GIT_COMMIT无变化,跳过构建
  7. 各视频、各音频之间格式任意玩弄(图文详解)
  8. 2-6 基于SpringBoot的SpringSecurity环境快速搭建与验证
  9. 传感器为什么在低量程偏差大_传感器作业
  10. 小白都能理解的FTRL
  11. [2018.10.23 T1] 战争
  12. Sony如何启动微型计算机,索尼vaio笔记本怎么进bios设置|索尼笔记本进bios按什么键-...
  13. 全国各省份结婚离婚面板数据(2000-2019年)
  14. 响应式Web设计在跨终端广告创意中的应用
  15. 闲鱼双11全链路营销体系初体验
  16. 大学生计算机入学考试试题,大学生统考计算机一级考试试题题库.doc
  17. 【Unity】使用事件实现自动开关门
  18. object mapping for [details] tried to parse field [details] as object, but found concrete value
  19. Java POI SXSSFWorkbook 读取模板,输出
  20. topsis(优劣解距离法)在matlab上的实现

热门文章

  1. android融云消息提示,如何解决 嵌入融云SDK后, Android 手机收不到消息推送?
  2. 吊打面试官系列之:什么是 认证、鉴权、授权、权限控制,这一篇必须安排的明明白白。
  3. URL和URI 的区别
  4. uniapp vue nvue 模态框遮罩
  5. Ubuntu18.04使用docker安装jitsi-meet
  6. 基于Matlab——夫琅禾夫衍射以及菲涅尔衍射
  7. 根据ip限制访问网页
  8. java Swing实现图书管理系统
  9. 木马专杀软件测试工资,挂马测试全军覆没?3大安全软件横评实测
  10. Android 实验室