Use C language to develop the entry game FlappyBird

foreword

"flappy bird" is a work developed by Dong Nguyen, an independent game developer from Vietnam. The game was launched on May 24, 2013, and suddenly became popular in February 2014. In February 2014, "Flappy Bird" was removed from the Apple and Google app stores by the developer himself. In August 2014, it officially returned to the APP Store and officially joined the long-awaited multiplayer mode of Flappy fans. In the game, the player must control a bird to cross obstacles made up of water pipes of various lengths.

It is possible through game development

1) Display obstacles moving from right to left in the game window, displaying three pillar walls;

2) The user uses the space bar to control the bird to move upward, subject to not hitting the obstacle, that is, it needs to pass through the gap of the pillar wall

OK, make sure that the size of the gap between the randomly generated obstacles is enough for the bird to pass through;

3) In the absence of user button operation, the bird will fall by itself under the influence of gravity;

4) Carry out the collision detection between the bird and the obstacle, if there is no collision, add 1 point to the player.

5) If the bird hits an obstacle or exceeds the upper and lower boundaries of the game screen, the game ends.

Use the space bar to control the bird to move up. Without the user's key operation, the bird will fall by itself under the influence of gravity. If the bird hits an obstacle or exceeds the upper and lower boundaries of the game screen, the game is over.

print upper and lower bounds

Cursor positioning in Linux environment

Learn to position the cursor in Linuxthe environment , print out different content at different positions on the screen.

The format of the cursor report is: 0x1B [row coordinate; column coordinate].

  1. //x 为行坐标 ,y 为列坐标
  2. printf ( "%c[%d;%df" ,0x1B,y,x);

Cursor positioning in Windows environment

In Windowsthe environment , the method of cursor positioning is different. Import the windows.h header file, and the structures or functions used below all exist in this header file.

First, you need to use the COORDstructure , which is defined as,

  1. typedef struct _COORD {
  2. SHORT X; // horizontal coordinate
  3. SHORT Y; // vertical coordinate
  4. } COORD;

Then GetStdHandle()get HANDLE;

  1. HANDLE hp = GetStdHandle(STD_OUTPUT_HANDLE);

Finally SetConsoleCursorPosition()set .

  1. //变量 pos 为 COORD 结构体对象
  2. SetConsoleCursorPosition(hp, pos);

Now, we can print output in different locations in different environments.

the code

#include <stdio.h>
#define BOOTEM 26 //下边界的高度
#define DISTANCE 10 //两个墙体之间的距离
#define WIDTH 5 //墙体宽度
#define BLANK 10 //上下墙体之间的距离
/**********Begin**********/
//光标定位
void gotoxy(int x, int y){

     printf("%c[%d;%df", 0x1B, y, x);
}

//函数功能:显示上下边界和分数
void begin(){

    system("clear");
    //打印上边界
    gotoxy(0, 0);
    printf("\n==========================\n");
    //打印下边界
    gotoxy(0, BOOTEM);
    printf("\n==========================");

}
/**********End**********/

int main()
{

	begin();

	return 0;

}

 


 

print bird

the code

#include "./head.h"
typedef struct COORD {
short X; // horizontal coordinate
short Y; // vertical coordinate
} COORD;


typedef struct bird
{
    COORD pos;
    int score;
} BIRD;

//函数功能:显示小鸟
void prtBird(BIRD *bird){
    /**********Begin**********/
    void prtBird(BIRD *bird)
   {
    gotoxy(bird->pos.X, bird->pos.Y);
    printf("O^^0");
    fflush(stdout);
   }

    /**********End**********/
    //linux环境printf频繁偶尔会在缓存中不会立即打印,fflush函数可以清空缓存并立即打印
    fflush(stdout);
}

int main()
{
	BIRD bird = {
    
    {
    
    10, 13}, 0};//小鸟的初始位置
	begin();
	prtBird(&bird);
	return 0;

}

 


 

bird movement

the code

#include "./head.h"

//EVALUATING 宏定义  1 为 评测模式  0 为命令行模式
#define EVALUATING 1

