当前位置:   article > 正文

基于Linux的Flappy bird游戏开发

基于Linux的Flappy bird游戏开发

项目介绍

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

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

功能:

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

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

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

        小鸟与管道碰到游戏结束

知识储备:

        C语言

        数据机构--链表

        Ncurses库(工具)

        信号机制(工具)

项目逻辑:

如何显示游戏界面?

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

Ncurses库

解决上述问题

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

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

注:
       安装命令:sudo apt-get install libncurses5-dev
       为了能够使用Ncurses库,必须在源程序中将#include<curses.h>包括进来,而且在编译的需要与它链接起来. 
       在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); 
    关闭属性设置

  1. #include <stdio.h>
  2. #include <curses.h>
  3. int main(int argc, const char *argv[])
  4. {
  5. char ch;
  6. initscr();//进入curses模式
  7. curs_set(0);
  8. noecho();//禁止字符显示
  9. keypad(stdscr,1);//允许使用功能键
  10. start_color();//启动颜色机制
  11. init_pair(1,COLOR_WHITE, COLOR_RED);
  12. init_pair(2,COLOR_GREEN,COLOR_WHITE);
  13. ch = getch();
  14. if(ch == 'Q')
  15. {
  16. attron(COLOR_PAIR(1));
  17. move(10,10);
  18. addch('A');
  19. refresh();
  20. attroff(COLOR_PAIR(1));
  21. }
  22. move(10,10);
  23. ch = (char)inch();
  24. if(ch == 'A')
  25. {
  26. attron(COLOR_PAIR(2));
  27. move(10,11);
  28. addch('B');
  29. refresh();
  30. attroff(COLOR_PAIR(2));
  31. }
  32. while(1);
  33. endwin();//退出curses模式
  34. return 0;
  35. }

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

  1. #include <stdio.h>
  2. #include <curses.h>
  3. int main(int argc, const char *argv[])
  4. {
  5. char ch;
  6. initscr();//进入curses模式
  7. curs_set(0);
  8. noecho();//禁止字符显示
  9. keypad(stdscr,1);//允许使用功能键
  10. start_color();//启动颜色机制
  11. init_pair(1,COLOR_WHITE, COLOR_RED);
  12. init_pair(2,COLOR_GREEN,COLOR_WHITE);
  13. move(5,5);
  14. addch('C');
  15. refresh();
  16. ch = getch();
  17. if(ch == 'Q')
  18. {
  19. attron(COLOR_PAIR(1));
  20. move(10,10);
  21. addch('A');
  22. refresh();
  23. attroff(COLOR_PAIR(1));
  24. }
  25. move(10,10);
  26. ch = (char)inch();
  27. if(ch == 'A')
  28. {
  29. attron(COLOR_PAIR(2));
  30. move(10,11);
  31. addch('B');
  32. refresh();
  33. attroff(COLOR_PAIR(2));
  34. }
  35. move(15,15);
  36. addch('D');
  37. refresh();
  38. while(1);
  39. endwin();//退出curses模式
  40. return 0;
  41. }

信号机制

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

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

 下图为信号的检测和处理流程图

 信号设置的函数

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

        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移动一下

  1. #include <stdio.h>
  2. #include <curses.h>
  3. #include <signal.h>
  4. #include <sys/time.h>
  5. int x=10,y=10;
  6. int a=5,b=10;
  7. void handler(int sig)
  8. {
  9. move(a,b);
  10. addch('B');
  11. refresh();
  12. b++;
  13. }
  14. int main(int argc, const char *argv[])
  15. {
  16. char ch;
  17. initscr();//进入curses模式
  18. curs_set(0);
  19. noecho();//禁止字符显示
  20. keypad(stdscr,1);//允许使用功能键
  21. start_color();//启动颜色机制
  22. init_pair(1,COLOR_WHITE, COLOR_RED);
  23. init_pair(2,COLOR_GREEN,COLOR_WHITE);
  24. signal(SIGALRM, handler);
  25. /*设置定时时间*/
  26. struct itimerval timer;
  27. timer.it_value.tv_sec = 3;//首次启动定时时间
  28. timer.it_value.tv_usec = 0;
  29. timer.it_interval.tv_sec = 1;//之后每次的定时时间
  30. timer.it_interval.tv_usec = 0;
  31. /*启动定时*/
  32. setitimer(ITIMER_REAL, &timer, NULL);
  33. while(1){
  34. ch = getch();
  35. if(ch == 'Q')
  36. {
  37. attron(COLOR_PAIR(1));
  38. move(x,y);
  39. addch('A');
  40. refresh();
  41. y++;
  42. attroff(COLOR_PAIR(1));
  43. }
  44. }
  45. while(1);
  46. endwin();//退出curses模式
  47. return 0;
  48. }

