项目实战:基于Linux的Flappy bird游戏开发

一、项目介绍

项目总结

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

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

        3.管道自动左移和创建

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

知识储备

        1.C语言

        2.数据结构-链表

        3.Ncurses库

        4.信号机制

二、Ncurses库介绍

         Ncurses是最早的System V Release 4.0 (SVr4)中 curses的一个克隆和升级。这是一个可自由配置的库,完全兼容旧版本curses。

        Ncurses构成了一个工作在底层终端代码之上的封装,并向用户提供了一个灵活高效的API(Application Programming Interface 应用程序接口)。它提供了创建窗口界面,移动光标,产生颜色,处理键盘按键等功能。使程序员编写应用程序不需要关心那些底层的终端操作。

        简而言之,它是一个管理应用程序在字符终端显示的函数库。

Ncurses库函数

注:安装命令: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); 

    关闭属性设置

Ncurses库安装

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

        注:为了能够使用Ncurses库,必须在源程序中将#include<curses.h>包括进来,而且在编译的需要与它链接起来. 在gcc中可以使用参数-lncurses进行编译。

编译gcc surses.c -o  curses -lncurses

三、信号

        在Linux中,软中断信号(signal,简称为信号)是在软件层次上对中断的一种模拟,用来通

知进程发生了异步事件。内核可以因为内部事件而给进程发送信号,通知进程发生了某个事件。

信号响应的方式:

        1.忽略信号,即对信号不做任何处理;

        2.捕捉信号,即信号发生时执行用户自定义的信号处理函数。

        3.执行缺省操作,Linux对每种信号都规定了默认操作。

设置信号相应方式-signal

#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 代表忽略信号;
        指定的信号处理函数代表捕捉方式
signal函数-示例
// 头文件省略
void handler (int signo) {
        printf(“HELLO!\n”);
}
int  main() {
        signal(SIGINT, handler);
        while ( 1 ) ;
        return 0;
}
设置定时器
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。

四、项目功能实现

项目安排

阶段1:初始化工作,小鸟功能实现

阶段2:管道功能实现

        1.创建链表

        2.显示管道

        3.清除管道

        4.移动管道

阶段3:完善代码,进行项目总结

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

        2.循环创建管道

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

代码功能

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/907092.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

nginx上传文件超过限制大小、响应超时、反向代理请求超时等问题解决

1、文件大小超过限制 相关配置&#xff1a; client_max_body_size&#xff1a; Syntax:client_max_body_size size;Default:client_max_body_size 1m;Context:http, server, location 2、连接超时: proxy_read_timeout&#xff1a; Syntax:proxy_read_timeout time;Default…

C++ --- 多线程的使用

目录 一.什么是线程&#xff1f; 线程的特点&#xff1a; 线程的组成&#xff1a; 二.什么是进程&#xff1f; 进程的特点&#xff1a; 进程的组成&#xff1a; 三.线程与进程的关系&#xff1a; 四.C的Thread方法的使用&#xff1a; 1.创建线程&#xff1a; 2.join(…

基于Spring Boot的医疗陪护系统设计与实现(源码+定制+开发)病患陪护管理平台、医疗服务管理系统、医疗陪护信息平台

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

ViT面试知识点

文章目录 VITCLIPSAMYOLO系列问题 VIT 介绍一下Visual Transformer&#xff1f; 介绍一下自注意力机制&#xff1f; 介绍一下VIT的输出方式 介绍一下VIT做分割任务 VIT是将NLP的transformer迁移到cv领域&#xff0c;他的整个流程大概如下&#xff1a;将一张图片切成很多个pat…

【Comsol教程】计算流道中的流量

在进行微流控方面的仿真的时候可能需要计算某一流道中流量的大小&#xff0c;下面展示如何计算。 流量分为质量流量和体积流量&#xff0c;我们常采用体积流量。在COMSOL中有两种方法计算&#xff0c; 1.使用Comsol内置的函数 这里我使用的是蠕动流模块【spf】,定义了3个开放边…

LeetCode 3226. 使两个整数相等的位更改次数

. - 力扣&#xff08;LeetCode&#xff09; 题目 给你两个正整数 n 和 k。你可以选择 n 的 二进制表示 中任意一个值为 1 的位&#xff0c;并将其改为 0。 返回使得 n 等于 k 所需要的更改次数。如果无法实现&#xff0c;返回 -1。 示例 1&#xff1a; 输入&#xff1a; n …

项目升级到.Net8.0 Autofac引发诡异的问题

前两天把项目升级到.Net8.0了&#xff0c;把.Net框架升级了&#xff0c;其他一些第三方库升级了一部分&#xff0c;升级完以后项目跑不起来了&#xff0c;报如下错误&#xff1a; An unhandled exception occurred while processing the request. DependencyResolutionExcepti…

RabbitMQ 七种工作模式介绍

目录 1.简单模式队列 2.WorkQueue(⼯作队列) 3 Publish/Subscribe(发布/订阅) 4 Routing(路由模式) 5.Topics(通配符模式) 6 RPC(RPC通信) 7 Publisher Confirms(发布确认) RabbitMQ 共提供了7种⼯作模式供我们进⾏消息传递,接下来一一介绍它的实现与目的 1.简单模式队列…

