游戏介绍:游戏开始运行后,会不断有英文字母从窗体出现并往下掉落,玩家通过按下键盘上相应的字母键,可以将正在掉落的字母消除掉,如果任由字母掉落在窗体下方所绘制的“地面”上,玩家会受到“伤害”,受伤次数达到5次则游戏结束。
游戏实现:
视频教学链接:https://www.51zxw.net/Show.aspx?cid=717&id=78854。
C++代码:
#include
#include
#include
#include //LetterShower类中要使用向量
#include //每次出现的字母具有随机性
//#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;
}