项目编程

小鸟飞起来

 1、curses库初始化

  1. void init_curses()//curses库初始化
  2. {
  3. initscr();//进入curses模式
  4. curs_set(0);//禁止光标显示
  5. noecho();//禁止输入字符显示
  6. keypad(stdscr,1);//启动功能按键
  7. start_color();//启动颜色机制
  8. init_pair(1,COLOR_WHITE, COLOR_RED);//小鸟颜色设置
  9. init_pair(2,COLOR_WHITE, COLOR_GREEN);//管道颜色设置
  10. }

 2、设置定时时间

  1. int set_timer(int ms_t)//设置定时器--ms
  2. {
  3. struct itimerval timer;
  4. long t_sec,t_usec;
  5. int ret;
  6. t_sec = ms_t / 1000; //s
  7. t_usec = (ms_t % 1000) * 1000;//us
  8. timer.it_value.tv_sec = t_sec;
  9. timer.it_value.tv_usec = t_usec;//首次启动定时值
  10. timer.it_interval.tv_sec = t_sec;
  11. timer.it_interval.tv_usec = t_usec;//定时时间间隔
  12. ret = setitimer(ITIMER_REAL, &timer, NULL);
  13. return ret;
  14. }

 3、小鸟功能

  1. void show_bird()//显示小鸟
  2. {
  3. attron(COLOR_PAIR(1));
  4. move(bird_y,bird_x);
  5. addch(BIRD);
  6. refresh();
  7. attroff(COLOR_PAIR(1));
  8. }
  9. void clear_bird()//清除小鸟
  10. {
  11. move(bird_y,bird_x);
  12. addch(BLANK);
  13. refresh();
  14. }
  15. void move_bird()//移动小鸟
  16. {
  17. char key;
  18. while(1)
  19. {
  20. key = getch();
  21. if(key == ' ')
  22. {
  23. clear_bird();
  24. bird_y--;
  25. show_bird();
  26. }
  27. }
  28. }

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

 

管道动起来

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

Pipe_list head, tail;

1.创建链表

  1. void creat_list()//创建链表
  2. {
  3. int i;
  4. Pipe_list p, new;
  5. head = (Pipe_list)malloc(sizeof(Pipe_node));
  6. head->next = NULL;
  7. p = head;
  8. for(i = 0; i < 5; i++)
  9. {
  10. new = (Pipe_list)malloc(sizeof(Pipe_node));
  11. new->x = (i + 1) * 20;//实现每隔20列创建一个管道节点
  12. new->y = rand() % 11 + 5; // 管道长度限制在(5-15行)之间,注意要添加随机种子srand(time(0))
  13. new->next = NULL;
  14. p->next = new;
  15. p = new;
  16. }
  17. tail = p;
  18. }

2.显示管道

  1. void show_pipe()//显示管道
  2. {
  3. Pipe_list p;
  4. int i,j;
  5. p = head->next;
  6. attron(COLOR_PAIR(2));
  7. while(p)
  8. {
  9. for(i = p->x; i < p->x+10; i++)
  10. {
  11. /*上半部分管道*/
  12. for(j=0; j<p->y; j++)
  13. {
  14. move(j,i);
  15. addch(PIPE);
  16. }
  17. /*下半部分管道创建*/
  18. for(j = p->y+5; j < 25; j++)
  19. {
  20. move(j,i);
  21. addch(PIPE);
  22. }
  23. }
  24. refresh();
  25. p = p->next;
  26. }
  27. attroff(COLOR_PAIR(2));
  28. }

3.清除管道

        只需将2的PIPE代替为BLANK

  1. void clear_pipe()//清除管道
  2. {
  3. Pipe_list p;
  4. int i,j;
  5. p = head->next;
  6. while(p)
  7. {
  8. for(i = p->x; i < p->x+10; i++)
  9. {
  10. /*上半部分管道*/
  11. for(j=0; j<p->y; j++)
  12. {
  13. move(j,i);
  14. addch(BLANK);
  15. }
  16. /*下半部分管道创建*/
  17. for(j = p->y+5; j < 25; j++)
  18. {
  19. move(j,i);
  20. addch(BLANK);
  21. }
  22. }
  23. refresh();
  24. p = p->next;
  25. }
  26. }

