“快速敲字母”小游戏——C++实现

游戏介绍:游戏开始运行后,会不断有英文字母从窗体出现并往下掉落,玩家通过按下键盘上相应的字母键,可以将正在掉落的字母消除掉,如果任由字母掉落在窗体下方所绘制的“地面”上,玩家会受到“伤害”,受伤次数达到5次则游戏结束。

游戏实现:

  1. 完成菜单函数;
  2. 菜单项切换及绘制地面;
  3. 显示血槽;
  4. 处理扣血并建立“字母”结构体;
  5. 产生字母的函数;
  6. 字母掉落的设计思想及触地判断;
  7. 处理Game over时的输出内容;
  8. 更新字母坐标及显示得分的函数;
  9. 字母持续掉落的实现;
  10. 按键消除字母;
  11. 完成显示级别的函数;
  12. 主函数循环体。

视频教学链接:https://www.51zxw.net/Show.aspx?cid=717&id=78854

C++代码:

#include<iostream>
#include<windows.h>
#include<conio.h>
#include<vector>//LetterShower类中要使用向量
#include<time.h>//每次出现的字母具有随机性
//#pragma comment(lib,"winmm.lib")//需要使用PlaySound函数播放音效
using namespace std;

HANDLE handle;
COORD crd;
const short GROUND = 27;
const short SCR_WIDTH = 60;
const short HEALTH = 5;