//函数功能:检测键盘输入
//有输入值时,该函数返回 1 ,没有输入时,该函数返回 0
int kbhit()
{
	struct termios oldt, newt; 
	int ch; 
	int oldf; 
	tcgetattr(STDIN_FILENO, &oldt); 
	newt = oldt; 
	newt.c_lflag &= ~(ICANON | ECHO); 
	tcsetattr(STDIN_FILENO, TCSANOW, &newt); 
	oldf = fcntl(STDIN_FILENO, F_GETFL, 0); 
	fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); 
	ch = getchar(); 
	tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 
	fcntl(STDIN_FILENO, F_SETFL, oldf); 
	if(ch != EOF) { 
    	ungetc(ch, stdin); 
    	return 1; 
	} 
	return 0; 
}
//函数功能:移动小鸟
void moveBird(BIRD *bird){

	/**********Begin**********/
     /**********Begin**********/
      char ch;  
    //下面两行代码  作用是覆盖上次打印出来的小鸟位置,如果不加的话 会显示残影  
    gotoxy(bird->pos.X, bird->pos.Y);  
    printf("     ");
    if (kbhit()) {  
        ch = getchar();  
        if (ch == ' ') {  
            bird->pos.Y--;//向上移动  
        }  
    }  
    else {  
        bird->pos.Y++;//向下移动  
    }
  

	/**********End**********/
	
}

int main()
{
	begin();
	BIRD bird = {
    
    {
    
    10, 13}, 0};//小鸟的初始位置

	//EVALUATING 宏定义 1 为评测模式  0 为命令行模式
 	if(EVALUATING){
		int cnt=3;// 请忽略 辅助评测 无实际意义 
    	while(cnt--){

			prtBird(&bird);
			usleep(400000);
			moveBird(&bird);
   		}
	}else{
		while(1){

		prtBird(&bird);
		usleep(400000);
		moveBird(&bird);
    	}
	}

	return 0;
}


 


 

print wall

We use a linked list to store walls. A linked list is a commonly used data structure consisting of several nodes. Each node includes two parts: one is the data field for storing data elements, and the other is for storing the next node. The pointer field of the address.

  1. typedef struct wall{
  2. COORD pos;
  3. struct wall *next;//指向下一链表
  4. }WALL;

Here we should pay attention to the life cycle of the variable. If the function stores the variable memory address in the stack area, when the function that creates the variable ends, the address created in the stack area will be released.

Therefore, we need to apply the node in the heap area. In C language, we can apply for the heap area through malloc()the function , for example.

  1. WALL *wall = (WALL *)malloc(sizeof(WALL));

When the variable does not need to be used, use free()the function to release its address space.

  1. free(wall);

Line 1 and BOOTEMline are the upper and lower boundaries, and walls are printed starting from line 2, where the upper and lower walls are separated by lines BLANK. DISTANCEis the distance between two adjacent walls, WIDTHand is the width of the wall.

  1. #define BOOTEM 26 //下边界的高度
  2. #define DISTANCE 10 //两个墙体之间的距离
  3. #define WIDTH 5 //墙体宽度
  4. #define BLANK 10 //上下墙体之间的距离

The wall style is,

  1. prtNode(p, "*");

the code

#include "./head.h"

//EVALUATING 宏定义  1 为 评测模式  0 为命令行模式
#define EVALUATING 1

typedef struct wall{
	COORD pos;
	struct wall *next;
}WALL;

/**********Begin**********/
 //创建节点
