赞
踩
FLAPPYBIRD 就是小鸟穿过管道的一个小游戏,相信大家都玩过,这次我们用之前所学的只是来完成一个他的简易版本。
1.按下空格键小鸟上升,不按小鸟下落
2.搭建小鸟需要穿过的管道
3.管道自动左移和创建
4.小鸟撞到管道游戏结束
想要完成这些目标,我们需要一个新的知识 Ncurses库
在项目中的作用:创建一个新的窗口,对这个窗口进行一系列的操作
安装命令:sudo apt-get install libncurses5-dev
man手册下载:sudo apt-get install ncurses-doc
注意:为了能够使用Ncurses库,必须在源程序中将#include<curses.h>包括进来,而且在编译的需要与它链接起来. 也就是在编译时需要在命令最后加 -lcurses或者-lncurses
1. initscr(void);
是curses模式的入口。将终端屏幕初始化为curses模式,为当前屏幕和相关的数据结构分配内存。简单来说就是创建一个新窗口。
2. int endwin(void);
是curses模式的出口,退出curses模式,释放curses子系统和相关数据结构占用的内存。他与initscr()配对使用,有打开就要有关闭。
3. int curs_set(int visibility);
设置光标是否可见,visibility = 0(不可见),visibility = 1(可见)
4. int move(int new_y, int new_x);
将光标移动到new_y所指定的列和new_x所指定的行,注意,此函数第一个参数代表列,第二个参数代表行,move(10,5)表示将光标移动到第五行第十列的位置。
5. int addch(const chtype char);
在当前光标位置添加字符,一般配合move使用,先移动到想要添加的位置,然后添加想要添加的字符。
扩展:
如果想添加字符串可以使用printw()函数,此函数作用为在光标处添加一个字符串,他的参数就是你想添加的字符串,也有升级版mvprintw()函数,他的作用是移动到你指定的位置后添加字符串,他有三个参数,前两个为你想要吧光标移动到的位置,最后一个参数为你要添加的字符串。
6. int refresh(void);
刷新物理屏幕。将获取的内容显示到显示器上。每次移动都需要刷新一下后才能显示移动之后的现象,否则程序裕兴但是没有现象发生。
7. int keypad(WINDOW *window_ptr, bool key_on);
允许使用功能键。exp:keypad(stdscr,1);
8. int getch(void);
读取键盘输入的一个字符,与getc作用基本相同,读取一个键盘输入的字符,在实现空格控制小鸟上升时使用。
9. chtype inch(void);
获取当前光标位置的字符。
注:curses有自己的字符类型chtype,使用时强制类型转换为char
(char)inch == ' '//判断当前位置字符是否为空格
10. int start_color(void);
启动color机制,初始化当前终端支持的所有颜色,当你想改变你创建的窗口里的元素的颜色时,就需要启动color机制。
11. int init_pair(short pair_number, short foreground, short background);
配置颜色对
COLOR_BLACK 黑色 COLOR_MAGENTA 品红色
COLOR_RED 红色 COLOR_CYAN 青色
COLOR_GREEN 绿色 COLOR_WHITE 白色
COLOR_YELLOW 黄色 COLOR_BLUE 蓝色
- init_pair(1,COLOR_RED,COLOR_BLUE)//设置颜色对的编号为 1 ,
- //他的字符颜色为RED,背景颜色为BLUE
12. int COLOR_PAIR(int pair_number);
设置颜色属性,设置完颜色对,可以通过COLOR_PAIR实现,他的参数就是上面设置的颜色对编号,COLOR_PAIR一般作为下面两个函数的参数去使用。
13. int attron(chtype attribute);
启用属性设置,他的参数为COLOR_PAIR(pair_number);使用在你需要进行更改颜色的代码之前。
14. int attroff(chtype attribute);
关闭属性设置,在需要更改颜色的代码之后,参数同上。
15.noecho();
关闭输入字符显示,使用后在键盘输入的字符将不会被显示。
因为一时想不起来要写什么,具体就全部写在代码的备注里与代码一起理解。
- #include<stdlib.h>
- #include<curses.h>
- #include <sys/time.h>
- #include<stdio.h>
- #include<signal.h>
- #include<unistd.h>
- #include<time.h>
- #define BIRD '@'//用 @ 代表小鸟
- #define PIPE '+'//用 + 代表管道
- #define DIS ' '// 被清除的管道和小鸟用空格代替。
- #define LOWSPEED 300//自动移动的速度
- int by = 15,bx = 5;//初始化小鸟的位置
- void interface();//对创建的新的界面的初始化
- void birdfly();//小鸟飞起来
- void clean();//清除小鸟上一次所在位置
- void birdpos();//显示小鸟当前位置
- void birdlow(int speed);//小鸟自动下降的实现
- /*
- 管道我们是用链表的结构来表示
- 思路是每隔20个位置就创建一个节点
- 用这个节点来表示管道的初始位置
- 后面创建管道时只需要在节点后添加管道表示PIPE即可
- 下面是管道节点的初始化
- */
- typedef struct pipe{
- int y;
- int x;
- struct pipe *next;
-
- }pipenode,*pipelist;
-
- pipelist head,tail;
- //下面函数就如定义的意思,不做解释
- void pipe_create();
- void pipe_clean();
- void pipe_move();
- void pipe_show();
- /*
- 我们要实现小鸟的自动下落和管道的自动移动,
- 我们的思路是移动管道,小鸟的横坐标不变,
- 所以我们在这里使用定时器,
- 让程序每隔LOWSPEED的时间发送一次INTALRM信号,
- 然后我们捕获这个信号
- 对其进行操作来实现管道自动左移和小鸟自动下落
- */
- void *handler(void *arg){
- pipelist p, new;
- int i,j;
- //三行实现小鸟下降
- clean();
- by++;
- birdpos();
- //判断小鸟下降时是否触碰到管道
- if((char)inch == PIPE){
- birdlow(0);
- endwin();
- exit(0);
- }
- //管道清除,当一个管道的第一个节点的x坐标为0时
- //清空整个管道,然后再清除该节点,然后再链表的最后新增一个节点
- //然后重新经过show函数使新管道出现
- p = head->next;
- if(p->x == 0)
- {
- head->next = p->next;
- for(i = p->x; i < p->x+10; i++)
- {
- /*上半部分管道清除*/
- for(j=0; j<p->y; j++)
- {
- move(j,i);
- addch(DIS);
- }
- /*下半部分管道清除*/
- for(j = p->y+5; j < 25; j++)
- {
- birdlow(0);
- endwin();
- exit(0);
- }
- refresh();
- }
- free(p);
- //创建新管道并初始化
- new = (pipelist)malloc(sizeof(pipenode));
- new->x = tail->x + 20;
- new->y = rand() % 11 + 5;
- new->next = NULL;
- tail->next = new;
- tail = new;
- }
- pipe_clean();
- pipe_move();
- pipe_show();
-
- }
- int main(int argc,char **argv){
-
- //先初始化界面,在进行项目的实现
- interface();
- birdpos();
- birdlow(LOWSPEED);
- srand(time(0));//使管道的长度每次重新开始时都不一样
- pipe_create();
- pipe_show();
- birdfly();
- while(1);
- endwin();
- }
-
- //显示小鸟当前位置
- void birdpos(){
- attron(COLOR_PAIR(1));//使用颜色对1
- move(by,bx);
- addch(BIRD);
- refresh();
- attroff(COLOR_PAIR(1));
-
- }
- //删除前一个位置的小鸟,实现就是把那个位置的字符串替换为空格
- void clean(){
-
- move(by,bx);
- addch(DIS);
- refresh();
-
- }
- //新界面的初始化
- void interface(){
-
- initscr();
- curs_set(0);
- noecho();
- keypad(stdscr,1);//允许使用功能键,stdscr代表键盘
- start_color();
- init_pair(1,COLOR_WHITE, COLOR_RED);//定义小鸟所用的颜色对 1
- init_pair(2,COLOR_WHITE, COLOR_GREEN);//定义管道所用的颜色对 2
- }
- //鸟飞
- void birdfly(){
- char fly;
- while(1){
- fly = getch();
- if(fly == ' '){
- /*
- 当接受到的字符为空格时
- 删除鸟当前的位置
- 然后让光标移动到之前位置的上面一个位置
- 在重新显示小鸟
- */
- clean();
- by--;
- birdpos();
- //判断小鸟上升时是否触碰到管道
- //若碰到则关闭窗口和程序来代表游戏结束。
- if((char)inch == PIPE){
- birdlow(0);//将信号器启动时间变为0,即不再发送信号。
- endwin();
- exit(0);
- }
- }
- }
-
- }
- /*
- 因为开始写函数时没想那么多所以起了个birdlow
- 他里面是一个定时器,每个多长时间会发送一次INTALRM信号
- 时间由上面的LOWSPPED决定
- 实际他代表了整个程序管道的移动和小鸟下落的速度
- */
- void birdlow(int speed){
- struct itimerval timer;
- int s,us;
- s = speed / 1000;//将速度变为微秒,让他快一点
- us = speed;
- timer.it_value.tv_sec = s;//定义第一次启动的时间
- timer.it_value.tv_usec = us;
- timer.it_interval.tv_sec = s;//定义之后每次重新启动的时间
- timer.it_interval.tv_usec = us;
- setitimer(ITIMER_REAL,&timer,NULL);//启动定时器
- signal(SIGALRM,handler);//信号捕捉函数,也可使用sigaction;
- }
-
- //创建管道 ,x代表管道节点的位置
- void pipe_create(){
-
- pipelist p,q;
- int i;
- head = (pipelist)malloc(sizeof(pipenode));
- head ->next = NULL;
- p = head;
- //屏幕上一共五个管道 ,创建五个节点来表示每个管道的起始位置
- for(i = 1; i <= 5 ;i++){
- q = (pipelist)malloc(sizeof(pipenode));
- q -> x = (i) * 20;//每个管道间隔为20
- q -> y = rand() % 11 + 5;//随机生成一个5-15的输来表示管道的高度
- q ->next = NULL;
- p ->next = q;
- p = q;
-
- }
- tail = p;
- }
-
-
- //管道展示,也可以理解为填充管道
- void pipe_show(){
-
- pipelist p;
- int i,j;
- p = head ->next;
- attron(COLOR_PAIR(2));
-
- while(p){
- /*
- 让p指向管道的第一个节点,将管道的宽度设计为10
- 因为下一个节点的位置在20格之后,所以我们每次填充了一排纵向管道
- 就让x的位置向后移动一格即可 ,在十格的管道都填充完之后,
- 就让p指向之前创建的链表的下一个节点。
- 第一层for循环的作用是移动管道的节点来为他添加纵向的管道
- 第二层for循环前面的作用是添加上半部分的管道
- 后面的作用是添加下半部分的管道
- 下班部分在y的基础上+5,所以管道之间的间隙就是5,
- 间隙之上是上半部分,间隙之下是下半部分
- */
- for(i = p->x;i <p->x +10 ; i++ ){
-
- for(j = 0;j < p->y;j++){
- move(j,i);
- addch(PIPE);
-
- }
-
- for(j = p->y +5 ; j < 25; j++){
-
- move(j,i);
- addch(PIPE);
- }
-
- }
- refresh();
- p = p->next;
- }
- attron(COLOR_PAIR(2));
- }
- //管道清除,实现逻辑与展示相同,只是把添加的字符由管道PIPE替换为空格DIS
- void pipe_clean(){
- pipelist p;
- int i,j;
- p = head ->next;
- while(p){
- for(i = p->x;i <p->x +10 ; i++ ){
-
- for(j = 0;j < p->y;j++){
- move(j,i);
- addch(DIS);
-
- }
-
- for(j = p->y +5 ; j < 25; j++){
-
- move(j,i);
- addch(DIS);
- }
-
- }
- refresh();
- p = p->next;
-
- }
-
- }
- // 管道移动,只要链表p不空,就一直让x--来实现管道左移
- // 实际上也就是更改x的值来使管道展示和清除时的位置发生变换来实现管道左移
- void pipe_move(){
-
- pipelist p;
- p = head ->next;
- while(p){
- p ->x--;
- p = p->next;
-
- }
-
-
- }
自认为可以完善的点:
1.代码堆在一块有点多,封装的再好一点
2.每次移动管道都需要把所有的管道都清除后再重新添加,重复工作太多,有待完善
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。