class Vitality {
	//管理生命力的变化
public:
	Vitality() {
		vitality = HEALTH;
	}
	void ShowVitality() {
		//显示当前血量
		crd.X = 36;//SCR_WIDTH*0.6
		crd.Y = 0;
		SetConsoleCursorPosition(handle, crd);
		SetConsoleTextAttribute(handle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
		for (short i = 0; i < HEALTH; i++) {
			cout << (i < vitality ? "■" : "__");
		}
		SetConsoleTextAttribute(handle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);//恢复成黑底白字
	}
	short GetWound(short n=-1) {
		//扣血
		vitality += n;
		ShowVitality();
		return vitality;
	}
	short GetRestore() {
		//恢复满血
		vitality = HEALTH;
		ShowVitality();
		return vitality;
	}
private:
	short vitality;//剩余血量
};

Vitality vty;//定义全局变量,在LetterShower类的成员函数中可以访问它

struct Letter {
	char letter;//当前字母
	short x;//x坐标
	short y;//y坐标
};

class LetterShower {
	//管理产生字母、字母下落、掉落字母等行为
public:
	LetterShower() {
		score = 0;
	}
	void GenerateLetter() {
		//新产生一个字母并进入向量
		Letter l = { 'A' + rand() % 26, rand() % SCR_WIDTH, 1 };
		letters[l.letter - 'A'].push_back(l);
	}
	short Fall() {
		//管理字母掉落及触地(往下掉落一个位置)
		short i;
		vector<Letter>::iterator itr;
		for (i = 0; i < 26; i++) {
			for (itr = letters[i].begin(); itr != letters[i].end(); ) {
				//若同一字母先后出现在屏幕上不同位置,靠内层循环依次处理
				//获得字母原来位置坐标,准备输出空格将其擦除
				crd.X = itr->x;
				crd.Y = itr->y;
				SetConsoleCursorPosition(handle, crd);
				cout << "  ";
				if (itr->y>=GROUND) {
					//若已触地,须做扣血处理
					if (vty.GetWound() <= 0) {
						crd.X = 13;
						crd.Y = GROUND + 2;
						SetConsoleCursorPosition(handle, crd);
						SetConsoleTextAttribute(handle, BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
						cout << "Game Over!";
						SetConsoleTextAttribute(handle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);//恢复成黑底白字
						//PlaySound(".\\Sound\\over.wav", NULL, SND_ASYNC | SND_FILENAME);
						
						//音效函数,SND_ASYNC是声音未播放完便可进行接下来的操作,SND_FILENAME是按照文件名指定声音来源
						return -1;//表明本函数的调用块游戏已结束
					}
					itr = letters[i].erase(itr);//血量还有,擦除字母即可,擦除后迭代器会自动自加
					//PlaySound(".\\Sound\\wound.wav", NULL, SND_ASYNC | SND_FILENAME);
				    continue;//还有下落的字母
				}
				itr->y++;
				crd.Y = itr->y;
				SetConsoleCursorPosition(handle, crd);
				cout << itr->letter;
				itr++;//迭代器没有被擦除的情况
			}
		}
		return 0;//表明尚未Game Over!
	}

	void ClearAll() {
		//清除所有
		for (short i = 0; i < 26; i++) {
			while (!letters[i].empty()) {
				letters[i].pop_back();
			}
		}
		score = 0;
	}

	void ShowScore() {
		//显示得分
		crd.X = 1;
		crd.Y = GROUND + 2;
		SetConsoleCursorPosition(handle, crd);
		cout << "Score:" << score << "    ";//多输出几个空格 ,避免上一次得分较大,清除不干净
	}

	void SetDelay(short d) {
		delay = d;
	}

	void Wait() {
		Sleep(delay);
	}

	void Rain() {
		//反复执行Fall()函数,字母不断掉落,捕捉用户按键,消除被按中的字母
		char ch;//保存用户按键信息
		vector<Letter>::iterator itr;
		Repeat:
		while (!_kbhit()) {
			if (Fall() == -1)
				return;
			Wait();
			if (rand() % 3 == 0)//有三分之一的概率产生新字母
				GenerateLetter();
		}
		ch = _getch();
		if (ch >= 'a'&&ch <= 'z') {
			//若按下的不是字母块,不进入循环
			if (!letters[ch - 'a'].empty()) {
				itr = letters[ch - 'a'].begin();
				crd.X = itr->x;
				crd.Y = itr->y;
				SetConsoleCursorPosition(handle, crd);
				cout << "  ";
				letters[ch - 'a'].erase(itr);
				//PlaySound();
				score++;
				ShowScore();
			}
		}
		goto Repeat;
	}
private:
	vector<Letter> letters[26];//建立26个成员的向量数组,下标0~25分别表示A~Z的各种情况
	short score;//保存得分
	short delay;//字母掉落时延时的毫秒数
};

void PrintLevel(short lv) {
	//输出难易级别,0~2分别表示EASY、INTERMEDIATE、HARD
	crd.X = 4;
	crd.Y = 0;
	SetConsoleCursorPosition(handle, crd);
	switch(lv) {
	case 0:
		cout << "Level: EASY";
		break;
	case 1:
		cout << "Level: INTERMEDIATE";
		break;
	default:
		cout << "Level: HARD";
		break;
	}
}

void Welcome() {
	crd.X = 17;
	crd.Y = 10;
	SetConsoleCursorPosition(handle, crd);
	cout << "Welcome to Type Game!";
	crd.X = 17;
	crd.Y = 12;
	SetConsoleCursorPosition(handle, crd);
	cout << "Press any key to continue...";
}

short ShowMenu() {
	system("cls");//清除Welcome()的内容
	//返回值显示用户所选难度级别,容易0,一般1,困难2
	crd.X = 10;
	crd.Y = 10;
	SetConsoleCursorPosition(handle, crd);
	cout << "Which level do you want to try?";
	short result = 0;
	char ch=0;
	do {
		if (ch == 75)//左方向键
			result = (result + 2) % 3;
		if (ch == 77)//右方向键
			result = (result + 1) % 3;
		crd.X = 10;
		crd.Y = 12;
		SetConsoleCursorPosition(handle, crd);
		//被选中时绿底黑字,未被选中时黑底白字
		if (result == 0)
			SetConsoleTextAttribute(handle, BACKGROUND_GREEN);
		else
			SetConsoleTextAttribute(handle, FOREGROUND_GREEN| FOREGROUND_BLUE| FOREGROUND_RED);
		cout << "EASY";
		crd.X = 20;
		crd.Y = 12;
		SetConsoleCursorPosition(handle, crd);
		//被选中时绿底黑字,未被选中时黑底白字
		if (result == 1)
			SetConsoleTextAttribute(handle, BACKGROUND_GREEN);
		else
			SetConsoleTextAttribute(handle, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
		cout << "INTERMEDIATE";
		crd.X = 38;
		crd.Y = 12;
		SetConsoleCursorPosition(handle, crd);
		//被选中时绿底黑字,未被选中时黑底白字
		if (result == 2)
			SetConsoleTextAttribute(handle, BACKGROUND_GREEN);
		else
			SetConsoleTextAttribute(handle, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
		cout << "HARD";
		ch = _getch();
		if (ch == 0)
			ch = _getch();
	} while (ch != VK_RETURN);//只有按下回车键,结束循环
	SetConsoleTextAttribute(handle, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);//恢复成默认文字及背景色
	return result;
}

void DrawGround() {
	//绘制地面
	crd.X = 0;
	crd.Y = GROUND + 1;
	SetConsoleCursorPosition(handle, crd);
	for (short i = 0; i < SCR_WIDTH; i++)
		cout << "=";
}

int main() {
	system("mode con cols=60 lines=31");
	handle = GetStdHandle(STD_OUTPUT_HANDLE);
	Welcome();//显示欢迎界面
	_getch();
	srand(time(0));//产生随机种子
	char choice;//由用户输出是否继续游戏
	short level;//接受用户输入级别
	LetterShower ls;
	do {
		system("cls");//清除欢迎界面文字,如果是第二次玩,清除屏幕字母
		level = ShowMenu();
		system("cls");
		vty.GetRestore();//恢复满血
		vty.ShowVitality();//血量 
		PrintLevel(level);
		DrawGround();
		ls.ClearAll();//清空向量且得分为0
		ls.ShowScore();
		ls.SetDelay(500-level*100);
		ls.Rain();
		cout << "    Try again?(y/n)"<<endl;
		cin >> choice;
	} while (choice == 'Y'||choice == 'y');
	return 0;
}

C++运行结果:
在这里插入图片描述在这里插入图片描述在这里插入图片描述

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值