每天更新代软件功能,每天有做不完的事情,所以,今天,我做了一个飞机大战的游戏来放松一下。
首先,我们先打开VS,我用的是VS2019,推荐这个版本,功能足够我们打代码了,而且BUG很少。
然后,我们创建一个控制台项目,就可以做一个飞机大战的游戏了。
我们打开了之后,我们就可以开始制作飞机大战的游戏了。
首先,我们先创建几个文件,如下图所示:
然后,我们就可以开始敲代码了!
想要实现像市面上的那种飞机大战的效果,这个是C++几乎无法实现的。(如果你能实现的话,博主希望你能发在评论区,被博主采纳的有红包哦~)
C++是一种多范式编程语言,它包含了过程化、面向对象、泛型等不同的编程范式。它是一种高效的编程语言,可以编写性能很高的程序,同时也具有良好的可移植性。C++的主要概念和用途如下:
类和对象:C++中的核心概念是类和对象,可以用类来描述具有相同属性和行为的一组对象。它是面向对象编程的基础。
继承和多态:C++支持继承,可以从已有类派生出新类,并且可以重写父类的方法实现多态性。
泛型编程:C++支持模板,可以实现泛型编程和函数重载。
异常处理机制:C++提供了一种处理程序运行时错误的机制,可以使用try、catch和throw关键字来实现异常处理。
标准库:C++有一个丰富的标准库,包含了大量的函数和类,可以帮助程序员更容易地实现一些常见的任务,如输入输出、字符串处理、容器和算法等。
C++的应用范围很广,包括系统编程、游戏开发、图形界面设计、数据库应用、网络编程、嵌入式开发等。由于C++是一种高效的语言,适合编写对性能要求很高的应用程序。同时,C++的可移植性也很好,可以在不同的平台上编译和运行。
但是,C++更偏向于计算方面,如果要进行美观的图形化界面的设置,那建议你使用Python或者是JavaScript来进行实现,因为它们能更方便。
话不多说,我们来解决函数困难。
game.h:
#pragma once
#include<iostream>
#include<Windows.h>//定义控制台应用程序的入口点
using namespace std;
//定义敌人结构 其中最后面Frame代表结构体类型 若不加typedef代表定义的结构体变量
typedef struct Frame
{
COORD position[2];
// COORD 是Windows API中定义的一种结构,表示一个字符在控制台屏幕上的坐标。
// 其定义为:
// typedef struct _COORD {
// SHORT X;
// SHORT Y;
// } COORD;
int flag;
}Frame;
class Game
{
public:
COORD position[10];
COORD bullet[10];//子弹坐标
Frame enemy[8];//敌人数量
int score;
int rank;//级别,难度
int rankf;//等级标志
string title;
int flag_rank;//等级标志
//构造函数
Game();
//初始化所有 //设定位置
void initPlane();
void initBullet();
void initEnemy();
//填充所有 --画出形状和消失的形状
void drawPlane();
void drawPlaneToNull();
void drawBullet();
void drawBulletToNull();
void drawEnemy();
void drawEnemyToNull();
//执行某一个操作
void Playing();//游戏主循环
void planeMove(char x);//飞机移动
void judgePlane();//判断飞机是否与敌机重叠
void GameOver();//游戏失败
void Pause();// 该成员函数用来使得游戏暂停
void Shoot();//发射子弹
void bulletMove();//子弹移动
void drawThisBulletToNull(COORD c);//画出失效子弹
void judgeEnemy();//判断子弹是否击中敌机
void drawThisEnemyToNull(Frame f); //击败的敌机清空
void enemyMove();//敌机移动
void printScore();//输出分数
};
//主菜单
int drawMenu();
//隐藏光标
void HideCursor();
void SetPos(int i, int j);//设置光标
COORD random(COORD a, COORD b);//产生随机敌机位置
void drawFrame(COORD a, COORD b, char row, char col);//画出敌机
//把第y行,[x1, x2) 之间的坐标填充为 ch
void drawRow(int y, int x1, int x2, char ch);
//把第x列,[y1, y2] 之间的坐标填充为 ch
void drawCol(int x, int y1, int y2, char ch);
// 绘制游戏界面
void drawPlaying();
void drawFrame(Frame frame, char row, char col);//画坠毁后的战机
// 该函数用来判断战机的某一部分是否与敌机有接触
bool judgeCoordInFrame(Frame frame, COORD spot);
void drawRow(COORD a, COORD b, char ch);
注释都在上边了,看不懂的可以私聊博主。
然后,我们来做游戏的另一个函数,也就是敌机和战机的外观和移动函数,这个函数是游戏的核心,没有了这个函数,那这个游戏就像一个图片,只能呈现出游戏的界面,而不能做出移动的效果,会让人第一个感觉就是一个字——丑。
首先,我们先打开一个黑框(可以用CMD代替):
我们需要让敌机从上往下移动,而战机是需要玩家自己上下左右进行控制的。然后,我们先定义战机的外观:
// 这个函数体类的代码其实就是为了初始化战机的十个部分的位置,战机的组成如下所示:
// | 5
// | 9
// ***** 12034
// *** 678
// 第一排5个0的坐标依次对应了position[1]position[2]position[0]position[3]position[4]
// 第二排三个0的坐标依次对应了position[6]position[7]position[8]
// 两排0上面的两|的坐标从上往下依次对应了position[5]position[9]
}
void Game::drawPlane()
{
for (int i = 0; i < 9; i++)
{
SetPos(position[i].X, position[i].Y);
if (i != 5)
{
cout << "*";
}
else if (i == 5)
{
cout << "|";
}
}
}
战机的样子在上面的代码块注释里面应该能看到,我们用WinAPI和for循环语句进行战机的输入与输出。
然后,我们来绘制敌机的样子:
// 接下来要根据敌机的左上角坐标和右下角坐标画出敌机,
// 显然,敌机的外形如下所示:
// --
// | |
// --
void Game::drawEnemy()
{
for (int i = 0; i < 8; i++)
{
drawFrame(enemy[i].position[0], enemy[i].position[1], '-', '|');
}
}
敌机的代码和样子都在上面了,我们用“-”和“|”来绘制敌机。
其他的移动代码和子弹等角色的代码有“亿”点复杂,如果我在这里讲的话,人家CSDN要打电话给我了。(因为如果写的话,这篇博文估计要5万多字)
现在供上角色的代码(game.cpp):
#include"game.h"
#include<Windows.h>
#include<conio.h>
Game::Game()
{
// 调用类成员函数来进行初始化
initPlane();
initBullet();
initEnemy();
// 初始化四个int型数据成员,采用赋值的方式进行初始化
// string类型的数据成员title没有进行初始化,因为:
// string本身就是一个标准库类类型,它的类定义中设置了默认构造函数,
// 这些默认构造函数会将对象初始化为合理的默认状态,
// string的默认构造函数会产生空字符串,相当于"" 。
this->score = 0;
rank = 25;
rankf = 25;
flag_rank = 0;
}
void Game::initPlane()
{
COORD centren;
centren.X = 39;
centren.Y = 22;
position[0].X = position[5].X = position[7].X = position[9].X = centren.X;
position[1].X = centren.X - 2;
position[2].X = position[6].X = centren.X - 1;
position[3].X = position[8].X = centren.X + 1;
position[4].X = centren.X + 2;
for (int i = 0; i <= 4; i++)
{
position[i].Y = centren.Y;
}
for (int i = 6; i <= 8; i++)
{
position[i].Y = centren.Y + 1;
}
position[5].Y = centren.Y - 1;
position[9].Y = centren.Y - 2;
// 这个函数体类的代码其实就是为了初始化战机的十个部分的位置,战机的组成如下所示:
// | 5
// | 9
// ***** 12034
// *** 678
// 第一排5个0的坐标依次对应了position[1]position[2]position[0]position[3]position[4]
// 第二排三个0的坐标依次对应了position[6]position[7]position[8]
// 两排0上面的两|的坐标从上往下依次对应了position[5]position[9]
}
void Game::drawPlane()
{
for (int i = 0; i < 9; i++)
{
SetPos(position[i].X,position[i].Y);
if (i != 5)
{
cout << "*";
}
else if (i == 5)
{
cout << "|";
}
}
}
// 这个成员函数通过将战机的每个坐标处输出" "来代替"0"和"|",
// 来达到将战机消除的目的。
void Game::drawPlaneToNull()
{
for (int i = 0; i < 9; i++)
{
SetPos(position[i].X, position[i].Y);
cout << " ";
}
}
// 该成员函数用来初始化子弹,
// 即将每个子弹的Y坐标初始化为30(bullet[i].Y = 30)来表示子弹处于失效状态
void Game::initBullet()
{
for (int i = 0; i < 10; i++)
{
bullet[i].Y = 30;
}
}
// 该成员函数用来画出子弹
// 首先检查每颗子弹的有效性,如果子弹有效,则定位到该子弹的坐标处,输出 "^",表示该子弹,
// 如果子弹是无效的,则不绘制
void Game::drawBullet()
{
for (int i = 0; i < 10; i++)
{
if (bullet[i].Y != 30)
{
SetPos(bullet[i].X,bullet[i].Y);
cout << "^";
}
}
}
//子弹失效
void Game::drawBulletToNull()
{
for (int i = 0; i < 10; i++)
if (bullet[i].Y != 30)
{
SetPos(bullet[i].X, bullet[i].Y + 1);
cout << " ";
}
}
// 这个函数用来初始敌机的位置,
// 屏幕当中只能同时存在八架敌机,
// 且每架敌机用如下结构体Frame来表示,如下所示:
// typedef struct Frame
// {
// COORD position[2];
// int flag;
// }Frame;
COORD random(COORD a, COORD b)
{
int x = rand() % (a.X - b.X) + a.X;
int y = rand() % (a.Y - b.Y) + a.Y;
COORD c = { x,y };
return c;
}
void Game::initEnemy()
{
COORD a = { 1, 1 };
COORD b = { 45, 15 };
for (int i = 0; i < 8; i++)
{
enemy[i].position[0] = random(a, b);
// random(a, b)是调用了一个重载的函数,它表示在坐标a、b之间的矩形框
// 内随机生成一个坐标值,并将该坐标值作为敌机的左上角的坐标。
// enemy[i].position[0]中是一个Frame结构体类型的变量,存放了敌机i的左上角的坐标。
enemy[i].position[1].X = enemy[i].position[0].X + 3;
enemy[i].position[1].Y = enemy[i].position[0].Y + 2;
// enemy[i].position[1]也中是一个Frame结构体类型的变量,存放了敌机i的右下角的坐标。
}
}
// 接下来要根据敌机的左上角坐标和右下角坐标画出敌机,
// 显然,敌机的外形如下所示:
// --
// | |
// --
void Game::drawEnemy()
{
for (int i = 0; i < 8; i++)
{
drawFrame(enemy[i].position[0], enemy[i].position[1], '-', '|');
}
}
// 将敌机消除,通过输出空白的方式
void Game::drawEnemyToNull()
{
for (int i = 0; i < 8; i++)
{
drawFrame(enemy[i].position[0], enemy[i].position[1], ' ', ' ');
}
}
//隐藏光标
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor_info = { 1,0 };//第二个值0表示隐藏光标
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
void SetPos(int i, int j)//设置坐标点位(光标)
{
HANDLE hout;
COORD coord;
coord.X = i;
coord.Y = j;
hout = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hout, coord);
}
//左上角坐标、右下角坐标、用row填充行、用col填充列
void drawFrame(COORD a, COORD b, char row, char col)
{
drawRow(a.Y, a.X + 1, b.X - 1, row);
drawRow(b.Y, a.X + 1, b.X - 1, row);
drawCol(a.X, a.Y + 1, b.Y - 1, col);
drawCol(b.X, a.Y + 1, b.Y - 1, col);
}
//把第y行,[x1, x2) 之间的坐标填充为 ch
void drawRow(int y, int x1, int x2, char ch)
{
SetPos(x1, y);
for (int i = 0; i <= (x2 - x1); i++)
{
cout << ch;
}
}
//把第x列,[y1, y2] 之间的坐标填充为 ch
void drawCol(int x, int y1, int y2, char ch)
{
int y = y1;
while (y != y2 + 1)
{
SetPos(x, y);
cout << ch;
y++;
}
}
//主菜单绘制
int drawMenu()
{
SetPos(30,1);
cout << "飞 机 大 战";
drawRow(3, 0, 79, '-');
drawRow(5, 0, 79, '-');
SetPos(28, 4);
cout << "w 和 s选择,k确定";
int j = 11;
SetPos(12, j);
cout << ">>";
SetPos(15, 11);
cout << "1. 简单的敌人";
SetPos(15, 13);
cout << "2. 冷酷的敌人";
drawRow(20, 0, 79, '-');
SetPos(47, 11);
cout << "简单的敌人:";
SetPos(47, 13);
cout << "简单敌人有着较慢的移动速度。";
SetPos(30, 21);
cout << "copy right@风兮木萧";
drawRow(22, 0, 79, '-');
while (true)
{
if (_kbhit())
{
char x = _getch();
switch (x)
{
case'w':
{
if (j == 13)
{
SetPos(12, j);
cout << " ";
j = 11;
SetPos(12, j);
cout << ">>";
SetPos(51, 13);
cout << " ";
SetPos(47, 11);
cout << "简单的敌人:";
SetPos(51, 13);
cout << "简单敌人有着较慢的移动速度,比较容易对付";
}
break;
}
case's':
{
if (j == 11)
{
SetPos(12, j);
cout << " ";
j = 13;
SetPos(12, j);
cout << ">>";
SetPos(51, 13);
cout << " ";
SetPos(47, 11);
cout << "冷酷的敌人:";
SetPos(51, 13);
cout << "冷酷的敌人移动速度较快,难对付哟。";
}
break;
}
case 'k':
{
if (j == 11)//源代码为8?
return 1;
else
return 2;
}
}
}
}
return 0;
}
void drawFrame(int x1, int y1, int x2, int y2, char row, char col)
{
COORD a = { x1, y1 };
COORD b = { x2, y2 };
drawFrame(a, b, row, col);
}
// 绘制游戏界面
void drawPlaying()
{
drawFrame(0, 0, 48, 24, '=', '|');// draw map frame主界面
drawFrame(49, 0, 79, 4, '-', '|');// draw output frame 状态界面
drawFrame(49, 4, 79, 9, '-', '|');// draw score frame 分数界面
drawFrame(49, 9, 79, 20, '-', '|');// draw operate frame 操作界面
drawFrame(49, 20, 79, 24, '-', '|');// draw other message frame 提示界面
SetPos(52, 6);
cout << "得分:";
SetPos(52, 7);
cout << "称号:";
SetPos(52, 11);
cout << "操作方式:";
SetPos(52, 13);
cout << " a,s,d,w:控制战机移动。";
SetPos(52, 15);
cout << " p:暂停游戏。";
SetPos(52, 17);
cout << " e:退出游戏。";
SetPos(52, 22);
cout << " 游戏虽好玩,不要贪多哦 ";
}
// 该成员函数用过响应战机的一个动作
// a,s,w,d,来控制战机的移动
void Game::planeMove(char x)
{
if (x == 'a')
{
if (position[1].X != 1)
{
for (int i = 0; i <= 9; i++)
{
position[i].X -= 2;
}
}
}
// 如果玩家按下 'a' 键,说明玩家想让战机往左移动一个距离(2个单位),
// 首先检测,战机的最左侧的位置坐标(即position[1].X)有没有达到左边界,
// 如果到达了边界,那就不做出移动;如果没有达到边界,则将战机10个部分的X值减小2。
if (x == 's')
{
if (position[7].Y != 23)
{
for (int i = 0; i <= 9; i++)
{
position[i].Y += 1;
}
}
}
// 如果玩家按下 's' 键,说明玩家想让战机往下移动一个距离(1个单位),
// 首先检测,战机的最底部的位置坐标(即position[6].Y或者position[7].Y或者position[8].Y)有没有达到下边界,
// 如果到达了边界,那就不做出移动;如果没有达到边界,则将战机10个部分的Y值增加1。
if (x == 'd' && (position[4].X != 47))
{
for (int i = 0; i <= 9; i++)
{
position[i].X += 2;
}
}
// 如果玩家按下 'd' 键,说明玩家想让战机往右移动一个距离(2个单位),
// 首先检测,战机的最右侧的位置坐标(即position[4].X)有没有达到右边界,
// 如果到达了边界,那就不做出移动;如果没有达到边界,则将战机10个部分的X值增加2。
if (x == 'w'&&(position[5].Y != 3))
{
for (int i = 0; i <= 9; i++)
{
position[i].Y -= 1;
}
}
// 如果玩家按下'w'键,说明玩家想让战机往上移动一个距离(1个单位),
// 首先检测,战机的最顶部的位置坐标(即position[5].Y)有没有达到上边界,
// 如果到达了边界,那就不做出移动;如果没有达到边界,则将战机10个部分的Y值减少1。
}
// 该函数用来判断战机的某一部分是否与敌机有接触
// 如果与敌机有接触在判断为坠毁
bool judgeCoordInFrame(Frame frame, COORD spot)
{
if ((spot.X >= frame.position[0].X) && (spot.X <= frame.position[1].X) && (spot.Y >= frame.position[0].Y) && (spot.Y <= frame.position[1].Y))
{
return true;
}
return false;
}
void drawFrame(Frame frame, char row, char col)
{
COORD a = frame.position[0];
COORD b = frame.position[1];
drawFrame(a, b, row, col);
}
//游戏结束
void Game::GameOver()
{
system("cls");
COORD p1 = { 28,9 };
COORD p2 = { 53,15 };
drawFrame(p1, p2, '=', '|');
SetPos(36, 12);
string str = "Game Over!";
for (int i = 0; i < str.size(); i++)
{
Sleep(80);
cout << str[i];
}
Sleep(1000);
system("cls");
drawFrame(p1, p2, '=', '|');
SetPos(31, 11);
cout << "击落敌机:" << score / 5 << " 架";
SetPos(31, 12);
cout << "得 分:" << score;
SetPos(31, 13);
cout << "获得称号:" << title;
SetPos(30, 18);
Sleep(1000);
cout << "继续? 是(y)| 否(n)";
as://goto 语句标签 直接跳转至此
char x = _getch();
if (x == 'n')
{
exit(0);
}
else if (x == 'y')
{
system("cls");
Game game;
int a = drawMenu(); // 绘制游戏开始界面主菜单
if (a == 2)
game.rank = 20;
system("cls");
drawPlaying(); // 绘制游戏界面框架
game.Playing();
}
else
goto as;
}
// 该成员函数用来判断战机是否坠毁,
// 依次判断每架敌机与战机的每个部分是否有接触,
// 如果有接触,则表示战机坠毁
void Game::judgePlane()
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 9; j++)
// 此处的实参position[j]是指战机的10个部分的COORD坐标,
// 类中的成员函数可以访问数据成员变量。
// 此处也可以写成this-> position[j],因为
// 成员函数具有一个附加的隐含形参,即指向该类对象的一个指针,
// 这个隐含形参命名为this,与调用成员函数的对象绑定在一起。
// 成员函数不能定义this形参,而是由编译器隐含地定义。
// 成员函数的函数体可以显式使用this指针,但不是必须这么做。
if (judgeCoordInFrame(enemy[i], position[j]))
{
SetPos(62, 1);
cout << "坠毁";
drawFrame(enemy[i], '+', '+');
// 将与战机相撞的敌机的形状绘制为:
// ++
// + +
// ++
Sleep(1000);
GameOver();
break;
}
}
}
// 该成员函数用来使得游戏暂停
void Game::Pause()
{
SetPos(61, 2);
cout << " ";
SetPos(61, 2);
cout << "暂停中...";
// 当出现"暂停中..."的提示以后,程序不停的接收按下的按键,
// 当按下'p'键以后,说明要退出暂停状态,此时需要清除"暂停中..."的提示
// 通过输出空白 " "来将其覆盖,达到效果
char c = _getch();
while (c != 'p')
{
c = _getch();
}
SetPos(61, 2);
cout << " ";
}
// 这个成员函数用来响应一次射击操作,
// 也就是,当游戏中的时候,玩家按下"k"键,就执行该函数。
// 由于子弹是由COORD bullet[10]定义的,因此同一时刻,界面内只能有10颗子弹同时出现。
// 如果界面内不够10颗子弹,按下"k"键后战机应该发射出一颗子弹,
// 于是,依次遍历10颗子弹,当遇到第一颗失效的子弹后,
// 立即将该子弹赋予新的坐标(战机的炮口,也就是(position[5].X,position[5].Y - 1)),
// 让其激活。然后退出for循环,函数执行完毕。
void Game::Shoot()
{
for (int i = 0; i < 10; i++)
{
if (bullet[i].Y == 30)
{
bullet[i].X = position[5].X;
bullet[i].Y = position[5].Y - 1;
break;
}
}
}
void Game::drawThisBulletToNull(COORD c)
{
SetPos(c.X,c.Y);
cout << " ";
}
// 此成员函数用来响应一次子弹的运动
// 每次子弹运动,屏幕子弹的坐标都会出现变化,即
// 先判断子弹是否有效(即判断语句if (bullet[i].Y != 30)),
// 若子弹有效,将该子弹的Y坐标减少1,X坐标不变,
// 检测子弹坐标更改之后是否达到上边界,如果达到上边界,则将该子弹从屏幕上擦除,
// 同时,将该子弹置为失效状态,即 bullet[i].Y = 30。
void Game::bulletMove()
{
for (int i = 0; i < 10; i++)
{
if (bullet[i].Y != 30)
{
bullet[i].Y -= 1;
if (bullet[i].Y == 1)
{
COORD pos = { bullet[i].X, bullet[i].Y + 1 };
drawThisBulletToNull(pos);
bullet[i].Y = 30;
}
}
}
}
//击败的敌机清空
void Game::drawThisEnemyToNull(Frame f)
{
drawFrame(f, ' ', ' ');
}
// 该成员函数依次遍历每一架敌机,
// 将每一架敌机依次与每一颗子弹进行检测,
// 判断敌机是否与子弹有接触,如果有接触,则表示击中敌机,
// 此时将敌机和子弹擦除,然后在界面顶部的位置处随机生成一架敌机
void Game::judgeEnemy()
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 10; j++)
{
if (judgeCoordInFrame(enemy[i], bullet[j]))
{
score += 5;
drawThisEnemyToNull(enemy[i]);
COORD a = { 1, 1 };
COORD b = { 45, 3 };
enemy[i].position[0] = random(a, b);
enemy[i].position[1].X = enemy[i].position[0].X + 3;
enemy[i].position[1].Y = enemy[i].position[0].Y + 2;
drawThisBulletToNull(bullet[j]);
bullet[j].Y = 30;
}
}
}
}
// 该成员函数用来响应一次敌机的移动
// 界面上必须同时出现八架敌机,因此,
// 如果有某架敌机运动到下边界处,则重置该敌机的坐标
void Game::enemyMove()
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 2; j++)
enemy[i].position[j].Y++;
// 我们将每架敌机的左上角和右下角坐标的Y值增加1,
// 表示该敌机向下走了一个距离
// 检测向下走一个距离后的敌机的右下角坐标的Y值是否达到24,
// 如果达到,代表敌机已经运动到下边界了,
// 此时需要随机重置该敌机的坐标
if (enemy[i].position[1].Y==24)
{
COORD a = { 1, 1 };
COORD b = { 45, 3 };
enemy[i].position[0] = random(a, b);
enemy[i].position[1].X = enemy[i].position[0].X + 3;
enemy[i].position[1].Y = enemy[i].position[0].Y + 2;
}
}
}
void Game::printScore()
{
if (score <= 120)
{
flag_rank = 1;
}
else if (score > 120 && score <= 360)
{
flag_rank = 2;
}
else if (score > 360 && score <= 480)
{
flag_rank = 3;
}
else if (score > 480)
{
flag_rank = 4;
}
SetPos(60, 6);
cout << score;
SetPos(60, 7);
if (flag_rank == 1)
{
title = "初级飞行员";
}
else if (flag_rank == 2)
{
title = "中级飞行员";
}
else if (flag_rank == 3)
{
title = "高级飞行员";
}
else if (flag_rank == 4)
{
title = "王牌飞行员";
}
cout << title;
}
// 这个成员函数是游戏的主循环函数,
// 定义了整个游戏过程。
void Game::Playing()
{
drawEnemy();
drawPlane();
int flag_bullet = 0;
int flag_enemy = 0;
while (true)
{
Sleep(20);
// 函数名:kbhit()(VC++6.0下为_kbhit())
// 功能及返回值: 检查当前是否有键盘输入,若有则返回一个非0值,否则返回0
// 用法:int kbhit(void);
// 包含头文件: include <conio.h>
// kbhit()在执行时,检测是否有按键按下,有按下返回非0值,没有按下则返回0,是非阻塞函数;
// 不同于getch()的在执行时, 检测按下什么键, 如果不按键该函数不返回,也就不进行下一步操作,是阻塞函数。
if (_kbhit())
{
char x = _getch();
// getch()是编程中所用的函数,这个函数是一个不回显函数,
// 当用户按下某个字符时,函数自动读取,无需按回车
// getch()并非标准C中的函数,不存在C语言中。
// 所在头文件是conio.h,而不是stdio.h。
// 用ch = getch(); 会等待你按下任意键之后,把该键字符所对应的ASCII码赋给ch, 再执行下面的语句。
if ('a' == x || 's' == x || 'd' == x || 'w' == x)
{
drawPlaneToNull(); // 将战机先擦除
planeMove(x); // 根据所输入的操作符,对战机的坐标进行更改
drawPlane(); // 访问类中的数据成员——战机的坐标,在新的坐标处重新绘制战机
judgePlane(); // 判断战机是否有坠毁
}
// 在某一循环当中,如果检测到有'p'键按下,
// 首先在右侧游戏界面输出"暂停中...",然后陷入while()循环一直等待'p'键再次按下,
// 如果'p'键没有按下,就一直处在while()循环内,因此不能执行后面的程序,起到暂停的效果。
else if ('p' == x)
{
Pause();
}
// 如果是检测到'k'键按下,则运行Shoot()函数,
else if ('k' == x)
{
Shoot();
}
// 如果是检测到'k'键按下,则运行GameOver()函数,
// GameOver()函数执行完毕后,执行break;语句跳出while循环(注意不是if (_kbhit()))。
// break语句用于结束最近的while、do while、for或switch语句,并将程序的执行权传递给紧接在
// 被终止语句之后的语句。
else if ('e' == x)
{
//CloseHandle(MFUN)
GameOver();
break;
}
}
// 接下来处理子弹
// 判断子弹状态的程序一直在运行
if (flag_bullet == 0)
{
bulletMove(); // 更新界面上有效子弹的坐标
drawBulletToNull(); // 将处于旧坐标的子弹擦除
drawBullet(); // 绘制出新坐标上的子弹
judgeEnemy(); // 判断敌机是否被子弹击中
}
flag_bullet++;
if (flag_bullet==1)
{
flag_bullet = 0;
}
// 接下来处理敌机
if (flag_enemy==0)
{
drawEnemyToNull(); // 将所有的敌机都擦除
enemyMove(); // 更新敌机的坐标
drawEnemy(); // 绘制出处于新坐标上的敌机
judgePlane(); // 判断敌机是否与战机接触
}
flag_enemy++;
if (flag_enemy >= rank)
{
flag_enemy = 0;
}
/* 输出得分 */
printScore();
}
}
主函数总不需要我来详细讲解了吧,直接供上(main.cpp):
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
using namespace std;
int main()
{
system("title 飞机大战");
srand((unsigned int)time(NULL));//随机时间种子
HideCursor();//隐藏光标
Game game;
int a = drawMenu();
if (a == 2)
{
game.rank = 20;
}
system("cls");
drawPlaying();
game.Playing();
}
文章来之不易,希望各位读者能打赏一下博主。
升级版链接:飞机大战升级版
文章浏览阅读1.5w次,点赞9次,收藏26次。简介一个创建某个文件到桌面快捷方式的BAT批处理.代码@echooff::设置程序或文件的完整路径(必选)setProgram=D:\Program Files (x86)\格式工厂.4.2.0\FormatFactory.exe::设置快捷方式名称(必选)setLnkName=格式工厂v4.2.0::设置程序的工作路径,一般为程序主目录,此项若留空,脚本将..._批处理创建桌面快捷方式
文章浏览阅读2k次。射频识别技术漫谈(6-10),概述RFID的通讯协议;射频ID卡的原理与实现,数据的传输与解码;介绍动物标签属性与数据传输;RFID识别号的变化等_芯片 ttf模式
文章浏览阅读1.1k次。今天小编心血来潮,为大家带来一个很有趣的项目,那就是使用 Python web 框架 Django 来实现支付宝支付,废话不多说,一起来看看如何实现吧。_django 对接支付宝接口流程
文章浏览阅读842次。Zabbix 5.0 LTS,跑了一年多了一直很稳定,前两天空间显示快满了,于是手贱清理了一下history_uint表(使用mysql truncate),结果折腾了一周。大概故障如下:然后zabbix论坛、各种群问了好久都没解决,最后自己一番折腾似乎搞定了。初步怀疑,应该是由于历史数据被清空后,zabbix需要去处理数据,但是数据量太大,跑不过来,所以来不及更新了(?)..._zabbix问题没有更新
文章浏览阅读296次。一、数据结构之字典 key-value
文章浏览阅读9.7k次,点赞3次,收藏13次。最近项目中遇到一个问题,是关于mybatis-plus的字段注解策略,记录一下。1问题调用了A组件(基础组件),来更新自身组件的数据,发现自己组件有个字段总是被清空。2原因分析调用的A组件的字段,属于基础字段,自己业务组件,对这个基础字段做了扩展,增加了业务字段。但是在自己的组件中的实体注解上,有一个注解使用错误。mybatis-plus封装的updateById方法,如果..._mybatisplus strategy
文章浏览阅读3.8k次。如何构建倒排索引,我们将这个过程叫做“索引构建”。如果我们的文档很多,这样索引就一次性装不下内存,该如何构建。硬件的限制 我们知道ram读写是随机的操作,只要输入相应的地址单元就能瞬间将数据读出来或者写进去。但是磁盘不行,磁盘必须有一个寻道的过程,外加一个旋转时间。那么只有涉及到磁盘,我们就可以考虑怎么节省I/O操作时间。【注】操作系统往往以数据块为单位进行读写。因为读一_为某一文档及集构件词项索引时,可使用哪些索引构建方法
文章浏览阅读836次。英特尔技术与制造事业部副总裁卞成刚7日在财富论坛间隙接受中新社记者采访时表示,该公司看好中国市场前景,扎根中国并以此走向世界是目前最重要的战略之一。卞成刚说,目前该公司正面临战略转型,即从传统PC服务领域扩展至所有智能设施领域,特别是移动终端。而中国目前正引领全球手机市场,预计未来手机、平板电脑等方面的发明创新将大量在中国市场涌现,并推向全球。持相同态度的还有英特尔中国区执行董事戈峻。戈峻
文章浏览阅读627次。https://blog.csdn.net/zrs19800702/article/details/53101213http://blog.csdn.net/lzw06061139/article/details/51445311https://my.oschina.net/linuxhunter/blog/654080rgw 概述Ceph 通过radosgw提供RES..._radosgw -c
文章浏览阅读3.7k次,点赞6次,收藏9次。我为什么选择ECharts ? 本周学校课程设计,原本随机佛系选了一个51单片机来做音乐播放器,结果在粗略玩了CN-DBpedia两天后才回过神,课设还没有开始整。于是懒癌发作,碍于身上还有比赛的作品没交,本菜鸡对硬件也没啥天赋,所以就直接把题目切换成软件方面的题目。写python的同学选择了一个时间序列数据的可视化曲线程序设计题目,果真python在数据可视化这一点性能很优秀。..._echarts 时间序列
文章浏览阅读1.6k次。事件类:/** * * * @className: EarlyWarnPublishEvent * * @description:数据风险预警发布事件 * * @param: * * @return: * * @throws: * * @author: lizz * * @date: 2020/05/06 15:31 * */public cl..._applicationeventpublisheraware
文章浏览阅读1.2k次。如需转载请注明出处!点击小图片转到图片查看的页面在Android开发中很常用到,抱着学习和分享的心态,在这里写下自己自定义的一个ImageView,可以实现类似微信朋友圈中查看图片的功能和效果。主要功能需求:1.缩放限制:自由缩放,有最大和最小的缩放限制 2居中显示:.若图片没充满整个ImageView,则缩放过程将图片居中 3.双击缩放:根据当前缩放的状态,双击放大两倍或缩小到原来 4.单指_imageview图片边界回弹