C与C++游戏项目练习8:弹跳的小球3.0(由快速敲字母游戏改进而来)

C与C++游戏项目练习8:弹跳的小球3.0(由快速敲字母游戏改进而来)

这次游戏项目的更新,比平时来得更晚一些~~~

前情提要:在做到弹跳的小球的课后题时,我被“按空格键发射新的小球”难住了。

宝书镇楼:
在这里插入图片描述

于是我想起了一个之前看网课学的“快速敲字母”游戏来类比我要实现的功能。
(详情可以看我的这篇博文:https://blog.csdn.net/bailichen800/article/details/115560127?spm=1001.2014.3001.5501)

话不多说,实现的功能:
1.按空格键生成一个小球
2.每一关随机生成随机数目的砖块
3.挡板可以通过按awsd来左上下右自由移动
4.可以由玩家选择是否重复玩多关

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

与快速敲字母游戏一样,本项目建议使用DEV C++运行,因为VS2019会报异常!!!!

与快速敲字母游戏一样,本项目建议使用DEV C++运行,因为VS2019会报异常!!!!

与快速敲字母游戏一样,本项目建议使用DEV C++运行,因为VS2019会报异常!!!!

因为电脑坏了,之前敲的书上初级代码不幸遗失,所以这次只有我完成的升级版代码了。
资源稍后放上,可在我个人主页自取。

#include<iostream>
#include<stdlib.h>
#include<conio.h>
#include<vector>
#include<windows.h>
#include<time.h>
#define Height 15
#define Width 20
using namespace std;

HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取句柄
COORD crd;


void gotoxy(int x, int y)//移动光标函数
{
	crd.X = x;
	crd.Y = y;
	SetConsoleCursorPosition(handle, crd);//设置光标位置为传入的参数x,y  
}
struct Ball//球类  
{
private:
	int ball_x;
	int ball_y;
	int ball_vx = 1;
	int ball_vy = 1;
public:
	Ball(int x, int y) { ball_x = x; ball_y = y; }
	int GetBall_x() { return ball_x; }
	int GetBall_y() { return ball_y; }
	
	void FallBall()
	{
		//获得小球原来的位置坐标,准备输出空格将其删除
		gotoxy(GetBall_x(),GetBall_y()); 
		cout << " ";//擦除当前小球的图案
		ball_x += ball_vx;
		ball_y += ball_vy;
		
	}
	void ChangeXWay()
	{
		ball_vx = -ball_vx;
	}
	void ChangeYWay()
	{
		ball_vy = -ball_vy;
	}
};

class Block//砖块类
{
private:
	int block_x;
	int block_y;
public:
	Block(int x, int y) { block_x = x; block_y = y; }
	int GetBlock_x() { return block_x; }
	int GetBlock_y() { return block_y; }
};

class Plate//挡板类
{
private:
	int ridus;//半径
	int left = position_x - ridus;//左边界
	int right = position_x + ridus;//右边界
public:
	int position_x, position_y;//挡板中心坐标
	Plate(int x, int y, int r) { position_x = x, position_y = y; ridus = r; ChangeBound(); }//调用构造函数时就要算一次左右边界
	int GetLeft() { return left; }
	int GetRight() { return right; }
	void ChangeBound()//在圆心坐标改变的时候改变左右边界
	{
		left = position_x - ridus;//左边界
		right = position_x + ridus;//右边界
	}
};

Plate plate((int)Width / 2, (int)Height - 1,5);

class BallController//控制小球的下落、挡板的移动
{
private:
	vector<Ball> balls;//小球
	vector<Block>blocks;//砖块
	short delay;//保存延时时间
	short score;//保存得分
public:
	BallController()
	{ 
		score = 0; 
		delay = 100;
		
	}//初始化得分为0
	
	void GenerateBall()//按下空格,调用此函数,产生小球
	{
		Ball ball(Width/2,Height/2);//初始位置从游戏中心区域发出
		balls.push_back(ball);//加入动态数组
	}
	void GenerateBlock()//无需输入,顶上三排每排自动生成10-19随机个数的砖块
	{
		for (int i = 0; i < 3; i++)//i表示纵坐标,三排
		{
			for (int j = 0; j < Width; j++)//j表示横坐标,哪一列
			{
				if (j < rand() % 10 + 10)//Width是20,j如果小于10-19的数就生成砖块,否则生成空格
				{
					Block block(j, i);
					blocks.push_back(block);
				}
				else
				{
					printf(" ");
				}
			}
		}
	}
	bool Ground(Ball* ball)
	{
		return ball->GetBall_y() >= Height-1;//看小球纵坐标是否大于等于高度,是则表示落地了
	}
	short Fall()//判断小球是否正常下落,是返回0,不是返回-1(即落地,游戏结束)
	{
		vector<Ball>::iterator itr;
		for (itr = balls.begin(); itr != balls.end(); itr++)
		{
			//获得小球原来的位置坐标,准备输出空格将其删除
			gotoxy(itr->GetBall_x(),itr->GetBall_y()); 
			cout << " ";//擦除当前小球的图案
			if (Ground(&(*itr)))//如果触地,游戏结束
			{
				crd.X = 13; crd.Y = Height + 2;
				SetConsoleCursorPosition(handle, crd);
				SetConsoleTextAttribute(handle, BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);//白色背景
				cout << "Game Over!" << endl;
				SetConsoleTextAttribute(handle, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);//恢复默认黑色背景
				printf("\a");
				return -1;//返回-1告知本函数的调用块:游戏已经结束
			}
			if ((itr->GetBall_y() == 1)|| (itr->GetBall_y() == plate.position_y-1) && (itr->GetBall_x() >= plate.GetLeft() && itr->GetBall_x() <= plate.GetRight()))//小球的纵坐标等于0,碰到边界要反弹(如果等于Height的话就属于前面结束游戏的情况了)
			{//注意碰到挡板的情况也是只有y反向,如果写成x、y都反向,小球就会不停原路返回,详见下方else if被注释的错误代码
				itr->ChangeYWay();//y方向反向
			}
			if (itr->GetBall_x() == 0+1 || itr->GetBall_x() == Width-1)//边界是[0,Width] ,左右各自提前一格判断,这样小球输出的空格就不会影响边界,上面条件的 (itr->GetBall_y() == plate.position_y-1) 同理 
			{
				itr->ChangeXWay();//x方向反向
			}
			//以下else if代码有问题,会导致小球始终原路返回
			//else if ((itr->GetBall_y() == plate.position_y) && (itr->GetBall_x() >= plate.GetLeft() && itr->GetBall_x() <= plate.GetRight())|| (itr->GetBall_x() == 0 || itr->GetBall_x() == Width)|| (itr->GetBall_y() == 0))
			//{//如果小球碰到挡板,那么X、Y方向都会反向(判断条件是纵坐标和挡板一样并且横坐标位于挡板左右边界(圆心+-半径)之间)
			//	itr->ChangeXWay();
			//	itr->ChangeYWay();
			//}
			vector<Block>::iterator blockitr;
			for (blockitr = blocks.begin(); blockitr != blocks.end(); )
			{
				//blockitr是砖块数组的迭代器,itr是小球数组的迭代器
				if (blockitr->GetBlock_x() == itr->GetBall_x() && blockitr->GetBlock_y() == itr->GetBall_y())//如果这个砖块的坐标==小球的坐标
				{
					gotoxy(blockitr->GetBlock_x(), blockitr->GetBlock_y());//坐标移到砖块处
					cout << " ";//砖块被消除,变成空格
					blocks.erase(blockitr);//消除这个砖块(此刻迭代器自动指向下一个元素了
					score++;//得分+1
				}
				else
				{
					blockitr++;//这个分支说明没消除砖块,迭代器手动后移
				}
			}
			gotoxy(itr->GetBall_x(), itr->GetBall_y());
			cout << "o";//在新位置画出小球
			itr->FallBall();//只要未触地就继续下落,y坐标变化
			ShowBall(); 
		}
		Wait();
		return 0;
	}
	void MovePlate(char input)
	{
		if (input == 'a' && plate.GetLeft() > 0)//挡板左移
		{
			plate.position_x--;
			plate.ChangeBound();//重新设置左右边界
		}
		else if (input == 'd' && plate.GetRight() < Width)//挡板右移
		{
			plate.position_x++;
			plate.ChangeBound();//重新设置左右边界
		}
		else if (input == 'w' && plate.position_y > 0)//挡板上移 
		{
			plate.position_y--;
			plate.ChangeBound();//重新设置左右边界
		}
		else if (input == 's' && plate.position_y < Height-1)//挡板下移 
		{
			plate.position_y++;
			plate.ChangeBound();//重新设置左右边界
		}
	}
	void ShowBlock()//绘制砖块 
	{
		vector<Block>::iterator itr;
		for (itr = blocks.begin(); itr != blocks.end(); itr++)
		{
			gotoxy(itr->GetBlock_x(),itr->GetBlock_y());
			cout << "#";
		}
		
	}
	void ClearPlate()//绘制挡板
	{
		gotoxy(plate.GetLeft(), plate.position_y);
		for (int i = plate.GetLeft(); i != plate.GetRight(); i++)
		{
			printf(" ");
		}
	}
	void ShowPlate()//绘制挡板
	{
		gotoxy(plate.GetLeft(), plate.position_y);
		for (int i = plate.GetLeft(); i != plate.GetRight(); i++)
		{
			printf("*");
		}
	}
	void ShowBall()//绘制小球
	{
		vector<Ball>::iterator itr;
		for (itr = balls.begin(); itr != balls.end(); itr++)
		{
			gotoxy(itr->GetBall_x(), itr->GetBall_y());
			cout << "o";
		}
	}
	void SetDelay(short d)//设置延迟时间
	{
		delay = d;
	}
	void Wait()
	{
		Sleep(delay);//按SetDelay中设定好的时间休眠
	}
	void ShowScore()//在边界的下面两个单位处输出得分 
	{
		crd.X = 1;
		crd.Y = Height + 2;
		SetConsoleCursorPosition(handle, crd);
		cout << " Score: " << score << "   ";//多输出几位空格来,防止上一次得分位数较多没有被完全遮挡
	}
	void CleanAll()//清空上一局得分、砖块、小球的数组,平板移到原处 
	{
		score=0; 
		blocks.clear();
		balls.clear();
		for(int i=0;i<Height;i++)
		{
			for(int j=0;j<Width;j++)
			{
				cout<<" ";
			}
		 } 
	} 
};


void ShowBound()//绘制边框
{
	//绘制竖边框
	for (int i = 0; i < Height; i++)
	{
		gotoxy(Width,i );
		printf("|\n");
	}
	//绘制横边框
	for (int j = 0; j < Width; j++)
	{
		gotoxy(j, Height);
		printf("-");
	}
}

int main()
{
	BallController ballctrl;
	srand((unsigned)time(NULL));//产生随机种子,程序时间作为参数传入
	//char choice;
	char input;//接受用户的控制输入
	char choice='n';
	do
	{
		//每次开始新一局游戏要做的事情 
		system("cls");//清除欢迎界面显示文字以及之前游戏遗留的字母
		ballctrl. CleanAll();//清空上一局得分 
		ShowBound();
		ballctrl.GenerateBlock();//生成并打印砖块
		ballctrl.ShowBlock();
		ballctrl.ShowPlate();//生成并展示类中包含的挡板
		ballctrl.SetDelay(100);//设置时间间隔为100s	
		do//初始化完成之后小球的运动和按键检测 
		{
		
			ballctrl.ShowScore();//输出得分 
			if (ballctrl.Fall() == -1)
			{
				break;//如果不想要外层do while的多次玩游戏入口,可以在此直接exit(0) 
			}
			if (_kbhit())//检测到键盘输入,输入只有3种情况:1.生成小球(空格),2.挡板移动(awsd) 
			{
				input = _getch();
				if (input == ' ')
				{
					ballctrl.GenerateBall();//空格生成小球
					ballctrl.ShowBall();//绘制小球
				}
				else if (input == 'a' || input == 'd' || input == 'w' || input == 's')//根据输入的是a或者d来决定挡板的移动
				{
					ballctrl.ClearPlate();//清除原来的挡板
					ballctrl.MovePlate(input);//根据按键移动挡板
					ballctrl.ShowPlate();//展示类中包含的挡板移动之后
				}
			}
		}while (true);
		cout << "		Try Again?	(y or n)";
		cin>>choice;    //choice=_getch();这种写法不用输入回车 
	}while (choice == 'y' ||  choice == 'Y');
	return 0;
}

下一道课后题是实现“接金币游戏”,emmm·····又要秃头了。(似乎就是把小球的挡板拿来接下落的字母,只不过把字母换成金币而已····?)

感谢你能看到这里,一起成为更好的自己吧~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值