当前位置:   article > 正文

嵌入式小项目——FLAPPYBIRD_flappy bird 粤嵌

flappy bird 粤嵌

简介:

      FLAPPYBIRD 就是小鸟穿过管道的一个小游戏,相信大家都玩过,这次我们用之前所学的只是来完成一个他的简易版本。

主要完成的目标:

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

2.搭建小鸟需要穿过的管道

3.管道自动左移和创建

4.小鸟撞到管道游戏结束

想要完成这些目标,我们需要一个新的知识 Ncurses库

Ncurses库简介:

在项目中的作用:创建一个新的窗口,对这个窗口进行一系列的操作

安装命令:sudo apt-get install libncurses5-dev

man手册下载:sudo apt-get install ncurses-doc

注意:为了能够使用Ncurses库,必须在源程序中将#include<curses.h>包括进来,而且在编译的需要与它链接起来. 也就是在编译时需要在命令最后加 -lcurses或者-lncurses
   

项目所需要的Ncurses库的函数:

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       蓝色

  1. init_pair(1,COLOR_RED,COLOR_BLUE)//设置颜色对的编号为 1 ,
  2. //他的字符颜色为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();

    关闭输入字符显示,使用后在键盘输入的字符将不会被显示。

代码实现:

     因为一时想不起来要写什么,具体就全部写在代码的备注里与代码一起理解。

  1. #include<stdlib.h>
  2. #include<curses.h>
  3. #include <sys/time.h>
  4. #include<stdio.h>
  5. #include<signal.h>
  6. #include<unistd.h>
  7. #include<time.h>
  8. #define BIRD '@'//用 @ 代表小鸟
  9. #define PIPE '+'//用 + 代表管道
  10. #define DIS ' '// 被清除的管道和小鸟用空格代替。
  11. #define LOWSPEED 300//自动移动的速度
  12. int by = 15,bx = 5;//初始化小鸟的位置
  13. void interface();//对创建的新的界面的初始化
  14. void birdfly();//小鸟飞起来
  15. void clean();//清除小鸟上一次所在位置
  16. void birdpos();//显示小鸟当前位置
  17. void birdlow(int speed);//小鸟自动下降的实现
  18. /*
  19. 管道我们是用链表的结构来表示
  20. 思路是每隔20个位置就创建一个节点
  21. 用这个节点来表示管道的初始位置
  22. 后面创建管道时只需要在节点后添加管道表示PIPE即可
  23. 下面是管道节点的初始化
  24. */
  25. typedef struct pipe{
  26. int y;
  27. int x;
  28. struct pipe *next;
  29. }pipenode,*pipelist;
  30. pipelist head,tail;
  31. //下面函数就如定义的意思,不做解释
  32. void pipe_create();
  33. void pipe_clean();
  34. void pipe_move();
  35. void pipe_show();
  36. /*
  37. 我们要实现小鸟的自动下落和管道的自动移动,
  38. 我们的思路是移动管道,小鸟的横坐标不变,
  39. 所以我们在这里使用定时器,
  40. 让程序每隔LOWSPEED的时间发送一次INTALRM信号,
  41. 然后我们捕获这个信号
  42. 对其进行操作来实现管道自动左移和小鸟自动下落
  43. */
  44. void *handler(void *arg){
  45. pipelist p, new;
  46. int i,j;
  47. //三行实现小鸟下降
  48. clean();
  49. by++;
  50. birdpos();
  51. //判断小鸟下降时是否触碰到管道
  52. if((char)inch == PIPE){
  53. birdlow(0);
  54. endwin();
  55. exit(0);
  56. }
  57. //管道清除,当一个管道的第一个节点的x坐标为0时
  58. //清空整个管道,然后再清除该节点,然后再链表的最后新增一个节点
  59. //然后重新经过show函数使新管道出现
  60. p = head->next;
  61. if(p->x == 0)
  62. {
  63. head->next = p->next;
  64. for(i = p->x; i < p->x+10; i++)
  65. {
  66. /*上半部分管道清除*/
  67. for(j=0; j<p->y; j++)
  68. {
  69. move(j,i);
  70. addch(DIS);
  71. }
  72. /*下半部分管道清除*/
  73. for(j = p->y+5; j < 25; j++)
  74. {
  75. birdlow(0);
  76. endwin();
  77. exit(0);
  78. }
  79. refresh();
  80. }
  81. free(p);
  82. //创建新管道并初始化
  83. new = (pipelist)malloc(sizeof(pipenode));
  84. new->x = tail->x + 20;
  85. new->y = rand() % 11 + 5;
  86. new->next = NULL;
  87. tail->next = new;
  88. tail = new;
  89. }
  90. pipe_clean();
  91. pipe_move();
  92. pipe_show();
  93. }
  94. int main(int argc,char **argv){
  95. //先初始化界面,在进行项目的实现
  96. interface();
  97. birdpos();
  98. birdlow(LOWSPEED);
  99. srand(time(0));//使管道的长度每次重新开始时都不一样
  100. pipe_create();
  101. pipe_show();
  102. birdfly();
  103. while(1);
  104. endwin();
  105. }
  106. //显示小鸟当前位置
  107. void birdpos(){
  108. attron(COLOR_PAIR(1));//使用颜色对1
  109. move(by,bx);
  110. addch(BIRD);
  111. refresh();
  112. attroff(COLOR_PAIR(1));
  113. }
  114. //删除前一个位置的小鸟,实现就是把那个位置的字符串替换为空格
  115. void clean(){
  116. move(by,bx);
  117. addch(DIS);
  118. refresh();
  119. }
  120. //新界面的初始化
  121. void interface(){
  122. initscr();
  123. curs_set(0);
  124. noecho();
  125. keypad(stdscr,1);//允许使用功能键,stdscr代表键盘
  126. start_color();
  127. init_pair(1,COLOR_WHITE, COLOR_RED);//定义小鸟所用的颜色对 1
  128. init_pair(2,COLOR_WHITE, COLOR_GREEN);//定义管道所用的颜色对 2
  129. }
  130. //鸟飞
  131. void birdfly(){
  132. char fly;
  133. while(1){
  134. fly = getch();
  135. if(fly == ' '){
  136. /*
  137. 当接受到的字符为空格时
  138. 删除鸟当前的位置
  139. 然后让光标移动到之前位置的上面一个位置
  140. 在重新显示小鸟
  141. */
  142. clean();
  143. by--;
  144. birdpos();
  145. //判断小鸟上升时是否触碰到管道
  146. //若碰到则关闭窗口和程序来代表游戏结束。
  147. if((char)inch == PIPE){
  148. birdlow(0);//将信号器启动时间变为0,即不再发送信号。
  149. endwin();
  150. exit(0);
  151. }
  152. }
  153. }
  154. }
  155. /*
  156. 因为开始写函数时没想那么多所以起了个birdlow
  157. 他里面是一个定时器,每个多长时间会发送一次INTALRM信号
  158. 时间由上面的LOWSPPED决定
  159. 实际他代表了整个程序管道的移动和小鸟下落的速度
  160. */
  161. void birdlow(int speed){
  162. struct itimerval timer;
  163. int s,us;
  164. s = speed / 1000;//将速度变为微秒,让他快一点
  165. us = speed;
  166. timer.it_value.tv_sec = s;//定义第一次启动的时间
  167. timer.it_value.tv_usec = us;
  168. timer.it_interval.tv_sec = s;//定义之后每次重新启动的时间
  169. timer.it_interval.tv_usec = us;
  170. setitimer(ITIMER_REAL,&timer,NULL);//启动定时器
  171. signal(SIGALRM,handler);//信号捕捉函数,也可使用sigaction;
  172. }
  173. //创建管道 ,x代表管道节点的位置
  174. void pipe_create(){
  175. pipelist p,q;
  176. int i;
  177. head = (pipelist)malloc(sizeof(pipenode));
  178. head ->next = NULL;
  179. p = head;
  180. //屏幕上一共五个管道 ,创建五个节点来表示每个管道的起始位置
  181. for(i = 1; i <= 5 ;i++){
  182. q = (pipelist)malloc(sizeof(pipenode));
  183. q -> x = (i) * 20;//每个管道间隔为20
  184. q -> y = rand() % 11 + 5;//随机生成一个5-15的输来表示管道的高度
  185. q ->next = NULL;
  186. p ->next = q;
  187. p = q;
  188. }
  189. tail = p;
  190. }
  191. //管道展示,也可以理解为填充管道
  192. void pipe_show(){
  193. pipelist p;
  194. int i,j;
  195. p = head ->next;
  196. attron(COLOR_PAIR(2));
  197. while(p){
  198. /*
  199. 让p指向管道的第一个节点,将管道的宽度设计为10
  200. 因为下一个节点的位置在20格之后,所以我们每次填充了一排纵向管道
  201. 就让x的位置向后移动一格即可 ,在十格的管道都填充完之后,
  202. 就让p指向之前创建的链表的下一个节点。
  203. 第一层for循环的作用是移动管道的节点来为他添加纵向的管道
  204. 第二层for循环前面的作用是添加上半部分的管道
  205. 后面的作用是添加下半部分的管道
  206. 下班部分在y的基础上+5,所以管道之间的间隙就是5,
  207. 间隙之上是上半部分,间隙之下是下半部分
  208. */
  209. for(i = p->x;i <p->x +10 ; i++ ){
  210. for(j = 0;j < p->y;j++){
  211. move(j,i);
  212. addch(PIPE);
  213. }
  214. for(j = p->y +5 ; j < 25; j++){
  215. move(j,i);
  216. addch(PIPE);
  217. }
  218. }
  219. refresh();
  220. p = p->next;
  221. }
  222. attron(COLOR_PAIR(2));
  223. }
  224. //管道清除,实现逻辑与展示相同,只是把添加的字符由管道PIPE替换为空格DIS
  225. void pipe_clean(){
  226. pipelist p;
  227. int i,j;
  228. p = head ->next;
  229. while(p){
  230. for(i = p->x;i <p->x +10 ; i++ ){
  231. for(j = 0;j < p->y;j++){
  232. move(j,i);
  233. addch(DIS);
  234. }
  235. for(j = p->y +5 ; j < 25; j++){
  236. move(j,i);
  237. addch(DIS);
  238. }
  239. }
  240. refresh();
  241. p = p->next;
  242. }
  243. }
  244. // 管道移动,只要链表p不空,就一直让x--来实现管道左移
  245. // 实际上也就是更改x的值来使管道展示和清除时的位置发生变换来实现管道左移
  246. void pipe_move(){
  247. pipelist p;
  248. p = head ->next;
  249. while(p){
  250. p ->x--;
  251. p = p->next;
  252. }
  253. }

自认为可以完善的点:

1.代码堆在一块有点多,封装的再好一点

2.每次移动管道都需要把所有的管道都清除后再重新添加,重复工作太多,有待完善

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/910766
推荐阅读
相关标签
  

闽ICP备14008679号