4.移动管道

  1. void move_pipe()//移动管道
  2. {
  3. Pipe_list p;
  4. p = head->next;
  5. while(p)
  6. {
  7. p->x--;
  8. p = p->next;
  9. }
  10. }

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

  1. void handler(int sig)
  2. {
  3. Pipe_list p, new;
  4. int i,j;
  5. /*小鸟下落*/
  6. clear_bird();
  7. bird_y++;
  8. show_bird();
  9. /*管道移动*/
  10. clear_pipe();
  11. move_pipe();
  12. show_pipe();
  13. }

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

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

2.循环创建管道

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

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

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

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

3、循环创建管道

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

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

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

  1. void init_curses()//curses库初始化
  2. {
  3. initscr();//进入curses模式
  4. curs_set(0);//禁止光标显示
  5. noecho();//禁止输入字符显示
  6. keypad(stdscr,1);//启动功能按键
  7. start_color();//启动颜色机制
  8. init_pair(1,COLOR_WHITE, COLOR_RED);//小鸟颜色设置
  9. init_pair(2,COLOR_WHITE, COLOR_GREEN);//管道颜色设置
  10. }
  11. void show_bird()//显示小鸟
  12. {
  13. attron(COLOR_PAIR(1));
  14. move(bird_y,bird_x);
  15. addch(BIRD);
  16. refresh();
  17. attroff(COLOR_PAIR(1));
  18. }
  19. void show_pipe()//显示管道
  20. {
  21. Pipe_list p;
  22. int i,j;
  23. p = head->next;
  24. attron(COLOR_PAIR(2));
  25. while(p)
  26. {
  27. for(i = p->x; i < p->x+10; i++)
  28. {
  29. /*上半部分管道*/
  30. for(j=0; j<p->y; j++)
  31. {
  32. move(j,i);
  33. addch(PIPE);
  34. }
  35. /*下半部分管道创建*/
  36. for(j = p->y+5; j < 25; j++)
  37. {
  38. move(j,i);
  39. addch(PIPE);
  40. }
  41. }
  42. refresh();
  43. p = p->next;
  44. }
  45. attroff(COLOR_PAIR(2));
  46. }