自动化测试类型与持续集成频率的关系

持续集成是敏捷开发的一个重要实践&#xff0c;可是究竟多频繁的集成才算“持续”集成&#xff1f; 一般来说&#xff0c;持续集成有3种常见的集成频率&#xff0c;分别是每分钟集成、每天集成和每迭代集成。项目组应当以怎样的频率进行集成&#xff0c;这取决于测试策略&…

操作系统期中复习2-4单元

Chapter-2 第一个图形界面——Xerox Alto 早期操作系统&#xff1a;规模小&#xff0c;简单&#xff0c;功能有限&#xff0c;无结构(简单结构)。&#xff08;MS-DOS,早期UNIX&#xff09; 层次结构&#xff1a;最底层为硬件&#xff0c;最高层为用户层&#xff0c;自下而上构…

2-141 怎么实现ROI-CS压缩感知核磁成像

怎么实现ROI-CS压缩感知核磁成像&#xff0c;这个案例告诉你。基于matlab的ROI-CS压缩感知核磁成像。ROI指在图像中预先定义的特定区域或区域集合&#xff0c;选择感兴趣的区域&#xff0c;通过减少信号重建所需的数据来缩短信号采样时间&#xff0c;减少计算量&#xff0c;并在…

Android中同步屏障(Sync Barrier)介绍

在 Android 中&#xff0c;“同步屏障”&#xff08;Sync Barrier&#xff09;是 MessageQueue 中的一种机制&#xff0c;允许系统临时忽略同步消息&#xff0c;以便优先处理异步消息。这在需要快速响应的任务&#xff08;如触摸事件和动画更新&#xff09;中尤为重要。 在 An…

【tomcat系列漏洞利用】

Tomcat 服务器是一个开源的轻量级Web应用服务器&#xff0c;在中小型系统和并发量小的场合下被普遍使用。主要组件&#xff1a;服务器Server&#xff0c;服务Service&#xff0c;连接器Connector、容器Container。连接器Connector和容器Container是Tomcat的核心。一个Container…

【压力测试】如何确定系统最大并发用户数?

一、明确测试目的与了解需求 明确测试目的&#xff1a;首先需要明确测试的目的&#xff0c;即为什么要确定系统的最大并发用户数。这通常与业务需求、系统预期的最大用户负载以及系统的稳定性要求相关。 了解业务需求&#xff1a;深入了解系统的业务特性&#xff0c;包括用户行…

深入理解Redis的四种模式

Redis是一个内存数据存储系统&#xff0c;支持多种不同的部署模式。以下是Redis的四种主要部署模式。 1、单机模式 单机模式是最简单的部署模式&#xff0c;Redis将数据存储在单个节点上。这个节点包括一个Redis进程和一个持久化存储。单机模式非常适合小型应用程序或者开发和…

【多态】析构函数的重写

析构函数的重写&#xff08;面试常见题&#xff09; 基类的析构函数为虚函数&#xff0c;此时派生类析构函数只要定义&#xff0c;⽆论是否加virtual关键字&#xff0c;都与基类的析构函数构成重写。 虽然基类与派⽣类析构函数名字不同看起来不符合重写的规则&#xff0c;实际…

合并区间 leetcode56

合并区间leetcode 目录一、题目二、踩坑过程三、上官方解答四、含泪体会彩蛋 目录 一、题目 二、踩坑过程 一开始想使用一个数组来标记区间&#xff0c;但是仔细想不好实现&#xff0c;单纯把区间里出现的设置为1&#xff0c;不好体现重叠的概念&#xff0c;如果使用三种状态…

机器人领域中的scaling law:通过复现斯坦福机器人UMI——探讨数据规模化定律(含UMI的复现关键)

前言 在24年10.26/10.27两天&#xff0c;我司七月在线举办的七月大模型机器人线下营时&#xff0c;我们带着大家一步步复现UMI「关于什么是UMI&#xff0c;详见此文&#xff1a;UMI——斯坦福刷盘机器人&#xff1a;从手持夹持器到动作预测Diffusion Policy(含代码解读)」&…

MybatisPlus入门(六)MybatisPlus-空值处理

一、MybatisPlus-空值处理 1.1&#xff09;问题引入&#xff1a; 在查询中遇到如下情况&#xff0c;有部分筛选条件没有值&#xff0c;如商品价格有最大值和最小值&#xff0c;商品价格部分时候没有值。 1.2&#xff09;解决办法&#xff1a; 步骤一&#xff1a;新建查询实体…

3.2链路聚合

1、链路聚合手动配置 将交换机S1、S2的GE0/0/1、GE0/0/2口来进行链路聚合。 交换机S1配置命令; [S1]interface eth-trunk 1 [S1-Eth-Trunk1]trunkport GigabitEthernet 0/0/1 to 0/0/2 [S1-Eth-Trunk1]port link-type trunk [S1-Eth-Trunk1]port trunk allow-pass vlan all …