游戏说明: linux环境下基于Ncurses图形库的C语言小游戏。

Ncurses介绍:
Ncurses(new curses)是一套编程库,它提供了一系列的函数以便使用者调用它们去生成基于文本的用户界面。 Ncurses是一个能提供功能键定义(快捷键),屏幕绘制以及基于文本终端的图形互动功能的动态库。Ncurses用得最多的地方是linux内核编译之前的内核配置,Ncurses早已淡出舞台,甚至体验感完爆Ncurses的C图形库GTK、C++图形库QT也区趋于落伍嵌入式设备上的Android 系统。这个游戏只是使用Ncurses并不以学习它为目的,主要还是通过这个游戏锻炼我们C语言的能力。

准备工作:
在ubuntu上面安装Ncurses库,输入以下指令:

sudo apt-get install libncurses5-dev

来编写第一个程序看看是否能成功运行:

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

运行上面那个程序时需要用到-lcurses:

gcc ncurses.c -lcurses -o ncurses  //因为需要用到curses库所以要链入curses

以上代码输出界面是:

Ncurses的上下左右键值:

#define KEY_DOWN 0402
#define KEY_UP   0403
#define KEY_LEFT 0404
#define KEY_RIGHT 0405
//这些是Ncurses的一些宏定义,表示上下左右键,这些数都是八进制的
/*
这些内容可以通过命令查看curses.h的文件可知
命令:cd /usr/include/curses.h
*/

代码如下:

#include<curses.h>
int main()
{initscr();//ncurses界面初始化函数printw("This is a ncurses window.\n");//在ncurses模式下的printfkeypad(stdscr,1);//这个函数是一个函数,第一个参数是从stdscr中接受功能建,第二个是参数表示是否接收,1表示接收while(1){//char c=getch();//等待用户输入,如果没有这句话,程序就退出了,看不到运行结果int key=getch();//这里要把char类型改为int类型,因为char类型最大表示是128,而这些方向键的值(0404等)要>128,所以用intprintw("you input :%d\n",key);}endwin();//程序退出,调用该函数来恢复shell终端的显示,如果没有这句话,shell终端字乱码坏掉return 0;
}代码运行结果如下:
This is a ncurses window.
you input :259
you input :258
you input :260
you input :261
按下上下左右键,分别输出259、258、260、261
输出的这些数字都是十进制的,分别对应宏定义中的:0403、0402、0404、0405

代码可以进一步优化:

#include<curses.h>
int main()
{initscr();//ncurses界面初始化函数printw("This is a ncurses window.\n");//在ncurses模式下的printfkeypad(stdscr,1);//这个函数是一个函数,第一个参数是从stdscr中接受功能建,第二个是参数表示是否接收,1表示接收while(1){//char c=getch();//等待用户输入,如果没有这句话,程序就退出了,看不到运行结果int key=getch();//这里要把char类型改为int类型,因为char类型最大表示是128,而这些方向键的值(0404等)要>128,所以用intswitch(key){case 0402:printw("DOWN");break;case 0402:printw("DOWN");break;case 0402:case 0402:printw("DOWN");break;printw("DOWN");break;}}endwin();//程序退出,调用该函数来恢复shell终端的显示,如果没有这句话,shell终端字乱码坏掉return 0;
}
当然程序中的代表方向的数字也可以用宏表示。
输入上下左右键,程序运行结果如下:
This is a ncurses window.
UP
DOWN
LEFT
RIGHT

地图规划:

两个- 和一个 | 组成一个方格,下图是20×20的一个地图。
地图代码:

#include<curses.h>void initNcurses()
{initscr();keypad(stdscr,1);
}void map()
{int hang;int lie;for(hang=0;hang<20;hang++){if(hang==0){for(lie=0;lie<20;lie++){printw("--");}printw("\n");printw("");}if(hang>=0&&hang<=19){for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|");}else{printw("  ");}}printw("\n");}if(hang==19){for(lie=0;lie<20;lie++){printw("--");}printw("\n");printw("");}}printw("By Feng Hui Nan.");
}
int main()
{initNcurses();map();getch();endwin();//没有这行会破坏shell终端return 0;
}

贪吃蛇身子节点构成:

struct Snake
{int hang;int lie;struct Snake*next;
};

实现蛇身子的全部显示,并向右移动:

#include<curses.h>
#include <stdlib.h>
#include <unistd.h>
void initNcurses()
{initscr();keypad(stdscr,1);
}struct Snake //贪吃蛇身子的一个结点
{int hang;int lie;struct Snake*next;
};
struct Snake* head=NULL;//将蛇的头节点定义为全局变量
struct Snake* tail=NULL;//将蛇的尾节点定义为全局变量,防止错误过多
int hasSnakeNode(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 map()
{int hang;int lie;move(0,0);//在每次调用地图的时候用move函数将光标移动到地图的第一个方格的位置for(hang=0;hang<20;hang++){if(hang==0){for(lie=0;lie<20;lie++){printw("--");}printw("\n");printw("");}if(hang>=0&&hang<=19){for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|");}else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw("  ");}}printw("\n");}if(hang==19){for(lie=0;lie<20;lie++){printw("--");}printw("\n");}}printw("By Feng Hui Nan.");
}
void addSnake()
{struct Snake* new=(struct Snake*)malloc(sizeof(struct Snake));new->hang=tail->hang;new->lie=tail->lie+1;new->next=NULL;tail->next=new;tail=new;
}
void initSnake()
{struct Snake*p;while(head!=NULL){p=head;head=head->next;//将链表的每一个节点都释放掉,指针后移free(p);}//添加这个循环的目的是在一次游戏结束后释放当局游戏创建的链表,避免内存泄露head=(struct Snake*)malloc(sizeof(struct Snake));head->hang=1;head->lie=1;head->next=NULL;tail=head;addSnake();addSnake();addSnake();
}
void deleSnake()
{struct Snake*p;p=head;head=head->next;free(p);
}
void moveSnake()
{addSnake();//在蛇的尾部添加节点并且删除节点后然后判断尾节点的行和列是否达到边界deleSnake();if(tail->hang==0 || tail->lie==20 || tail->lie==0 || tail->hang==20){initSnake();}
}
int main()
{initNcurses();initSnake();map();while(1){moveSnake();map();//删除后再次刷新地图显示移动后的界面refresh();//刷新界面函数//sleep(1);//每隔一秒蛇移动一下,并刷新界面,这行控制蛇的移动速率usleep(100000);//sleep以秒为单位有点慢,usleep以微秒为单位此处控制蛇的移动速度}getch();endwin();//没有这行会破坏shell终端return 0;
}

效果图:
遇到问题:
如何在响应方向键的同时,控制蛇的移动?好像需要两个while循环同时执行。我想到一个方法,就是用switch判断输入的方向键,然后再封装向上下左右移动的函数,在switch中进行调用,仔细思考以下发现不可行,因为在这个游戏中地图是每隔100毫秒刷新一次要不断的刷新,所以不可行。

问题解决:
用linux线程即可解决该问题,我们将一个程序里的执行路线叫做线程(thread)。更准确的定义是:线程是一个进程内部的控制序列。

线程demo:

#include<stdio.h>
#include<pthread.h>void*thread(void *arg)
{printf("this is a thread and arg=%d.\n",*(int*)arg);*(int*)arg=0;return arg;
}
int main(int argc,char *argv[])
{pthread_t th;int ret;int arg=10;int *thread_ret=NULL;ret=pthread_create(&th,NULL,thread,&arg);//pthread_create这个函数是线程的创建函数//第一个参数th是线程的描述符,就是上边定义的pthread_t th中的th,就相当于给第三那个参数的一个ID吧//第二个参数一般就写NULL//第三个参数是做好的准备被调用的函数,最后一个参数是函数的参数if(ret!=0){printf("Creat thread error!\n");return -1;}printf("this is the mian process.\n");pthread_join(th,(void**)&thread_ret);//int pthread_join(pthread_t thread, void **value_ptr);//thread:等待退出线程的线程号。//value_ptr:退出线程的返回值。return 0;
}

自己实现线程的创建:

#include<stdio.h>
#include<pthread.h>
#include <unistd.h>
void* fun1()
{while(1){printf("this is func1.\n");sleep(1);}
}
void* fun2()
{while(1){printf("this is fun2.\n");sleep(1);}
}
int main()
{pthread_t th1;pthread_t th2;int ret1;int ret2;int *pthread_th1=NULL;int *pthread_th2=NULL;ret1=pthread_create(&th1,NULL,fun1,NULL);ret2=pthread_create(&th2,NULL,fun2,NULL);if(ret1!=0 || ret2!=0){printf("thread creat error.\n");perror("error");return -1;}
//      while(1);//在主线程等待其他线程运行完后再退出pthread_join(th1,(void **)&pthread_th1);pthread_join(th2,(void **)&pthread_th2);//这种方法也可以使主线程等待指定线程退出后才退出//th2是线程的ID//int pthread_join(pthread_t thread, void **value_ptr);//thread:等待退出线程的线程号。//value_ptr:退出线程的返回值。return 0;}

程序总体代码:

#include<curses.h>
#include <stdlib.h>
#include <unistd.h>
#include<pthread.h>#define UP     1
#define DOWN  -1
#define RIGHT  2
#define LEFT  -2struct Snake //贪吃蛇身子的一个结点
{int hang;int lie;struct Snake*next;
};struct Snake food;
void initFood()
{int x=rand()%20;int y=rand()%20;//%20是为了保证使rand出现的随机数尽量的出现在地图范围内food.hang=x;food.lie=y;
}
void initNcurses()
{initscr();keypad(stdscr,1);noecho();//不要把无关的东西打印在界面上
}struct Snake* head=NULL;//将蛇的头节点定义为全局变量
struct Snake* tail=NULL;//将蛇的尾节点定义为全局变量,防止错误过多
int key;
int dir;int hasFood(int i,int j)
{if(food.hang==i && food.lie==j){return 1;}return 0;
}
int hasSnakeNode(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 map()
{int hang;int lie;move(0,0);//在每次调用地图的时候用move函数将光标移动到地图的第一个方格的位置for(hang=0;hang<20;hang++){if(hang==0){for(lie=0;lie<20;lie++){printw("--");}printw("\n");printw("");}if(hang>=0&&hang<=19){for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|");}else if(hasSnakeNode(hang,lie)){printw("[]");}else if(hasFood(hang,lie)){printw("**");}else{printw("  ");}}printw("\n");}if(hang==19){for(lie=0;lie<20;lie++){printw("--");}printw("\n");}}printw("By Feng Hui Nan.");
}
void addSnake()
{struct Snake* new=(struct Snake*)malloc(sizeof(struct Snake));new->next=NULL;switch(dir){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;}tail->next=new;tail=new;
}
void initSnake()//蛇的初始化函数
{struct Snake*p;dir=RIGHT;while(head!=NULL){p=head;head=head->next;//将链表的每一个节点都释放掉,指针后移free(p);}//添加这个循环的目的是在一次游戏结束后释放当局游戏创建的链表,避免内存泄露initFood();head=(struct Snake*)malloc(sizeof(struct Snake));head->hang=1;head->lie=1;head->next=NULL;tail=head;addSnake();addSnake();addSnake();
}
void deleSnake()
{struct Snake*p;p=head;head=head->next;free(p);
}int ifSnakeDie()
{struct Snake*p;p=head;if(tail->hang<0 || tail->lie==20 || tail->lie==0 || tail->hang==20){return 1;}while(p->next!=NULL){if(p->hang==tail->hang && p->lie==tail->lie){return 1;}p=p->next;}return 0;}
void* moveSnake()
{addSnake();//在蛇的尾部添加节点并且删除节点后然后判断尾节点的行和列是否达到边界if(hasFood(tail->hang,tail->lie)){initFood();}else{deleSnake();}if(ifSnakeDie()){initSnake();}
}
void* refreshUi()//封装的界面刷新函数
{while(1){moveSnake();map();//删除后再次刷新地图显示移动后的界面refresh();//刷新界面函数//sleep(1);//每隔一秒蛇移动一下,并刷新界面,这行控制蛇的移动速率usleep(150000);//sleep以秒为单位有点慢,usleep以微秒为单位此处控制蛇的移动速度}
}
void turn(int direction)//这个函数的作用是当蛇在竖直方向上运动时使上下键无效//在水平方向运动时,左右方向键无效
{if(abs(dir)!=abs(direction)){dir=direction;}
}
void* changeDir()
{while(1){key=getch();switch(key){case KEY_DOWN:turn(DOWN);break;case KEY_UP:turn(UP);break;case KEY_RIGHT:turn(RIGHT);break;case KEY_LEFT:turn(LEFT);break;}}}
int main()
{initNcurses();initSnake();map();pthread_t t1;pthread_t t2;int *pthread1=NULL;int *pthread2=NULL;pthread_create(&t1,NULL,changeDir,NULL);pthread_create(&t2,NULL,refreshUi,NULL);
//      pthread_join(t1,(void**)&pthread1);
//      pthread_join(t2,(void**)&pthread2);while(1);getch();endwin();//没有这行会破坏shell终端return 0;
}

编译的时时候记得要加-lpthread、-lcurses

linux环境下Ncurses实现贪吃蛇游戏相关推荐

  1. VC环境下简单的贪吃蛇

    下午真无聊的不知道干什么了,翻翻自己以前写的代码,偶然看到大一时写的这个简单的贪吃蛇,自己玩了几把后还是决定发到博客吧.(实在无聊,打发时间) 注:完全基于VC++6.0环境,不支持Linux 下te ...

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

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

  3. linux贪吃蛇c程序,Linux环境下C语言实现贪吃蛇游戏

    Linux环境下C语言实现贪吃蛇游戏 [liul@test snake]$ more snake.c #include #include #include #include #include #def ...

  4. 详解Linux终端下编写“贪吃蛇”游戏

    大一学习C语言的时候就想要用Turbo C编写一个视频小游戏出来,种种原因后面搁浅了,现在借着学习Linux系统编程的劲头,编写了一个终端下可以运行的贪吃蛇游戏,其中此视频游戏用到的一些知识和操作系统 ...

  5. Linux下编写贪吃蛇游戏

    Linux下编写贪吃蛇游戏 文章目录 Linux下编写贪吃蛇游戏 前言 一.贪吃蛇代码 二.运行贪吃蛇代码 前言 本程序需要ncurses库,ubuntu下安装ncurses可以执行下面命令: sud ...

  6. C语言实现贪吃蛇游戏(Linux下实现)

    文章目录 一.ncurse图形库 二.思路步骤 1.准备工作 2.考虑如何输出图形界面 3.考虑蛇是如何移动的 4.实现页面刷新 5.判断键盘输入 6.Liunx线程解决键入值和界面刷新并行问题 三. ...

  7. Linux下基于SDL库贪吃蛇游戏

    Linux下基于SDL库贪吃蛇游戏   SDL(Simple DirectMediaLayer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成.SDL提供了数种控制图像.声音.输出入的函数,让开 ...

  8. Linux 贪吃蛇游戏 -C语言(本人的第一篇博文)

    一.Ncurses库简介: 1.curses是一个在Linux/Unix下广泛应用的图形函数库, 作用是可以在终端内绘制简单的图形用户界面. 2.Nurses库头文件:#include <cur ...

  9. 结对-贪吃蛇游戏-开发环境搭建过程

    结对编程成员:赵建辉,马壮 搭建环境: 会 html,css,以及java开发知识. 会应用sublime,dw等编辑软件 编写程序阶段: 1.利用html搭建前端页面,构建游戏的页面框架 2.利用j ...

最新文章

  1. 拉开你和同龄人差距的,不是基因,不是努力,而是……
  2. 超级详细的解决方法 (CentOS7) :永久修改 mysql read-only 问题 could not retrieve transation read-only status server
  3. python中文件的读取与写入以及os模块
  4. Android插件化原理解析——Hook机制之动态代理
  5. 性能测试—前端性能1
  6. htc g7 android 4.4,HTC G7刷机,从WM手机刷到了安卓,开启了新的刷机体验....
  7. UNIX(进程间通信):03---僵尸进程
  8. 太惨了,今年没年终奖了!
  9. Excel 关于新建xls文件 新建sheet 合并sheet的VBA操作代码
  10. use texstudio with chinese on ubuntu
  11. Python设计模式:责任链模式
  12. python输入一个三位数输出百位十位个位_编程实现:输入一个三位数,输出其百位、十位、个位上的数字。_学小易找答案...
  13. pwm波控制舵机原理(转)
  14. java poi word 复制_java poi实现word导出(包括word模板的使用、复制表格、复制行、插入图片的使用)...
  15. 普渡大学电子和计算机工程专业,2020年普渡大学热门专业
  16. 兼容android 6.0以上获取设备编号等权限
  17. 忘记HP服务器ilo密码?如何在不重启服务器的情况下重置ilo登录密码
  18. 从零玩转HTML5+CSS3项目实战-跟着李南江学编程
  19. 最近三年的百度产品经理面试与笔试题完整版
  20. EDK2之debug

热门文章

  1. 【pyqt5】配置Qt Designer之【designer.exe的保存位置及ui文件转py文件及no Qt platform plugin could be initialized 问题解决】
  2. 深度学习之pytorch(三) C++调用
  3. 红帽linux无法进入tty,linux自启脚本(以及无法进入tty控制台)
  4. python在材料模拟中的应用_基于Python的ABAQUS二次开发及在板料快速冲压成形模拟中的应用...
  5. mysql分析sql语句性能_sql语句执行性能分析
  6. 图像处理核函数:之高斯核的生成方法 python
  7. 连接两个linux系统软件,【技术共享】synergy-两台电脑共享一套鼠标键盘-Linuxwindows...
  8. android手机活跃度,微信Android机型活跃度曝光,这个结果你满意吗?
  9. python全栈学习--day12(函数高级应用-带参数的装饰器,多个装饰器装饰一个函数)...
  10. hdu 5045 Contest(状态压缩DP)