代码总结

  1. #include <stdio.h>
  2. #include <curses.h>
  3. #include <signal.h>
  4. #include <sys/time.h>
  5. #include <stdlib.h>
  6. #define BIRD '@'
  7. #define BLANK ' '
  8. #define PIPE '+'
  9. /*定义关于管道的结构体*/
  10. typedef struct Pipe{
  11. int x;//列坐标
  12. int y;//横坐标
  13. struct Pipe *next;
  14. }Pipe_node, *Pipe_list;
  15. Pipe_list head, tail;
  16. void creat_list();//创建链表
  17. void show_pipe();//显示管道
  18. void clear_pipe();//清除管道
  19. void move_pipe();//移动管道
  20. int bird_y, bird_x;//小鸟坐标
  21. void show_bird();//显示小鸟
  22. void clear_bird();//清除小鸟
  23. void move_bird();//移动小鸟
  24. void init_curses();//curses库初始化
  25. int set_timer(int ms_t);//设置定时器--ms
  26. void handler(int sig);//信号处理函数
  27. int main(int argc, const char *argv[])
  28. {
  29. bird_y = 15;//
  30. bird_x = 10;//
  31. init_curses();
  32. signal(SIGALRM, handler);
  33. set_timer(500);//500ms
  34. srand(time(0));//随机种子
  35. creat_list();
  36. show_pipe();
  37. show_bird();
  38. move_bird();
  39. return 0;
  40. }
  41. void init_curses()//curses库初始化
  42. {
  43. initscr();//进入curses模式
  44. curs_set(0);//禁止光标显示
  45. noecho();//禁止输入字符显示
  46. keypad(stdscr,1);//启动功能按键
  47. start_color();//启动颜色机制
  48. init_pair(1,COLOR_WHITE, COLOR_RED);//小鸟颜色设置
  49. init_pair(2,COLOR_WHITE, COLOR_GREEN);//管道颜色设置
  50. }
  51. int set_timer(int ms_t)//设置定时器--ms
  52. {
  53. struct itimerval timer;
  54. long t_sec,t_usec;
  55. int ret;
  56. t_sec = ms_t / 1000; //s
  57. t_usec = (ms_t % 1000) * 1000;//us
  58. timer.it_value.tv_sec = t_sec;
  59. timer.it_value.tv_usec = t_usec;//首次启动定时值
  60. timer.it_interval.tv_sec = t_sec;
  61. timer.it_interval.tv_usec = t_usec;//定时时间间隔
  62. ret = setitimer(ITIMER_REAL, &timer, NULL);
  63. return ret;
  64. }
  65. void handler(int sig)
  66. {
  67. Pipe_list p, new;
  68. int i,j;
  69. /*小鸟下落*/
  70. clear_bird();
  71. bird_y++;
  72. show_bird();
  73. /*游戏结束判断*/
  74. if((char)inch() == PIPE)
  75. {
  76. set_timer(0);
  77. endwin();
  78. exit(1);
  79. }
  80. p = head->next;
  81. if(p->x == 0)
  82. {
  83. head->next = p->next;
  84. for(i = p->x; i < p->x+10; i++)
  85. {
  86. /*上半部分管道*/
  87. for(j=0; j<p->y; j++)
  88. {
  89. move(j,i);
  90. addch(BLANK);
  91. }
  92. /*下半部分管道创建*/
  93. for(j = p->y+5; j < 25; j++)
  94. {
  95. move(j,i);
  96. addch(BLANK);
  97. }
  98. refresh();
  99. }
  100. free(p);
  101. new = (Pipe_list)malloc(sizeof(Pipe_node));
  102. new->x = tail->x + 20;
  103. new->y = rand() % 11 + 5;
  104. new->next = NULL;
  105. tail->next = new;
  106. tail = new;
  107. }
  108. /*管道移动*/
  109. clear_pipe();
  110. move_pipe();
  111. show_pipe();
  112. }
  113. void show_bird()//显示小鸟
  114. {
  115. attron(COLOR_PAIR(1));
  116. move(bird_y,bird_x);
  117. addch(BIRD);
  118. refresh();
  119. attroff(COLOR_PAIR(1));
  120. }
  121. void clear_bird()//清除小鸟
  122. {
  123. move(bird_y,bird_x);
  124. addch(BLANK);
  125. refresh();
  126. }
  127. void move_bird()//移动小鸟
  128. {
  129. char key;
  130. while(1)
  131. {
  132. key = getch();
  133. if(key == ' ')
  134. {
  135. clear_bird();
  136. bird_y--;
  137. show_bird();
  138. /*游戏结束判断*/
  139. if((char)inch() == PIPE)
  140. {
  141. set_timer(0);
  142. endwin();
  143. exit(1);
  144. }
  145. }
  146. }
  147. }
  148. void creat_list()//创建链表
  149. {
  150. int i;
  151. Pipe_list p, new;
  152. head = (Pipe_list)malloc(sizeof(Pipe_node));
  153. head->next = NULL;
  154. p = head;
  155. for(i = 0; i < 5; i++)
  156. {
  157. new = (Pipe_list)malloc(sizeof(Pipe_node));
  158. new->x = (i + 1) * 20;
  159. new->y = rand() % 11 + 5; // (5-15行)
  160. new->next = NULL;
  161. p->next = new;
  162. p = new;
  163. }
  164. tail = p;
  165. }
  166. void show_pipe()//显示管道
  167. {
  168. Pipe_list p;
  169. int i,j;
  170. p = head->next;
  171. attron(COLOR_PAIR(2));
  172. while(p)
  173. {
  174. for(i = p->x; i < p->x+10; i++)
  175. {
  176. /*上半部分管道*/
  177. for(j=0; j<p->y; j++)
  178. {
  179. move(j,i);
  180. addch(PIPE);
  181. }
  182. /*下半部分管道创建*/
  183. for(j = p->y+5; j < 25; j++)
  184. {
  185. move(j,i);
  186. addch(PIPE);
  187. }
  188. }
  189. refresh();
  190. p = p->next;
  191. }
  192. attroff(COLOR_PAIR(2));
  193. }
  194. void clear_pipe()//清除管道
  195. {
  196. Pipe_list p;
  197. int i,j;
  198. p = head->next;
  199. while(p)
  200. {
  201. for(i = p->x; i < p->x+10; i++)
  202. {
  203. /*上半部分管道*/
  204. for(j=0; j<p->y; j++)
  205. {
  206. move(j,i);
  207. addch(BLANK);
  208. }
  209. /*下半部分管道创建*/
  210. for(j = p->y+5; j < 25; j++)
  211. {
  212. move(j,i);
  213. addch(BLANK);
  214. }
  215. }
  216. refresh();
  217. p = p->next;
  218. }
  219. }
  220. void move_pipe()//移动管道
  221. {
  222. Pipe_list p;
  223. p = head->next;
  224. while(p)
  225. {
  226. p->x--;
  227. p = p->next;
  228. }
  229. }

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

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

闽ICP备14008679号