C语言版flappy_bird实现
来源:互联网 发布:android程序员考试 编辑:程序博客网 时间:2024/05/13 02:52
一、实验说明
1. 环境介绍
本实验环境采用带桌面的Ubuntu Linux环境,实验中会用到桌面上的程序:
- LX终端(LXTerminal):Linux命令行终端,打开后会进入Bash环境,可以使用Linux命令。
- GVim
2.环境使用
使用GVim编辑器输入实验所需的代码及文件,使用LX终端(LXTerminal)运行所需命令进行操作。 完成实验后可以点击桌面上方的“实验截图”保存并分享实验结果到微博,向好友展示自己的学习进度。实验楼提供后台系统截图,可以真实有效证明您已经完成了实验。 实验记录页面可以在“我的主页”中查看,其中含有每次实验的截图及笔记,以及每次实验的有效学习时间(指的是在实验桌面内操作的时间,如果没有操作,系统会记录为发呆时间)。这些都是您学习的真实性证明。 本课程中的所有源码可以通过以下方式下载:
二、项目介绍
这次我们的项目是flappy bird游戏,这次,准备好砸你的键盘吧。最终效果图是这样的
三、项目实战
1. 基础知识
我们的项目用到了一点数据结构的知识,还涉及到了linux的一些系统调用,有助于我们加深对linux下的程序设计的深入理解。此外,我们还用了一个文本界面的屏幕绘图库ncurses,编译时需要加上-lcurses选项。
1.1.安装ncurses库
在终端输入sudo apt-get install libncurses5-dev
2. 设计思路
我们的flappy bird游戏里最关键的两点就是响应键盘输入和定时绘图。这就需要结合linux提供的系统函数和我们使用的ncurses库来完成了。另一个问题是如何使bird能看起来像是在向前飞一样,如果一直移动bird势必会超出屏幕范围。我们不妨反过来想,让bird一直保持在原地,而让背景一直向bird的方向移动。这样,就造成了好像bird一直向前移动的效果。
3. 开始动手
让我们先来完成一些基础工作,因为我们是终端字符界面,所以一切离不开ASCII字符,我们需要定义一些常量。我们用'*'来表示背景里的柱子,用'O'来表示bird。好了,代码如下:
include <curses.h>include <stdlib.h>include <signal.h>include <sys/time.h>define CHAR_BIRD 'O'define CHAR_STONE '*'define CHAR_BLANK ' '
背景上的柱子用链表结构表示,定义为结构体如下:
typedef struct node { int x, y; struct node *next;}node, *Node;
还需要几个全局变量:
Node head, tail;int bird_x, bird_y;int ticker;
为了调用起来方便,我们先声明一下我们定义的函数:
void init();void init_bird();void init_draw();void init_head();void init_wall();void drop(int sig);int set_ticker(int n);
4. 定时问题
现在我们来解决如何让背景定时移动的问题。linux系统为我们提供了信号这一概念,可以解决我们的问题。不知道什么是信号?没关系,说白了就是linux内核有个定时器,它每隔一段时间就会向我们的程序发送一个信号,我们的信号接收函数就会被自动执行,我们只要在接受信号的函数里移动背景就行了。因为是内核发送的信号,所以不会因为我们的键盘接受阻塞而阻塞。怎么样,是不是很简单?下面就来写我们的代码:
int set_ticker(int n_msec) { struct itimerval timeset; long n_sec, n_usec; n_sec = n_msec / 1000; n_usec = (n_msec % 1000) * 1000L; timeset.it_interval.tv_sec = n_sec; timeset.it_interval.tv_usec = n_usec; timeset.it_value.tv_sec = n_sec; timeset.it_value.tv_usec = n_usec; return setitimer(ITIMER_REAL, ×et, NULL); }以上代码用来设定内核的定时周期,下面是我们的信号接受函数:void drop(int sig){ int j; Node tmp, p;//draw bird move(bird_y, bird_x); addch(CHAR_BLANK); refresh(); bird_y++; move(bird_y, bird_x); addch(CHAR_BIRD); refresh(); if((char)inch() == CHAR_STONE) { set_ticker(0); sleep(1); endwin(); exit(0); }//first wall out of screen? p = head->next; if(p->x < 0) { head->next = p->next; free(p); tmp = malloc(sizeof(node)); tmp->x = 99; do { tmp->y = rand() % 16; } while(tmp->y < 5); tail->next = tmp; tmp->next = NULL; tail = tmp; ticker -= 10; //speed up! set_ticker(ticker); }//draw new walls for(p = head->next; p->next != NULL; p->x--, p = p->next) { for(j = 0; j < p->y; j++) { move(j, p->x); addch(CHAR_BLANK); refresh(); } for(j = p->y+5; j <= 23; j++) { move(j, p->x); addch(CHAR_BLANK); refresh(); } if(p->x-10 >= 0 && p->x < 80) { for(j = 0; j < p->y; j++) { move(j, p->x-10); addch(CHAR_STONE); refresh(); } for(j = p->y + 5; j <= 23; j++) { move(j, p->x-10); addch(CHAR_STONE); refresh(); } } } tail->x--;}
我们在信号接受函数里将背景向前移动一列,并且让bird向下掉落一行,而且要检测bird是否撞到柱子,是的话game就over了。
5. main函数
先看看代码:
int main(){ char ch; init(); while(1) { ch = getch(); if(ch == ' ' || ch == 'w' || ch == 'W') { move(bird_y, bird_x); addch(CHAR_BLANK); refresh(); bird_y--; bird_y--; move(bird_y, bird_x); addch(CHAR_BIRD); refresh(); if((char)inch() == CHAR_STONE) { set_ticker(0); sleep(1); endwin(); exit(0); } } else if(ch == 'z' || ch == 'Z') { set_ticker(0); do { ch = getch(); } while(ch != 'z' && ch != 'Z'); set_ticker(ticker); } else if(ch == 'q' || ch == 'Q') { sleep(1); endwin(); exit(0); } } return 0;}
我们在main里先做好初始化,然后在循环中接受键盘输入。如果是w或空格键被按下,我们的bird就向上飞两行,如果是q被按下就退出游戏,z被按下游戏则会暂停。 下面看一下init函数:
void init(){ initscr(); cbreak(); noecho(); curs_set(0); srand(time(0)); signal(SIGALRM, drop); init_bird(); init_head(); init_wall(); init_draw(); sleep(1); ticker = 500; set_ticker(ticker);}
init函数首先初始化屏幕,调用了ncurses提供的函数,然后调用各个子函数进行初始化。注意,我们安装了信号接收函数drop,并且设定了定时时间。各个初始化子函数如下。 初始化bird位置:
void init_bird(){ bird_x = 5; bird_y = 15; move(bird_y, bird_x); addch(CHAR_BIRD); refresh(); sleep(1);}
初始化背景里的柱子链表结构:
void init_head() //with header{ Node tmp; tmp = malloc(sizeof(node)); tmp->next = NULL; head = tmp; tail = head;}void init_wall(){ int i; Node tmp, p; p = head; for(i = 19; i <= 99; i += 20) { tmp = malloc(sizeof(node)); tmp->x = i; do { tmp->y = rand() % 16; }while(tmp->y < 5); p->next = tmp; tmp->next = NULL; p = tmp; } tail = p;}
初始化屏幕:
void init_draw(){ Node p; int i, j; for(p = head->next; p->next != NULL; p = p->next) { for(i = p->x; i > p->x-10; i--) { for(j = 0; j < p->y; j++) { move(j, i); addch(CHAR_STONE); refresh(); } for(j = p->y+5; j <= 23; j++) { move(j, i); addch(CHAR_STONE); refresh(); } } sleep(1); }}
6. 编译
gcc flappy_bird.c -o flappy_bird -lcurses
到此,我们的flappy_bird游戏就完成了
- C语言版flappy_bird实现
- C语言版 topK 算法实现
- Sunday算法c语言版实现
- 清华大学出版社 数据结构(C语言版)的实现
- 最小堆的实现(C 语言版)
- 数据结构C语言版单链表的实现
- 栈的实现(C语言版)
- 数组实现队列(C语言版)
- Horner规则实现多项式求值--C语言版
- 数据结构 二叉树的实现 c语言版
- kmp算法实现-算法导论C语言版
- 用数组实现队列(C语言版)
- sms4的简单实现(C语言版)
- 链表-游标实现(C语言版)
- 栈的数组实现(C语言版)
- 顺序表的实现(C语言版)
- 循环队列的实现(C语言版)
- 数据结构C语言版--单链表的基本功能实现
- Spring MVC 教程,快速入门,深入分析
- 串流大师Stream Author 4.0出试题功能的试用心得
- Leetcode Intersection of Two Linked Lists
- 提高SQL的查询效率
- phpstorm 配置Apache服务器
- C语言版flappy_bird实现
- 串流大师制作课件步骤
- 经典dp:最大k乘之类的题
- android Intent机制详解
- Robot Framework Reference Links
- MySQL使用索引的场景及真正利用索引的SQL类型
- poj 2296 Map Labeler【二分+2-set】【经典】
- git svn 忽略列表命令
- solr的配置文档