WALL *createWall(int x, int y){
    //首先生成一个节点
    WALL *wall = (WALL *)malloc(sizeof(WALL));
    if(wall == NULL) return NULL;
    wall->pos.X = x;
    wall->pos.Y = y;
    wall->next = NULL;
    return wall;
}
//遍历链表
WALL *lastNode(WALL *wall){
    WALL *p = wall;
    if(wall == NULL) return NULL;
    while(p->next){
        p = p->next;
    }
    return p;
}
//创建链表
WALL *createLink(){
    //生成一个随机数,作为上半部分墙体的高度
    srand(0);
    //生成随机值,当rd等于0或等于1时,给其赋值2
    int rd = rand() % (BOOTEM / 2);
    if(rd == 1||rd==0) rd = 2;
    //初始化一个节点
    WALL *wall = createWall(20, rd);
    WALL *temp, *p;
    for(int i = 1; i <= 2; i ++){
        //生成随机值,当rd等于0或等于1时,给其赋值2
        rd = rand() % (BOOTEM / 2);
        if(rd == 1||rd==0) rd = 2;
        p = lastNode(wall);//找到了链表的尾节点
        //创建节点
        temp = createWall(p->pos.X + DISTANCE + WIDTH * 2, rd);
        p->next = temp;//尾插法
    }
    return wall;
}
//销毁链表
void deleteLink(WALL *wall){
    WALL *p, *q;
    p = q = wall;
    if(wall != NULL){
        while(p->next != NULL){
            q = p->next;
            p->next = q->next;
            free(q);
        }
    }
    free(wall);//free(p);
}
//打印单个墙体
void prtNode(WALL *node, char ch[]){
    if(node == NULL) return ;
    int i, j;
    //上半部分墙体,第一行是上边界,从第2行开始打印
    for(i = 2; i <= node->pos.Y; i ++){
        gotoxy(node->pos.X, i);
        for(j = 1; j <= WIDTH; j ++){
            printf("%s", ch);
        }
    }
    //下半部分墙体 第BOOTEM行是下边界,此处 BOOTEM -1 避免墙体覆盖边界
    for(i = node->pos.Y + BLANK; i < BOOTEM - 1; i ++){
        gotoxy(node->pos.X, i);
        for(j = 1; j <= WIDTH; j ++){
            printf("%s", ch);
        }
    }
}
//打印整个墙体
void prtWall(WALL *wall){
    if(wall == NULL) return ;
    WALL *p = wall;
    while(p){
        prtNode(p, "*");
        p = p->next;
    }
}

int main()
{
	begin();
	BIRD bird = {
    
    {
    
    10, 13}, 0};//小鸟的初始位置
	WALL *wall= createLink();//链表生成墙体
	prtWall(wall);
    
	//EVALUATING 宏定义 1 为评测模式  0 为命令行模式
 	if(EVALUATING){
		int cnt=3;// 请忽略 辅助评测 无实际意义 
    	while(cnt--){
			prtBird(&bird);
			usleep(400000);
			moveBird(&bird);
   		}
	}else{
		while(1){
			prtBird(&bird);
			usleep(400000);
			moveBird(&bird);
    	}
	}
    deleteLink(wall);
	return 0;

}

 


 

detect collision

When justHead()the function does not detect a collision, it returns 0, and when a collision is detected, it returns 1.

When the bird collides with the upper and lower boundaries,

  1. //与上下边界发生碰撞
  2. if(bird->pos.Y <= 0 || bird->pos.Y >= BOOTEM)

When the bird collides with the wall,

  1. //小鸟与墙体发生碰撞
  2. bird->pos.X >= wall->pos.X && bird->pos.X <= wall->pos.X+ WIDTH

the code

#include "./head.h"

//EVALUATING 宏定义  1 为 评测模式  0 为命令行模式
#define EVALUATING 1



//监测小鸟碰撞
int justHead(WALL *wall, BIRD *bird){
    if(wall == NULL) return -1;
    //与上下边界发生碰撞
    if(bird->pos.Y <= 0 || bird->pos.Y >= BOOTEM) return 1;
    //小鸟与墙体发生碰撞
    if(bird->pos.X  >= wall->pos.X && bird->pos.X <= wall->pos.X+ WIDTH){
        if(bird->pos.Y <= wall->pos.Y || bird->pos.Y >= wall->pos.Y + BLANK){
            return 1;
        }
    }
    return 0;
} 

