基于Linux的Flappy bird游戏开发

项目介绍

主要是使用C语言实现,开启C项目之旅。

复习巩固C语言、培养做项目的思维。

功能:

        按下空格键小鸟上升,不按下落;

        显示小鸟需要穿过的管道;

        小鸟自动向右飞行;(管道自动左移和创建)

        小鸟与管道碰到游戏结束

知识储备:

        C语言

        数据机构--链表

        Ncurses库(工具)

        信号机制(工具)

项目逻辑:

基于Linux的Flappy bird游戏开发_第1张图片

如何显示游戏界面?

如何实现空格键控制小鸟上升?

Ncurses库

解决上述问题

其中vim界面就是使用Ncurses库来实现的

此项目一共使用14个函数来实现

注:
       安装命令:sudo apt-get install libncurses5-dev
       为了能够使用Ncurses库,必须在源程序中将#include包括进来,而且在编译的需要与它链接起来. 
       在gcc中可以使用参数-lncurses进行编译.

1.  initscr(void);
    是curses模式的入口。将终端屏幕初始化为curses模式,为当前屏幕和相关的数据结构分配内存。
    
2.  int  endwin(void); 
    是curses模式的出口,退出curses模式,释放curses子系统和相关数据结构占用的内存。
    
3.  int curs_set(int visibility); 
    设置光标是否可见,visibility:0(不可见),1(可见)
        
4.  int move(int  new_y, int  new_x);
    将光标移动到new_y所指定的行和new_x所指定的列
    
5.  int addch(const  chtype  char); 
    在当前光标位置添加字符
    
6. int  refresh(void); 
    刷新物理屏幕。将获取的内容显示到显示器上。    
    
7.  int  keypad(WINDOW  *window_ptr,  bool  key_on); 
    允许使用功能键。exp:keypad(stdscr,1);//允许使用功能按键
    
8.  int getch(void); 
    读取键盘输入的一个字符
    
9. chtype inch(void); 
    获取当前光标位置的字符。
    注:curses有自己的字符类型chtype,使用时强制类型转换为char
    
10. int start_color(void); 
    启动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       蓝色
    
12. int  COLOR_PAIR(int  pair_number); 
    设置颜色属性,设置完颜色对,可以通过COLOR_PAIR实现
    
13. int  attron(chtype  attribute); 
    启用属性设置
    
14. int  attroff(chtype  attribute); 
    关闭属性设置

#include 
#include 
int main(int argc, const char *argv[])
{
	char ch;
	initscr();//进入curses模式
	curs_set(0);
	noecho();//禁止字符显示
	keypad(stdscr,1);//允许使用功能键
	start_color();//启动颜色机制
	init_pair(1,COLOR_WHITE, COLOR_RED);
	init_pair(2,COLOR_GREEN,COLOR_WHITE);

	ch = getch();
	if(ch == 'Q')
	{
		attron(COLOR_PAIR(1));
		move(10,10);
		addch('A');
		refresh();
		attroff(COLOR_PAIR(1));
	}
	move(10,10);
	ch = (char)inch();
	if(ch == 'A')
	{
		attron(COLOR_PAIR(2));
		move(10,11);
		addch('B');
		refresh();		
		attroff(COLOR_PAIR(2));
	}


	while(1);
	endwin();//退出curses模式
	return 0;
}

 然后,在屏幕上10行10列显示C,接着按Q显示AB和D

#include 
#include 
int main(int argc, const char *argv[])
{
	char ch;
	initscr();//进入curses模式
	curs_set(0);
	noecho();//禁止字符显示
	keypad(stdscr,1);//允许使用功能键
	start_color();//启动颜色机制
	init_pair(1,COLOR_WHITE, COLOR_RED);
	init_pair(2,COLOR_GREEN,COLOR_WHITE);

	move(5,5);
	addch('C');
	refresh();
	ch = getch();
	if(ch == 'Q')
	{
		attron(COLOR_PAIR(1));
		move(10,10);
		addch('A');
		refresh();
		attroff(COLOR_PAIR(1));
	}
	move(10,10);
	ch = (char)inch();
	if(ch == 'A')
	{
		attron(COLOR_PAIR(2));
		move(10,11);
		addch('B');
		refresh();		
		attroff(COLOR_PAIR(2));
	}
	move(15,15);
	addch('D');
	refresh();


	while(1);
	endwin();//退出curses模式
	return 0;
}

信号机制

getch()阻塞获取键盘按键输入,怎么操作才能不影响小鸟下落和管道移动?

        使用信号机制,将小鸟下落和管道移动放到信号处理函数中。

基于Linux的Flappy bird游戏开发_第2张图片

 下图为信号的检测和处理流程图基于Linux的Flappy bird游戏开发_第3张图片

 信号设置的函数

#include 

 #include

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 ) ;

        sleep(5);

        return 0;

}

在5秒内若按下Ctrl+C,会打印HELLO,接着退出程序

 设置定时器

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。 

每一秒都显示一次B,按一次Q显示一次A ,并且A和B移动一下

#include 
#include 
#include 
#include 

