C++练手小游戏2——简易版flappy bird

概述

程序运行时,通过空格键进行调整小球的上下跳跃,而障碍物的高度、宽度和速度均是随机的,每通过一个障碍物分数加1。但碰撞后游戏会暂停,直到按下“r”后游戏重新开始,分数也会同步清零。
在这里插入图片描述

实现效果和涉及的知识

本例子我们将模拟简易版的flappy bird。
例子中我们会学习EasyX图形库的文本相关库函数、随机数的生成和函数等。

文件结构

这个例子需要如下几个模块:小球,障碍物,得分计算,碰撞判断和游戏重启。代码的文件结构也是根据模块划分进行设置的。
在这里插入图片描述

具体代码

main.cpp

// 初版flappy bird

#include <graphics.h> // 绘图函数库,非cpp STL,是TurboC扩展库(EasyX)
#include <conio.h> // console input/output的简写,非cpp STL,定义了通过控制台进行输入输出的函数,如getch()
#include <stdio.h> // standard input&output:标准输入输出头文件,如scanf, printf
#include <iostream> // C++ STL 输入输出库

#include "ball_xy.h" // 计算小球的位置
#include "rect_xy.h" // 计算障碍物的位置
#include "ifcoll.h" // 判断小球和障碍物是否碰撞
#include "score.h" // 计算得分
#include "data.h" // 全局变量

using namespace std;

int main()
{
	float GraWid = 600, GraHei = 400, r = 10, g = 1;
	bool ifKBhit = false;
	ball_xy Ball_XY;
	rect_xy Rect_XY;
	ifcoll IfColl;

	Ball_XY.set_ball_envi(GraWid, GraHei, r, g);
	Rect_XY.set_rect_ab(GraWid, GraHei);
	initscore();

	initgraph(GraWid, GraHei);

	while (1)
	{
		while (1)
		{
			if (_kbhit()) // 注意这个函数,非常特殊:键盘敲击:true or 键盘不敲击:null(不是false)
				// 如果不加_kbhit(),console在每个while循环都会被_getch()卡住:等待使用者键盘输入。
			{
				char kbin = _getch(); // Get a character from the console
				if (kbin == ' ')
				{
					ifKBhit = true;
				}
			}

			cleardevice();

			Ball_XY.cal_ball_xy(ifKBhit);
			fillcircle(Ball_XY.get_ball_x(), Ball_XY.get_ball_y(), r);

			Rect_XY.cal_rect_para();
			fillrectangle(Rect_XY.get_rect_ax(), Rect_XY.get_rect_by(), Rect_XY.get_rect_cx(), Rect_XY.get_rect_dy());

			scorefun(Rect_XY.get_rect_ax(), GraWid); // 计算得分
			TCHAR s[20]; // 这里划重点:_UNICODE宏
			swprintf_s(s, _T("%d"), score);
			settextstyle(40, 0, _T("宋体")); // EasyX:The function is used to styling the current font
			outtextxy(50, 30, s); // EasyX:The function is used to output the string at the specified location.
			
			cout << score;
;
			IfColl.set_ifcoll_para(Ball_XY.get_ball_x(), Ball_XY.get_ball_y(), r, Rect_XY.get_rect_ax(), Rect_XY.get_rect_by(), Rect_XY.get_rect_cx(), Rect_XY.get_rect_dy());
			if (IfColl.cal_coll()) // 如果碰撞则退出当前循环
			{
				break;
			}

			Sleep(50);
			ifKBhit = false;
		}

		while (1) // 按‘r’重新开始游戏
		{
			if (_kbhit()) // 注意这个函数,非常特殊:键盘敲击:true or 键盘不敲击:null(不是false)     
			{
				char kbinRS = _getch();
				if (kbinRS == 'r') // 按‘r’重新开始游戏
				{
					Ball_XY.set_ball_envi(GraWid, GraHei, r, g);
					Rect_XY.set_rect_ab(GraWid, GraHei);

					initscore(); // 重新开始游戏时,分数应置零

					break;
				}
			}
			Sleep(50);
		}
		Sleep(1000);
	}

	closegraph();

	return 0;
}

ball_xy.h

#pragma once // 防止.h被重复包含

#ifndef BALL_XY_H_
#define BALL_XY_H_

class ball_xy
{
private:
	float x, y, vx, vy;
	float GraWid, GraHei, r, g;

public:
	ball_xy();
	~ball_xy();
	void set_ball_envi(float GraWid_FP, float GraHei_FP, float r_FP, float g_FP);
	void cal_ball_xy(bool ifKBhit);
	float get_ball_x();
	float get_ball_y();
};

#endif // !BALL_XY_H_

ball_xy.cpp

#include "ball_xy.h"

ball_xy::ball_xy()
{
	x = 0;
	y = 0;
	vx = 0;
	vy = 0;
	GraWid = 0;
	GraHei = 0;
	r = 0;
	g = 0;
}

ball_xy::~ball_xy()
{
}

void ball_xy::set_ball_envi(float GraWid_FP, float GraHei_FP, float r_FP, float g_FP)
{
	GraWid = GraWid_FP;
	GraHei = GraHei_FP;
	x = GraWid / 4;
	y = GraHei - r_FP;
	vx = 0;
	vy = 0;
	r = r_FP;
	g = g_FP;
}

void ball_xy::cal_ball_xy(bool ifKBhit)
{

	if (ifKBhit)
	{
		vy = -15;
		y = y + vy;
	}
	else if (y - r < 0)
	{
		vy = -vy;
		y = r;
	}
	else if (y + r >= GraHei)
	{
		vy = 0;
		y = GraHei - r;
	}
	else
	{
		vy = vy + g;
		y = y + vy;
	}
}

float ball_xy::get_ball_x()
{
	return x;
}