int main()
{
	begin();
	BIRD bird = {
    
    {
    
    10, 13}, 0};//小鸟的初始位置
	WALL *wall= createLink();//链表生成墙体
	prtWall(wall);
	//EVALUATING 宏定义 1 为评测模式  0 为命令行模式
 	if(EVALUATING){
		int cnt=3;// 请忽略 辅助评测 无实际意义 
    	while(cnt--&&justHead(wall,&bird)==0){
			prtBird(&bird);
			usleep(400000);
			moveBird(&bird);
			wall = moveWall(wall,&bird);
   		}
	}else{
		while(justHead(wall,&bird)==0){

			prtBird(&bird);
			usleep(400000);
			moveBird(&bird);
            wall = moveWall(wall,&bird);
    	}
	}
    deleteLink(wall);
	return 0;

}

 

 Flappy bird practice exercises

the code

#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <time.h>
#include <termios.h>
#include <unistd.h> 
#include <fcntl.h>
#include <stdbool.h>
#define DIS 22
#define BLAN 9   //上下两部分柱子墙之间的缝隙
typedef struct COORD {
short X; // horizontal coordinate
short Y; // vertical coordinate
} COORD;
typedef struct bird
{
    COORD pos;
    int score;
} BIRD;
//bool SetConsoleColor(unsigned int wAttributes); //设置颜色
void Gotoxy(int x, int y);//定位光标
bool SetConsoleColor(int back,int front); //设置颜色
void CheckWall(COORD wall[]);//显示柱子墙体
void PrtBird(BIRD *bird);//显示小鸟
int CheckWin(COORD *wall, BIRD *bird);//检测小鸟是否碰到墙体或者超出上下边界
void Begin(BIRD *bird);//显示上下边界和分数
int SelectMode();  //选择模式
int kbhit()
{
    struct termios oldt, newt; 
    int ch; 
    int oldf; 
    tcgetattr(STDIN_FILENO, &oldt); 
    newt = oldt; 
    newt.c_lflag &= ~(ICANON | ECHO); 
    tcsetattr(STDIN_FILENO, TCSANOW, &newt); 
    oldf = fcntl(STDIN_FILENO, F_GETFL, 0); 
    fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); 
    ch = getchar(); 
    tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 
    fcntl(STDIN_FILENO, F_SETFL, oldf); 
    printf("%c\n",ch);
    if(ch != EOF) { 
        ungetc(ch, stdin); 
        return 1; 
    } 
    return 0; 
}
//主函数
int main(int argc, char* argv[])
{
    BIRD bird = {
    
    {
    
    20, 13}, 0};//小鸟的初始位置
    COORD wall[3] = {
    
    {
    
    40, 10},{
    
    60, 6},{
    
    80, 8}}; //柱子的初始位置和高度
    int i;
    char ch;
    int gameMode = 1;
    gameMode = SelectMode();
    if(gameMode==1) //用于评测
    {
        int count = 0;
        while (count < 60)      //游戏循环执行
        {        
          Begin(&bird); //清屏并显示上下边界和分数
          PrtBird(&bird);//显示小鸟
          CheckWall(wall);//显示柱子墙
          ch = getchar();//输入的字符存入ch
          printf("%c",ch);
          if (ch == 'u')//输入的是u
            {
                bird.pos.Y -= 1; //小鸟向上移动一格
            }
            if (ch == 'd')//输入的是d
            {
                bird.pos.Y += 1; //小鸟向下移动一格
          }
          for (i=0; i<3; ++i)
          {
            wall[i].X--; //柱子墙向左移动一格
          }
         if(CheckWin(wall, &bird)==0)
            {
                printf("Bird Lose!");
                return 0;
            }
            count++;
        }
        printf("Bird Win!");
        return 0;
    }
    while (CheckWin(wall, &bird))
    {
        Begin(&bird); //清屏并显示上下边界和分数
        PrtBird(&bird);//显示小鸟
        CheckWall(wall);//显示柱子墙
        usleep(400000);
        if (kbhit()) //检测到有键盘输入
        {
            ch = getchar();//输入的字符存入ch
            printf("%c",ch);
            if (ch == ' ')//输入的是空格
            {
                bird.pos.Y -= 1; //小鸟向上移动一格
            }
        }        
        else //未检测到键盘输入
        {
            bird.pos.Y += 1;//小鸟向下移动一格
        }
        for (i=0; i<3; ++i)
        {
            wall[i].X--; //柱子墙向做移动一格
        }
    }
    return 0;
}
int SelectMode()
{
    printf(" 请选择模式:\n 1.评测模式\n 2.自由体验游戏\n");
    int mode;
    while(scanf("%d", &mode))
    {
        if (mode != 1 && mode != 2)
        {
            printf(" 输入数据有误,请重新输入!\n\n 请选择模式:\n 1.评测模式\n 2.自由体验游戏\n");
            continue;
        }
        else return mode;
    }
    return 1;
}
//函数功能:定位光标
void Gotoxy(int x, int y)//void Gotoxy(COORD pos)
{
printf ( "%c[%d;%df" ,0x1B,y,x);
}
//函数功能:设置颜色
bool SetConsoleColor(int back,int front)
{
    printf("\033[%dm",(front));
    return TRUE; 
}
//函数功能:显示柱子墙体
void CheckWall(COORD wall[])
{
    int i;
    srand(time(0));
    COORD temp = {wall[2].X + DIS, rand() % 13 + 5};//随机产生一个新的柱子
    if (wall[0].X < 10)  //超出预设的左边界
    {    //最左侧的柱子墙消失,第二个柱子变成第一个,第三个柱子变成第二个,新产生的柱子变成第三个
        /********** Begin **********/
        wall[0] = wall[1];//最左侧的柱子墙消失,第二个柱子变成第一个
        wall[1] = wall[2];//第三个柱子变成第二个
        wall[2] = temp;   //新产生的柱子变成第三个
        /********** End **********/
    }
    SetConsoleColor(40,31); //设置黑色背景,亮红色前景
    for (i=0; i<3; ++i)//每次显示三个柱子墙
    {
        //显示上半部分柱子墙
        temp.X = wall[i].X + 1;//向右缩进一格显示图案
        for (temp.Y=2; temp.Y<wall[i].Y; temp.Y++)//从第2行开始显示
        {
            Gotoxy(temp.X, temp.Y);
            printf("■■■■■■");
        }
        temp.X--;//向左移动一格显示图案
        Gotoxy(temp.X, temp.Y);
        printf("■■■■■■■■");
        //显示下半部分柱子墙
        temp.Y += BLAN;
        Gotoxy(temp.X, temp.Y);
        printf("■■■■■■■■");
        temp.X++; //向右缩进一格显示图案
        temp.Y++; //在下一行显示下面的图案
        for (; (temp.Y)<26; temp.Y++)//一直显示到第25行
        {
            Gotoxy(temp.X, temp.Y);
            printf("■■■■■■");
        }
    }
    Gotoxy(0, 26);
    printf("\n");//第1行显示分数
}
//函数功能:显示小鸟
void PrtBird(BIRD *bird)
{
    SetConsoleColor(40,33); //设置黑色背景,亮黄色前景
    Gotoxy(bird->pos.X, bird->pos.Y);
    printf("o->");
}
//函数功能:检测小鸟是否碰到墙体或者超出上下边界,是则返回0,否则分数加1并返回1
int CheckWin(COORD *wall, BIRD *bird)
{  
    /********** Begin **********/
    if (bird->pos.X >= wall->X) //小鸟的横坐标进入柱子坐标范围
    {
        if (bird->pos.Y <= wall->Y || bird->pos.Y >= wall->Y + BLAN) 
        {
            return 0; //小鸟的纵坐标碰到上下柱子,则返回0
        }
    }
    if (bird->pos.Y < 1 || bird->pos.Y > 26) 
    {
        return 0; //小鸟的位置超出上下边界,则返回0
    }
    (bird->score)++; //分数加1
    return 1;
    /********** End **********/
}
//函数功能:显示上下边界和分数
void Begin(BIRD *bird)
{
    system("clear");    
    Gotoxy(0, 26); //第26行显示下边界
printf("=========================================================="
"================================Bottom");
Gotoxy(0, 1); //第1行显示上边界
printf("=========================================================="
"================================Top");
    SetConsoleColor(40,33);//设置黑色背景,亮黄色前景
    printf("\n%4d", bird->score);//第1行显示分数
}

Finally, the game can be completed in the liunx environment

Guess you like

Origin blog.csdn.net/qq_64691289/article/details/127680432