int x=10,y=10;
int a=5,b=10;
void handler(int sig)
{
	move(a,b);
	addch('B');
	refresh();
	b++;

}
int main(int argc, const char *argv[])
{
	char ch;
	initscr();//进入curses模式
	curs_set(0);
	noecho();//禁止字符显示
	keypad(stdscr,1);//允许使用功能键
	start_color();//启动颜色机制
	init_pair(1,COLOR_WHITE, COLOR_RED);
	init_pair(2,COLOR_GREEN,COLOR_WHITE);

	signal(SIGALRM, handler);

	/*设置定时时间*/
	struct itimerval timer;
	timer.it_value.tv_sec = 3;//首次启动定时时间
	timer.it_value.tv_usec = 0;
	timer.it_interval.tv_sec = 1;//之后每次的定时时间
	timer.it_interval.tv_usec = 0;
	/*启动定时*/
	setitimer(ITIMER_REAL, &timer, NULL);

	while(1){
		ch = getch();
		if(ch == 'Q')
		{
			attron(COLOR_PAIR(1));
			move(x,y);
			addch('A');
			refresh();
			y++;
			attroff(COLOR_PAIR(1));
		}
	}

	while(1);
	endwin();//退出curses模式
	return 0;
}

项目编程

小鸟飞起来

基于Linux的Flappy bird游戏开发_第4张图片

 1、curses库初始化

void init_curses()//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);//管道颜色设置
}

 2、设置定时时间

int set_timer(int ms_t)//设置定时器--ms
{
	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;

}

 3、小鸟功能

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();
			
		}
	}
}

 小鸟自动下落,使用信号处理函数

 基于Linux的Flappy bird游戏开发_第5张图片

基于Linux的Flappy bird游戏开发_第6张图片

管道动起来

/*定义关于管道的结构体*/
typedef struct Pipe{
    int x;//列坐标
    int y;//横坐标
    struct Pipe *next;
}Pipe_node, *Pipe_list;

Pipe_list head, tail;

1.创建链表

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;//实现每隔20列创建一个管道节点
		new->y = rand() % 11 + 5; // 管道长度限制在(5-15行)之间,注意要添加随机种子srand(time(0))
		new->next = NULL;
		p->next = new;
		p = new;
	}
	tail = p;
}

2.显示管道

基于Linux的Flappy bird游戏开发_第7张图片

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; jy; 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));
}

基于Linux的Flappy bird游戏开发_第8张图片

基于Linux的Flappy bird游戏开发_第9张图片

3.清除管道

        只需将2的PIPE代替为BLANK

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; jy; j++)
			{
				move(j,i);
				addch(BLANK);
			}
			/*下半部分管道创建*/
			for(j = p->y+5; j < 25; j++)
			{
				move(j,i);
				addch(BLANK);
			}
		}
		refresh();
		p = p->next;
	}

}

4.移动管道

void move_pipe()//移动管道
{
	Pipe_list p;
	p = head->next;
	while(p)
	{
		p->x--;
		p = p->next;
	}

}

 都放到信号处理函数中实现

void handler(int sig)
{
	Pipe_list p, new;
	int i,j;
	/*小鸟下落*/
	clear_bird();
	bird_y++;
	show_bird();
	/*管道移动*/
	clear_pipe();
	move_pipe();
	show_pipe();
}

基于Linux的Flappy bird游戏开发_第10张图片

 目前有两个BUG:1、小鸟碰到管道没有结束游戏,2、最下方不停的有+生成;3、只创建了5组管道

1.判断游戏结束:小鸟与管道碰到

2.循环创建管道

3.为管道和小鸟添加色彩

1、小鸟上升,碰到管道结束游戏

基于Linux的Flappy bird游戏开发_第11张图片

2、小鸟下降,碰到管道结束游戏

基于Linux的Flappy bird游戏开发_第12张图片

此时,实现功能1.判断游戏结束:小鸟与管道碰到

3、循环创建管道

 判断第一组管道是否移动到第0列,如果是就清空第一组管道,同时释放第一组管道的节点空间;

紧接着创建一个新的节点,并把此节点添加到链表中

基于Linux的Flappy bird游戏开发_第13张图片

3.为管道和小鸟添加色彩 

void init_curses()//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);//管道颜色设置
}
void show_bird()//显示小鸟
{
	attron(COLOR_PAIR(1));
	move(bird_y,bird_x);
	addch(BIRD);
	refresh();
	attroff(COLOR_PAIR(1));
}
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; jy; 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));
}

代码总结

#include 
#include 
#include 
#include 
#include 
#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);//信号处理函数

int main(int argc, const char *argv[])
{
	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;
}
void init_curses()//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);//管道颜色设置
}
int set_timer(int ms_t)//设置定时器--ms
{
	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;

}
void handler(int sig)
{
	Pipe_list p, new;
	int i,j;
	/*小鸟下落*/
	clear_bird();
	bird_y++;
	show_bird();
	/*游戏结束判断*/
	if((char)inch() == PIPE)
	{
		set_timer(0);
		endwin();
		exit(1);
	}
	p = head->next;
	if(p->x == 0)
	{
		head->next = p->next;
		for(i = p->x; i < p->x+10; i++)
		{
			/*上半部分管道*/
			for(j=0; jy; 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();
}
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);
			}
		}
	}
}
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; jy; 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; jy; 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;
	}

}

          指定的信号处理函数代表捕捉方式

你可能感兴趣的:(小鸟项目,linux,运维,服务器)