Usa el lenguaje C para desarrollar el juego de entrada FlappyBird

prefacio

"flappy bird" es un trabajo desarrollado por Dong Nguyen, un desarrollador de juegos independiente de Vietnam. El juego se lanzó el 24 de mayo de 2013 y de repente se hizo popular en febrero de 2014. En febrero de 2014, el propio desarrollador eliminó "Flappy Bird" de las tiendas de aplicaciones de Apple y Google. En agosto de 2014, regresó oficialmente a la tienda de aplicaciones y se unió oficialmente al modo multijugador tan esperado por los fanáticos de Flappy. En el juego, el jugador debe controlar un pájaro para cruzar obstáculos formados por tuberías de agua de varias longitudes.

Es posible a través del desarrollo de juegos.

1) Mostrar obstáculos moviéndose de derecha a izquierda en la ventana del juego, mostrando tres paredes de pilares;

2) El usuario usa la barra espaciadora para controlar que el ave se mueva hacia arriba, sujeto a no golpear el obstáculo, es decir, necesita pasar por el hueco de la pared del pilar.

Bien, asegúrese de que el tamaño del espacio entre los obstáculos generados aleatoriamente sea suficiente para que pase el ave;

3) En ausencia de la operación del botón del usuario, el ave caerá sola bajo la influencia de la gravedad;

4) Realizar la detección de colisión entre el ave y el obstáculo, si no hay colisión suma 1 punto al jugador.

5) Si el ave golpea un obstáculo o excede los límites superior e inferior de la pantalla del juego, el juego termina.

Usa la barra espaciadora para controlar el movimiento del pájaro hacia arriba. Sin la operación clave del usuario, el pájaro caerá por sí mismo bajo la influencia de la gravedad. Si el ave golpea un obstáculo o excede los límites superior e inferior de la pantalla del juego, el juego termina.

imprimir límites superior e inferior

Posicionamiento del cursor en entorno Linux

Aprenda a colocar el cursor en Linuxel entorno , imprima diferentes contenidos en diferentes posiciones de la pantalla.

El formato del informe del cursor es: 0x1B [coordenada de fila; coordenada de columna].

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

Posicionamiento del cursor en entorno Windows

En Windowsel entorno , el método de posicionamiento del cursor es diferente. Importe el archivo de encabezado windows.h, y las estructuras o funciones utilizadas a continuación existen en este archivo de encabezado.

Primero, debe usar la COORDestructura , que se define como,

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

Luego GetStdHandle()obtenga HANDLE;

  1. HANDLE hp = GetStdHandle(STD_OUTPUT_HANDLE);

Finalmente SetConsoleCursorPosition()establezca .

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

Ahora, podemos imprimir la salida en diferentes ubicaciones en diferentes entornos.

el código

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

}

 


 

pájaro de impresión

el código

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

}

 


 

movimiento de aves

el código

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


 


 

impresión de la pared

Usamos una lista enlazada para almacenar paredes. Una lista enlazada es una estructura de datos de uso común que consta de varios nodos. Cada nodo incluye dos partes: una es el campo de datos para almacenar elementos de datos y la otra es para almacenar el siguiente nodo. El campo de puntero de la dirección.

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

Aquí debemos prestar atención al ciclo de vida de la variable, si la función almacena la dirección de memoria de la variable en el área de la pila, cuando finalice la función que crea la variable, se liberará la dirección creada en el área de la pila.

Por lo tanto, necesitamos aplicar el nodo en el área del montón.En lenguaje C, podemos aplicar para el área del montón a través de malloc()la función , por ejemplo.

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

Cuando no necesite usar la variable, use free()la función para liberar su espacio de direcciones.

  1. free(wall);

La línea 1 y BOOTEMla línea son los límites superior e inferior, y las paredes se imprimen a partir de la línea 2, donde las paredes superior e inferior están separadas por líneas BLANK. DISTANCEes la distancia entre dos paredes adyacentes, WIDTHy es el ancho de la pared.

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

El estilo de la pared es,

  1. prtNode(p, "*");

el código

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

}

 


 

detectar colisión

Cuando justHead()la función no detecta una colisión, devuelve 0, y cuando detecta una colisión, devuelve 1.

Cuando el pájaro choca con los límites superior e inferior,

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

Cuando el pájaro choca contra la pared,

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

el código

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

}

 

 Ejercicios de práctica del pájaro Flappy

el código

#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行显示分数
}

Finalmente, el juego se puede completar en el entorno liunx.

Supongo que te gusta

Origin blog.csdn.net/qq_64691289/article/details/127680432
Recomendado
Clasificación