float ball_xy::get_ball_y()
{
	return y;
}

rect_xy.h

#pragma once

#ifndef RECT_H_
#define RECT_H_

class rect_xy
{
private:
	float rect_x, rect_L, rect_H, rect_vx, grawid, grahei;
	float ax, by, cx, dy;

public:
	rect_xy();
	~rect_xy();
	void set_rect_ab(float GraWid, float GraHei);
	void cal_rect_para();
	float get_rect_ax();
	float get_rect_by();
	float get_rect_cx();
	float get_rect_dy();
};

#endif // !RECT_H_

rect_xy.cpp

这里要说一下随机数的生成:

  1. void srand(unsigned seed);

srand被包含在stdlib.h内,用来指定种子seed的函数,若不显式调用则系统默认调用srand(1)来指定rand()的初始化种子。srand函数指定的种子会对应一个随机数系列,所以当你用srand指定一样的种子是,rand出来的随机数序列总是一样的。当然为了防止一样,一方面我们可用流逝的时间作为种子,即用time函数来获取系统时间,time函数会返回一个time_t的数据,表示1970年1月1日至今所经历的时间(以秒为单位),单位为妙,然后我们将这个time_t类型的数据转换为unsigned类型就可以srand成种子了,即srand((unsigned)time(0)。

  1. int rand(void);

rand()被包含在stdlib.h内,从srand(seed)中指定的seed为范围的开始,返回一个[seed,RAND_MAX)区间中的随机数值。总结来说,可以表示为:int num = rand() % n +a;其中的a是起始值,n-1+a是终止值,n是整数的范围。

  1. static time_t time(time_t * const _Time)

包含在ctime内,time(0)指函数返回当前时间,如果发生错误返回0。time(1)指函数返回当前时间,如果发生错误返回1.

#include "rect_xy.h"
#include <stdlib.h>
#include <ctime>

rect_xy::rect_xy()
{
	float rect_x = 0, rect_L = 15, rect_H = 0, rect_vx = -5, grawid = 0, grahei = 0;
	float ax = 0, by = 0, cx = 0, dy = 0;
}

rect_xy::~rect_xy()
{
}

void rect_xy::set_rect_ab(float GraWid, float GraHei)
{
	grawid = GraWid;
	grahei = GraHei;
	rect_x = GraWid;
	rect_L = 15;
	rect_H = 100;
	rect_vx = -10;


}

void rect_xy::cal_rect_para()
{
	if (rect_x > 0 - rect_L && rect_x <= grawid)
	{
		rect_x = rect_x + rect_vx;
		ax = rect_x;
		by = grahei - rect_H;
		cx = rect_x + rect_L;
		dy = grahei;
	}
	else
	{
		rect_x = grawid;
		srand(time(0)); // int rand(void):返回当前时间,如果发生错误返回零。
		rect_vx = (5 + rand() % 20) * -1;
		rect_H = (20 + rand() % 250);
		rect_L = (15 + rand() % 100);

		ax = rect_x;
		by = grahei - rect_H;
		cx = rect_x + rect_L;
		dy = grahei;
	}
	
}

float rect_xy::get_rect_ax()
{
	return ax;
}

float rect_xy::get_rect_by()
{
	return by;
}

float rect_xy::get_rect_cx()
{
	return cx;
}

float rect_xy::get_rect_dy()
{
	return dy;
}

ifcoll.h

#pragma once

#ifndef IFCOLL_H_
#define IFCOLL_H_

class ifcoll
{
private:
	float ball_x, ball_y, r;
	float rect_ax, rect_by, rect_cx, rect_dy;
	bool ball_rect_coll;

public:
	ifcoll();
	~ifcoll();
	void set_ifcoll_para(float Ball_x, float Ball_y, float R, float Rect_ax, float Rect_by, float Rect_cx, float Rect_dy);
	bool cal_coll();
};

#endif // !IFCOLL_H_


ifcoll.cpp

#include "ifcoll.h"

ifcoll::ifcoll()
{
	float ball_x = 0, ball_y = 0, r = 0;
	float rect_ax = 0, rect_by = 0, rect_cx = 0, rect_dy = 0;
	bool ball_rect_coll = false;
}

ifcoll::~ifcoll()
{
}

void ifcoll::set_ifcoll_para(float Ball_x, float Ball_y, float R, float Rect_ax, float Rect_by, float Rect_cx, float Rect_dy)
{
	ball_x = Ball_x;
	ball_y = Ball_y;
	r = R;

	rect_ax = Rect_ax;
	rect_by = Rect_by;
	rect_cx = Rect_cx;
	rect_dy = Rect_dy;

	ball_rect_coll = false;
}

bool ifcoll::cal_coll()
{
	if (ball_x + r >= rect_ax && ball_x - r <= rect_cx)
	{
		if (ball_y >= rect_by)
		{
			ball_rect_coll = true;
		}
	}
	else if (ball_x >= rect_ax && ball_x <= rect_cx)
	{
		if (ball_y - r >= rect_by)
		{
			ball_rect_coll = true;
		}
	}
	else
	{
		ball_rect_coll = false;
	}

	return ball_rect_coll;
}

score.h

#pragma once

#ifndef SCORE_H_
#define SCORE_H_

extern void initscore();
extern void scorefun(float rect_x, float grawid);

#endif // !SCORE_H_

score.cpp

#include "score.h"
#include "data.h"

void initscore()
{
	score = 0;
}

void scorefun(float rect_x, float grawid)
{
	if (rect_x >= grawid)
	{
		score = score + 1;
	}
}

data.h

#pragma once

#ifndef DATA_H_
#define DATA_H_

extern int score;

#endif // !DATA_H_

data.cpp

// 全局变量

int score = -1;
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值