目录
1、项目介绍
2、功能总结
3、前期准备
3.1 Ncurses库
3.2 信号机制
3.2.1 设置信号响应方式
3.2.2 设置定时器
4、代码实现
4.1 头文件引用及变量、函数定义
4.2 主函数
4.3 curses初始化
4.4 设置定时器
4.5 定时器响应函数
4.6 小鸟控制相关函数
4.7 管道控制相关函数
5、效果测试
1、项目介绍
flappy bird是一款来自越南的独立游戏开发者Dong Nguyen所开发的作品游戏中玩家必须控制一只小鸟,跨越由各种不同长度水管所组成的障碍。
2、功能总结
- 按下空格键小鸟上升,不按小鸟下落
- 搭建小鸟需要穿过的管道
- 管道自动左移和创建
- 小鸟撞到管道游戏结束
3、前期准备
3.1 Ncurses库
做这个项目之前,有两个值得思考:
- 如何显示游戏界面?
- 如何实现空格键控制小鸟上升?
我们可以通过Ncurses库来实现
Ncurses是最早的System V Release 4.0 (SVr4)中 curses的一个克隆和升级。
这是一个可自由配置的库,完全兼容旧版本curses。Ncurses构成了一个工作在底层终端代码之上的封装,并向用户提供了一个灵活高效的API(Application Programming Interface 应用程序接口)。它提供了创建窗口界面,移动光标,产生颜色,处理键盘按键等功能。使程序员编写应用程序不需要关心那些底层的终端操作。
简而言之,它是一个管理应用程序在字符终端显示的函数库。
安装命令:
sudo apt-get install libncurses5-dev
注:为了能够使用Ncurses库,必须在源程序中将#include<curses.h>包括进来,而且在编译的需要与它链接起来. 在gcc中可以使用参数-lncurses进行编译.
initscr(void); | 是curses模式的入口。将终端屏幕初始化为curses模式,为当前屏幕和相关的数据结构分配内存。 |
int endwin(void); | 是curses模式的出口,退出curses模式,释放curses子系统和相关数据结构占用的内存。 |
int curs_set(int visibility); | 设置光标是否可见,visibility:0(不可见),1(可见) |
int move(int new_y, int new_x); | 将光标移动到new_y所指定的行和new_x所指定的列 |
int addch(const chtype char); | 在当前光标位置添加字符 |
int refresh(void); | 刷新物理屏幕。将获取的内容显示到显示器上。 |
int keypad(WINDOW *window_ptr, bool key_on); | 允许使用功能键。exp:keypad(stdscr,1);//允许使用功能按键 |
int getch(void); | 读取键盘输入的一个字符 |
chtype inch(void); | 获取当前光标位置的字符。 注:curses有自己的字符类型chtype,使用时强制类型转换为char |
int start_color(void); | 启动color机制,初始化当前终端支持的所有颜色 |
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 蓝色 |
int COLOR_PAIR(int pair_number); | 设置颜色属性,设置完颜色对,可以通过COLOR_PAIR实现 |
int attron(chtype attribute); | 启用属性设置 |
int attroff(chtype attribute); | 关闭属性设置 |
示例:
#include <stdio.h>
#include <curses.h>
int main(int argc,const char* argv[])
{
char ch;
initscr();//进入curses模式
curs_set(0);
keypad(stdscr,1);//允许使用功能按键
noecho();//禁止输入字符显示
start_color();//启动颜色机制
init_pair(1,COLOR_RED,COLOR_BLUE);
attron(COLOR_PAIR(1));
move(10,10);
addch('@');
refresh();
attroff(COLOR_PAIR(1));
while(1);
endwin();//退出curses模式
return 0;
}
3.2 信号机制
获取空格按键是可以通过getchar阻塞获取字符按键输入,但是这样做就会影响小鸟的下落和管道的创建。在Linux中,软中断信号(signal,简称为信号)是在软件层次上对中断的一种模拟,用来通知进程发生了异步事件。内核可以因为内部事件而给进程发送信号,通知进程发生了某个事件。
信号响应的方式:
- 忽略信号,即对信号不做任何处理;
- 捕捉信号,即信号发生时执行用户自定义的信号处理函数。
- 执行缺省操作,Linux对每种信号都规定了默认操作。
3.2.1 设置信号响应方式
#include <unistd.h>
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
成功时返回原先的信号处理函数,失败时返回SIG_ERR
- signum:指明了所要处理的信号类型
- handler:描述了与信号关联的动作,SIG_DFL代表缺省方式; SIG_IGN 代表忽略信号;指定的信号处理函数代表捕捉方式
示例
// 头文件省略
void handler (int signo) {
printf(“HELLO!\n”);
}
int main() {
signal(SIGINT, handler);
while ( 1 ) ;
return 0;
}
3.2.2 设置定时器
struct itimerval {
struct timeval it_interval; /* 计时器重新启动的间歇值 */
struct timeval it_value; /* 计时器安装后首次启动的初 }; 始值,之后就没有用 */
struct timeval {
long tv_sec; /* 秒 */
long tv_usec; /* 微妙*/
};
int setitimer(int which, const struct itimerval *value,
struct itimerval *ovalue)
参数:
- which:间歇计时器类型,ITIMER_REAL //数值为0,发送的信号是SIGALRM。
- struct itimerval *value:将value指向的结构体设为计时器的当前值;
- struct itimerval *ovalue:保存计时器原有值。一般设置为NULL。
返回值: 成功返回0。失败返回-1。
4、代码实现
4.1 头文件引用及变量、函数定义
#include <stdio.h>
#include <curses.h>
#include <signal.h>
#include <sys/time.h>
#include <stdlib.h>
#define BIRD '@'
#define BLANK ' '
#define PIPE '+'
/*定义关于管道的结构体*/
typedef struct Pipe{
int x;//列坐标
int y;//横坐标
struct Pipe *next;
}Pipe_node, *Pipe_list;
Pipe_list head,tail;
void creat_list();//创建链表
void show_pipe();//显示管道
void clear_pipe();//清楚管道
void move_pipe();//移动管道
int bird_y,bird_x;//小鸟坐标
void show_bird();//显示小鸟
void clear_bird();//清除小鸟
void move_bird();//移动小鸟
void init_curses();//curses库初始化
int set_timer(int ms_t);//设置定时器--ms
void handler(int sig);//信号处理函数
4.2 主函数
int main()
{
bird_y = 15;//行
bird_x = 10;//列
init_curses();
signal(SIGALRM,handler);
set_timer(500);//500ms
srand(time(0));//随机种子
creat_list();
show_pipe();
show_bird();
move_bird();
return 0;
}
4.3 curses初始化
void init_curses()
{
initscr();//进入curses模式
curs_set(0);//禁止光标显示
noecho();//禁止输入字符显示
keypad(stdscr,1);//启动功能按键
start_color();//启动颜色机制
init_pair(1,COLOR_WHITE,COLOR_RED);//小鸟颜色设置
init_pair(2,COLOR_WHITE,COLOR_GREEN);//管道颜色设置
}
4.4 设置定时器
int set_timer(int ms_t)
{
struct itimerval timer;
long t_sec,t_usec;
int ret;
t_sec = ms_t / 1000;//s
t_usec = (ms_t % 1000)*1000;//us
timer.it_value.tv_sec = t_sec;
timer.it_value.tv_usec = t_usec;//首次启动定时值
timer.it_interval.tv_sec = t_sec;
timer.it_interval.tv_usec = t_usec;//定时时间间隔
ret = setitimer(ITIMER_REAL,&timer,NULL);//
return ret;
}
4.5 定时器响应函数
void handler(int sig)
{
Pipe_list p,new;
//小鸟下落
clear_bird();
bird_y++;
show_bird();
/*游戏结束判断*/
if((char)inch() == PIPE)
{
set_timer(0);
endwin();
exit(1);
}
p = head->next;
int i,j;
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(BLANK);
}
//创建下半部分管道
for(j=p->y+5;j<25;j++)
{
move(j,i);
addch(BLANK);
}
refresh();
}
free(p);
new = (Pipe_list)malloc(sizeof(Pipe_node));
new->x = tail->x +20;
new->y = rand() % 11 +5;
new->next = NULL;
tail->next = new;
tail = new;
}
//管道移动
clear_pipe();
move_pipe();
show_pipe();
}
4.6 小鸟控制相关函数
void show_bird()//显示小鸟
{
attron(COLOR_PAIR(1));
move(bird_y,bird_x);
addch(BIRD);
refresh();
attroff(COLOR_PAIR(1));
}
void clear_bird()//清除小鸟
{
move(bird_y,bird_x);
addch(BLANK);
refresh();
}
void move_bird()//移动小鸟
{
char key;
while(1)
{
key = getch();
if(key == ' ')
{
clear_bird();
bird_y--;
show_bird();
/*游戏结束判断*/
if((char)inch() == PIPE)
{
set_timer(0);
endwin();
exit(1);
}
}
}
}
4.7 管道控制相关函数
void creat_list()//创建链表
{
int i;
Pipe_list p,new;
head = (Pipe_list)malloc(sizeof(Pipe_node));
head->next = NULL;
p = head;
for(i = 0;i<5;i++)
{
new = (Pipe_list)malloc(sizeof(Pipe_node));
new->x = (i+1)*20;
new->y = rand()%11+5;//(5-15行)
new->next = NULL;
p->next = new;
p = new;
}
tail = p;
}
void show_pipe()//显示管道
{
Pipe_list p;
int i,j;
p = head->next;
attron(COLOR_PAIR(2));
while(p)
{
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;
}
attroff(COLOR_PAIR(2));
}
void clear_pipe()//清除管道
{
Pipe_list 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(BLANK);
}
//创建下半部分管道
for(j=p->y+5;j<25;j++)
{
move(j,i);
addch(BLANK);
}
}
refresh();
p = p->next;
}
}
void move_pipe()//移动管道
{
Pipe_list p;
p = head->next;
while(p)
{
p->x--;
p = p->next;
}
}