• 用VSC++做一个飞机大战的游戏


    每天更新代软件功能,每天有做不完的事情,所以,今天,我做了一个飞机大战的游戏来放松一下。

     

    一、文件框架

    首先,我们先打开VS,我用的是VS2019,推荐这个版本,功能足够我们打代码了,而且BUG很少。

     

    615a4a3b82744490a0aa434527c51754.png

     f5df2f54c8f341ee9362c14295a018df.png

     

    然后,我们创建一个控制台项目,就可以做一个飞机大战的游戏了。

    我们打开了之后,我们就可以开始制作飞机大战的游戏了。

    首先,我们先创建几个文件,如下图所示:

    7943830c76384aefa73df6700df52f27.png

     

    然后,我们就可以开始敲代码了!👨‍💻

    二、函数

    想要实现像市面上的那种飞机大战的效果,这个是C++几乎无法实现的。(如果你能实现的话,博主希望你能发在评论区,被博主采纳的有红包哦~)

    C++是一种多范式编程语言,它包含了过程化、面向对象、泛型等不同的编程范式。它是一种高效的编程语言,可以编写性能很高的程序,同时也具有良好的可移植性。C++的主要概念和用途如下:

    1. 类和对象:C++中的核心概念是类和对象,可以用类来描述具有相同属性和行为的一组对象。它是面向对象编程的基础。

    2. 继承和多态:C++支持继承,可以从已有类派生出新类,并且可以重写父类的方法实现多态性。

    3. 泛型编程:C++支持模板,可以实现泛型编程和函数重载。

    4. 异常处理机制:C++提供了一种处理程序运行时错误的机制,可以使用try、catch和throw关键字来实现异常处理。

    5. 标准库:C++有一个丰富的标准库,包含了大量的函数和类,可以帮助程序员更容易地实现一些常见的任务,如输入输出、字符串处理、容器和算法等。

    C++的应用范围很广,包括系统编程、游戏开发、图形界面设计、数据库应用、网络编程、嵌入式开发等。由于C++是一种高效的语言,适合编写对性能要求很高的应用程序。同时,C++的可移植性也很好,可以在不同的平台上编译和运行。

    但是,C++更偏向于计算方面,如果要进行美观的图形化界面的设置,那建议你使用Python或者是JavaScript来进行实现,因为它们能更方便。

    话不多说,我们来解决函数困难。

    游戏界面

    game.h:

    1. #pragma once
    2. #include
    3. #include//定义控制台应用程序的入口点
    4. using namespace std;
    5. //定义敌人结构 其中最后面Frame代表结构体类型 若不加typedef代表定义的结构体变量
    6. typedef struct Frame
    7. {
    8. COORD position[2];
    9. // COORD 是Windows API中定义的一种结构,表示一个字符在控制台屏幕上的坐标。
    10. // 其定义为:
    11. // typedef struct _COORD {
    12. // SHORT X;
    13. // SHORT Y;
    14. // } COORD;
    15. int flag;
    16. }Frame;
    17. class Game
    18. {
    19. public:
    20. COORD position[10];
    21. COORD bullet[10];//子弹坐标
    22. Frame enemy[8];//敌人数量
    23. int score;
    24. int rank;//级别,难度
    25. int rankf;//等级标志
    26. string title;
    27. int flag_rank;//等级标志
    28. //构造函数
    29. Game();
    30. //初始化所有 //设定位置
    31. void initPlane();
    32. void initBullet();
    33. void initEnemy();
    34. //填充所有 --画出形状和消失的形状
    35. void drawPlane();
    36. void drawPlaneToNull();
    37. void drawBullet();
    38. void drawBulletToNull();
    39. void drawEnemy();
    40. void drawEnemyToNull();
    41. //执行某一个操作
    42. void Playing();//游戏主循环
    43. void planeMove(char x);//飞机移动
    44. void judgePlane();//判断飞机是否与敌机重叠
    45. void GameOver();//游戏失败
    46. void Pause();// 该成员函数用来使得游戏暂停
    47. void Shoot();//发射子弹
    48. void bulletMove();//子弹移动
    49. void drawThisBulletToNull(COORD c);//画出失效子弹
    50. void judgeEnemy();//判断子弹是否击中敌机
    51. void drawThisEnemyToNull(Frame f); //击败的敌机清空
    52. void enemyMove();//敌机移动
    53. void printScore();//输出分数
    54. };
    55. //主菜单
    56. int drawMenu();
    57. //隐藏光标
    58. void HideCursor();
    59. void SetPos(int i, int j);//设置光标
    60. COORD random(COORD a, COORD b);//产生随机敌机位置
    61. void drawFrame(COORD a, COORD b, char row, char col);//画出敌机
    62. //把第y行,[x1, x2) 之间的坐标填充为 ch
    63. void drawRow(int y, int x1, int x2, char ch);
    64. //把第x列,[y1, y2] 之间的坐标填充为 ch
    65. void drawCol(int x, int y1, int y2, char ch);
    66. // 绘制游戏界面
    67. void drawPlaying();
    68. void drawFrame(Frame frame, char row, char col);//画坠毁后的战机
    69. // 该函数用来判断战机的某一部分是否与敌机有接触
    70. bool judgeCoordInFrame(Frame frame, COORD spot);
    71. void drawRow(COORD a, COORD b, char ch);

    注释都在上边了,看不懂的可以私聊博主。

    然后,我们来做游戏的另一个函数,也就是敌机和战机✈的外观和移动函数,这个函数是游戏的核心,没有了这个函数,那这个游戏就像一个图片,只能呈现出游戏的界面,而不能做出移动的效果,会让人第一个感觉就是一个字——丑。

    战机和敌机的外观及其移动方式

    首先,我们先打开一个黑框(可以用CMD代替):

    7e5d6d1847c54a2fbecf99a67a2c088e.png

    我们需要让敌机从上往下移动,而战机是需要玩家自己上下左右进行控制的。然后,我们先定义战机的外观:

    1. // 这个函数体类的代码其实就是为了初始化战机的十个部分的位置,战机的组成如下所示:
    2. // | 5
    3. // | 9
    4. // ***** 12034
    5. // *** 678
    6. // 第一排5个0的坐标依次对应了position[1]position[2]position[0]position[3]position[4]
    7. // 第二排三个0的坐标依次对应了position[6]position[7]position[8]
    8. // 两排0上面的两|的坐标从上往下依次对应了position[5]position[9]
    9. }
    10. void Game::drawPlane()
    11. {
    12. for (int i = 0; i < 9; i++)
    13. {
    14. SetPos(position[i].X, position[i].Y);
    15. if (i != 5)
    16. {
    17. cout << "*";
    18. }
    19. else if (i == 5)
    20. {
    21. cout << "|";
    22. }
    23. }
    24. }

    战机的样子在上面的代码块注释里面应该能看到,我们用WinAPI和for循环语句进行战机的输入与输出。

    然后,我们来绘制敌机的样子:

    1. // 接下来要根据敌机的左上角坐标和右下角坐标画出敌机,
    2. // 显然,敌机的外形如下所示:
    3. // --
    4. // | |
    5. // --
    6. void Game::drawEnemy()
    7. {
    8. for (int i = 0; i < 8; i++)
    9. {
    10. drawFrame(enemy[i].position[0], enemy[i].position[1], '-', '|');
    11. }
    12. }

    敌机的代码和样子都在上面了,我们用“-”和“|”来绘制敌机。

    其他的移动代码和子弹等角色的代码有“亿”点复杂,如果我在这里讲的话,人家CSDN要打电话给我了。(因为如果写的话,这篇博文估计要5万多字)

    现在供上角色的代码(game.cpp):

    1. #include"game.h"
    2. #include
    3. #include
    4. Game::Game()
    5. {
    6. // 调用类成员函数来进行初始化
    7. initPlane();
    8. initBullet();
    9. initEnemy();
    10. // 初始化四个int型数据成员,采用赋值的方式进行初始化
    11. // string类型的数据成员title没有进行初始化,因为:
    12. // string本身就是一个标准库类类型,它的类定义中设置了默认构造函数,
    13. // 这些默认构造函数会将对象初始化为合理的默认状态,
    14. // string的默认构造函数会产生空字符串,相当于"" 。
    15. this->score = 0;
    16. rank = 25;
    17. rankf = 25;
    18. flag_rank = 0;
    19. }
    20. void Game::initPlane()
    21. {
    22. COORD centren;
    23. centren.X = 39;
    24. centren.Y = 22;
    25. position[0].X = position[5].X = position[7].X = position[9].X = centren.X;
    26. position[1].X = centren.X - 2;
    27. position[2].X = position[6].X = centren.X - 1;
    28. position[3].X = position[8].X = centren.X + 1;
    29. position[4].X = centren.X + 2;
    30. for (int i = 0; i <= 4; i++)
    31. {
    32. position[i].Y = centren.Y;
    33. }
    34. for (int i = 6; i <= 8; i++)
    35. {
    36. position[i].Y = centren.Y + 1;
    37. }
    38. position[5].Y = centren.Y - 1;
    39. position[9].Y = centren.Y - 2;
    40. // 这个函数体类的代码其实就是为了初始化战机的十个部分的位置,战机的组成如下所示:
    41. // | 5
    42. // | 9
    43. // ***** 12034
    44. // *** 678
    45. // 第一排5个0的坐标依次对应了position[1]position[2]position[0]position[3]position[4]
    46. // 第二排三个0的坐标依次对应了position[6]position[7]position[8]
    47. // 两排0上面的两|的坐标从上往下依次对应了position[5]position[9]
    48. }
    49. void Game::drawPlane()
    50. {
    51. for (int i = 0; i < 9; i++)
    52. {
    53. SetPos(position[i].X,position[i].Y);
    54. if (i != 5)
    55. {
    56. cout << "*";
    57. }
    58. else if (i == 5)
    59. {
    60. cout << "|";
    61. }
    62. }
    63. }
    64. // 这个成员函数通过将战机的每个坐标处输出" "来代替"0"和"|",
    65. // 来达到将战机消除的目的。
    66. void Game::drawPlaneToNull()
    67. {
    68. for (int i = 0; i < 9; i++)
    69. {
    70. SetPos(position[i].X, position[i].Y);
    71. cout << " ";
    72. }
    73. }
    74. // 该成员函数用来初始化子弹,
    75. // 即将每个子弹的Y坐标初始化为30(bullet[i].Y = 30)来表示子弹处于失效状态
    76. void Game::initBullet()
    77. {
    78. for (int i = 0; i < 10; i++)
    79. {
    80. bullet[i].Y = 30;
    81. }
    82. }
    83. // 该成员函数用来画出子弹
    84. // 首先检查每颗子弹的有效性,如果子弹有效,则定位到该子弹的坐标处,输出 "^",表示该子弹,
    85. // 如果子弹是无效的,则不绘制
    86. void Game::drawBullet()
    87. {
    88. for (int i = 0; i < 10; i++)
    89. {
    90. if (bullet[i].Y != 30)
    91. {
    92. SetPos(bullet[i].X,bullet[i].Y);
    93. cout << "^";
    94. }
    95. }
    96. }
    97. //子弹失效
    98. void Game::drawBulletToNull()
    99. {
    100. for (int i = 0; i < 10; i++)
    101. if (bullet[i].Y != 30)
    102. {
    103. SetPos(bullet[i].X, bullet[i].Y + 1);
    104. cout << " ";
    105. }
    106. }
    107. // 这个函数用来初始敌机的位置,
    108. // 屏幕当中只能同时存在八架敌机,
    109. // 且每架敌机用如下结构体Frame来表示,如下所示:
    110. // typedef struct Frame
    111. // {
    112. // COORD position[2];
    113. // int flag;
    114. // }Frame;
    115. COORD random(COORD a, COORD b)
    116. {
    117. int x = rand() % (a.X - b.X) + a.X;
    118. int y = rand() % (a.Y - b.Y) + a.Y;
    119. COORD c = { x,y };
    120. return c;
    121. }
    122. void Game::initEnemy()
    123. {
    124. COORD a = { 1, 1 };
    125. COORD b = { 45, 15 };
    126. for (int i = 0; i < 8; i++)
    127. {
    128. enemy[i].position[0] = random(a, b);
    129. // random(a, b)是调用了一个重载的函数,它表示在坐标a、b之间的矩形框
    130. // 内随机生成一个坐标值,并将该坐标值作为敌机的左上角的坐标。
    131. // enemy[i].position[0]中是一个Frame结构体类型的变量,存放了敌机i的左上角的坐标。
    132. enemy[i].position[1].X = enemy[i].position[0].X + 3;
    133. enemy[i].position[1].Y = enemy[i].position[0].Y + 2;
    134. // enemy[i].position[1]也中是一个Frame结构体类型的变量,存放了敌机i的右下角的坐标。
    135. }
    136. }
    137. // 接下来要根据敌机的左上角坐标和右下角坐标画出敌机,
    138. // 显然,敌机的外形如下所示:
    139. // --
    140. // | |
    141. // --
    142. void Game::drawEnemy()
    143. {
    144. for (int i = 0; i < 8; i++)
    145. {
    146. drawFrame(enemy[i].position[0], enemy[i].position[1], '-', '|');
    147. }
    148. }
    149. // 将敌机消除,通过输出空白的方式
    150. void Game::drawEnemyToNull()
    151. {
    152. for (int i = 0; i < 8; i++)
    153. {
    154. drawFrame(enemy[i].position[0], enemy[i].position[1], ' ', ' ');
    155. }
    156. }
    157. //隐藏光标
    158. void HideCursor()
    159. {
    160. CONSOLE_CURSOR_INFO cursor_info = { 1,0 };//第二个值0表示隐藏光标
    161. SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
    162. }
    163. void SetPos(int i, int j)//设置坐标点位(光标)
    164. {
    165. HANDLE hout;
    166. COORD coord;
    167. coord.X = i;
    168. coord.Y = j;
    169. hout = GetStdHandle(STD_OUTPUT_HANDLE);
    170. SetConsoleCursorPosition(hout, coord);
    171. }
    172. //左上角坐标、右下角坐标、用row填充行、用col填充列
    173. void drawFrame(COORD a, COORD b, char row, char col)
    174. {
    175. drawRow(a.Y, a.X + 1, b.X - 1, row);
    176. drawRow(b.Y, a.X + 1, b.X - 1, row);
    177. drawCol(a.X, a.Y + 1, b.Y - 1, col);
    178. drawCol(b.X, a.Y + 1, b.Y - 1, col);
    179. }
    180. //把第y行,[x1, x2) 之间的坐标填充为 ch
    181. void drawRow(int y, int x1, int x2, char ch)
    182. {
    183. SetPos(x1, y);
    184. for (int i = 0; i <= (x2 - x1); i++)
    185. {
    186. cout << ch;
    187. }
    188. }
    189. //把第x列,[y1, y2] 之间的坐标填充为 ch
    190. void drawCol(int x, int y1, int y2, char ch)
    191. {
    192. int y = y1;
    193. while (y != y2 + 1)
    194. {
    195. SetPos(x, y);
    196. cout << ch;
    197. y++;
    198. }
    199. }
    200. //主菜单绘制
    201. int drawMenu()
    202. {
    203. SetPos(30,1);
    204. cout << "飞 机 大 战";
    205. drawRow(3, 0, 79, '-');
    206. drawRow(5, 0, 79, '-');
    207. SetPos(28, 4);
    208. cout << "w 和 s选择,k确定";
    209. int j = 11;
    210. SetPos(12, j);
    211. cout << ">>";
    212. SetPos(15, 11);
    213. cout << "1. 简单的敌人";
    214. SetPos(15, 13);
    215. cout << "2. 冷酷的敌人";
    216. drawRow(20, 0, 79, '-');
    217. SetPos(47, 11);
    218. cout << "简单的敌人:";
    219. SetPos(47, 13);
    220. cout << "简单敌人有着较慢的移动速度。";
    221. SetPos(30, 21);
    222. cout << "copy right@风兮木萧";
    223. drawRow(22, 0, 79, '-');
    224. while (true)
    225. {
    226. if (_kbhit())
    227. {
    228. char x = _getch();
    229. switch (x)
    230. {
    231. case'w':
    232. {
    233. if (j == 13)
    234. {
    235. SetPos(12, j);
    236. cout << " ";
    237. j = 11;
    238. SetPos(12, j);
    239. cout << ">>";
    240. SetPos(51, 13);
    241. cout << "            ";
    242. SetPos(47, 11);
    243. cout << "简单的敌人:";
    244. SetPos(51, 13);
    245. cout << "简单敌人有着较慢的移动速度,比较容易对付";
    246. }
    247. break;
    248. }
    249. case's':
    250. {
    251. if (j == 11)
    252. {
    253. SetPos(12, j);
    254. cout << " ";
    255. j = 13;
    256. SetPos(12, j);
    257. cout << ">>";
    258. SetPos(51, 13);
    259. cout << "               ";
    260. SetPos(47, 11);
    261. cout << "冷酷的敌人:";
    262. SetPos(51, 13);
    263. cout << "冷酷的敌人移动速度较快,难对付哟。";
    264. }
    265. break;
    266. }
    267. case 'k':
    268. {
    269. if (j == 11)//源代码为8?
    270. return 1;
    271. else
    272. return 2;
    273. }
    274. }
    275. }
    276. }
    277. return 0;
    278. }
    279. void drawFrame(int x1, int y1, int x2, int y2, char row, char col)
    280. {
    281. COORD a = { x1, y1 };
    282. COORD b = { x2, y2 };
    283. drawFrame(a, b, row, col);
    284. }
    285. // 绘制游戏界面
    286. void drawPlaying()
    287. {
    288. drawFrame(0, 0, 48, 24, '=', '|');// draw map frame主界面
    289. drawFrame(49, 0, 79, 4, '-', '|');// draw output frame 状态界面
    290. drawFrame(49, 4, 79, 9, '-', '|');// draw score frame 分数界面
    291. drawFrame(49, 9, 79, 20, '-', '|');// draw operate frame 操作界面
    292. drawFrame(49, 20, 79, 24, '-', '|');// draw other message frame 提示界面
    293. SetPos(52, 6);
    294. cout << "得分:";
    295. SetPos(52, 7);
    296. cout << "称号:";
    297. SetPos(52, 11);
    298. cout << "操作方式:";
    299. SetPos(52, 13);
    300. cout << " a,s,d,w:控制战机移动。";
    301. SetPos(52, 15);
    302. cout << " p:暂停游戏。";
    303. SetPos(52, 17);
    304. cout << " e:退出游戏。";
    305. SetPos(52, 22);
    306. cout << " 游戏虽好玩,不要贪多哦 ";
    307. }
    308. // 该成员函数用过响应战机的一个动作
    309. // a,s,w,d,来控制战机的移动
    310. void Game::planeMove(char x)
    311. {
    312. if (x == 'a')
    313. {
    314. if (position[1].X != 1)
    315. {
    316. for (int i = 0; i <= 9; i++)
    317. {
    318. position[i].X -= 2;
    319. }
    320. }
    321. }
    322. // 如果玩家按下 'a' 键,说明玩家想让战机往左移动一个距离(2个单位),
    323. // 首先检测,战机的最左侧的位置坐标(即position[1].X)有没有达到左边界,
    324. // 如果到达了边界,那就不做出移动;如果没有达到边界,则将战机10个部分的X值减小2。
    325. if (x == 's')
    326. {
    327. if (position[7].Y != 23)
    328. {
    329. for (int i = 0; i <= 9; i++)
    330. {
    331. position[i].Y += 1;
    332. }
    333. }
    334. }
    335. // 如果玩家按下 's' 键,说明玩家想让战机往下移动一个距离(1个单位),
    336. // 首先检测,战机的最底部的位置坐标(即position[6].Y或者position[7].Y或者position[8].Y)有没有达到下边界,
    337. // 如果到达了边界,那就不做出移动;如果没有达到边界,则将战机10个部分的Y值增加1。
    338. if (x == 'd' && (position[4].X != 47))
    339. {
    340. for (int i = 0; i <= 9; i++)
    341. {
    342. position[i].X += 2;
    343. }
    344. }
    345. // 如果玩家按下 'd' 键,说明玩家想让战机往右移动一个距离(2个单位),
    346. // 首先检测,战机的最右侧的位置坐标(即position[4].X)有没有达到右边界,
    347. // 如果到达了边界,那就不做出移动;如果没有达到边界,则将战机10个部分的X值增加2。
    348. if (x == 'w'&&(position[5].Y != 3))
    349. {
    350. for (int i = 0; i <= 9; i++)
    351. {
    352. position[i].Y -= 1;
    353. }
    354. }
    355. // 如果玩家按下'w'键,说明玩家想让战机往上移动一个距离(1个单位),
    356. // 首先检测,战机的最顶部的位置坐标(即position[5].Y)有没有达到上边界,
    357. // 如果到达了边界,那就不做出移动;如果没有达到边界,则将战机10个部分的Y值减少1。
    358. }
    359. // 该函数用来判断战机的某一部分是否与敌机有接触
    360. // 如果与敌机有接触在判断为坠毁
    361. bool judgeCoordInFrame(Frame frame, COORD spot)
    362. {
    363. 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))
    364. {
    365. return true;
    366. }
    367. return false;
    368. }
    369. void drawFrame(Frame frame, char row, char col)
    370. {
    371. COORD a = frame.position[0];
    372. COORD b = frame.position[1];
    373. drawFrame(a, b, row, col);
    374. }
    375. //游戏结束
    376. void Game::GameOver()
    377. {
    378. system("cls");
    379. COORD p1 = { 28,9 };
    380. COORD p2 = { 53,15 };
    381. drawFrame(p1, p2, '=', '|');
    382. SetPos(36, 12);
    383. string str = "Game Over!";
    384. for (int i = 0; i < str.size(); i++)
    385. {
    386. Sleep(80);
    387. cout << str[i];
    388. }
    389. Sleep(1000);
    390. system("cls");
    391. drawFrame(p1, p2, '=', '|');
    392. SetPos(31, 11);
    393. cout << "击落敌机:" << score / 5 << " 架";
    394. SetPos(31, 12);
    395. cout << "得  分:" << score;
    396. SetPos(31, 13);
    397. cout << "获得称号:" << title;
    398. SetPos(30, 18);
    399. Sleep(1000);
    400. cout << "继续? 是(y)| 否(n)";
    401. as://goto 语句标签 直接跳转至此
    402. char x = _getch();
    403. if (x == 'n')
    404. {
    405. exit(0);
    406. }
    407. else if (x == 'y')
    408. {
    409. system("cls");
    410. Game game;
    411. int a = drawMenu(); // 绘制游戏开始界面主菜单
    412. if (a == 2)
    413. game.rank = 20;
    414. system("cls");
    415. drawPlaying(); // 绘制游戏界面框架
    416. game.Playing();
    417. }
    418. else
    419. goto as;
    420. }
    421. // 该成员函数用来判断战机是否坠毁,
    422. // 依次判断每架敌机与战机的每个部分是否有接触,
    423. // 如果有接触,则表示战机坠毁
    424. void Game::judgePlane()
    425. {
    426. for (int i = 0; i < 8; i++)
    427. {
    428. for (int j = 0; j < 9; j++)
    429. // 此处的实参position[j]是指战机的10个部分的COORD坐标,
    430. // 类中的成员函数可以访问数据成员变量。
    431. // 此处也可以写成this-> position[j],因为
    432. // 成员函数具有一个附加的隐含形参,即指向该类对象的一个指针,
    433. // 这个隐含形参命名为this,与调用成员函数的对象绑定在一起。
    434. // 成员函数不能定义this形参,而是由编译器隐含地定义。
    435. // 成员函数的函数体可以显式使用this指针,但不是必须这么做。
    436. if (judgeCoordInFrame(enemy[i], position[j]))
    437. {
    438. SetPos(62, 1);
    439. cout << "坠毁";
    440. drawFrame(enemy[i], '+', '+');
    441. // 将与战机相撞的敌机的形状绘制为:
    442. // ++
    443. // + +
    444. // ++
    445. Sleep(1000);
    446. GameOver();
    447. break;
    448. }
    449. }
    450. }
    451. // 该成员函数用来使得游戏暂停
    452. void Game::Pause()
    453. {
    454. SetPos(61, 2);
    455. cout << " ";
    456. SetPos(61, 2);
    457. cout << "暂停中...";
    458. // 当出现"暂停中..."的提示以后,程序不停的接收按下的按键,
    459. // 当按下'p'键以后,说明要退出暂停状态,此时需要清除"暂停中..."的提示
    460. // 通过输出空白 " "来将其覆盖,达到效果
    461. char c = _getch();
    462. while (c != 'p')
    463. {
    464. c = _getch();
    465. }
    466. SetPos(61, 2);
    467. cout << " ";
    468. }
    469. // 这个成员函数用来响应一次射击操作,
    470. // 也就是,当游戏中的时候,玩家按下"k"键,就执行该函数。
    471. // 由于子弹是由COORD bullet[10]定义的,因此同一时刻,界面内只能有10颗子弹同时出现。
    472. // 如果界面内不够10颗子弹,按下"k"键后战机应该发射出一颗子弹,
    473. // 于是,依次遍历10颗子弹,当遇到第一颗失效的子弹后,
    474. // 立即将该子弹赋予新的坐标(战机的炮口,也就是(position[5].X,position[5].Y - 1)),
    475. // 让其激活。然后退出for循环,函数执行完毕。
    476. void Game::Shoot()
    477. {
    478. for (int i = 0; i < 10; i++)
    479. {
    480. if (bullet[i].Y == 30)
    481. {
    482. bullet[i].X = position[5].X;
    483. bullet[i].Y = position[5].Y - 1;
    484. break;
    485. }
    486. }
    487. }
    488. void Game::drawThisBulletToNull(COORD c)
    489. {
    490. SetPos(c.X,c.Y);
    491. cout << " ";
    492. }
    493. // 此成员函数用来响应一次子弹的运动
    494. // 每次子弹运动,屏幕子弹的坐标都会出现变化,即
    495. // 先判断子弹是否有效(即判断语句if (bullet[i].Y != 30)),
    496. // 若子弹有效,将该子弹的Y坐标减少1,X坐标不变,
    497. // 检测子弹坐标更改之后是否达到上边界,如果达到上边界,则将该子弹从屏幕上擦除,
    498. // 同时,将该子弹置为失效状态,即 bullet[i].Y = 30。
    499. void Game::bulletMove()
    500. {
    501. for (int i = 0; i < 10; i++)
    502. {
    503. if (bullet[i].Y != 30)
    504. {
    505. bullet[i].Y -= 1;
    506. if (bullet[i].Y == 1)
    507. {
    508. COORD pos = { bullet[i].X, bullet[i].Y + 1 };
    509. drawThisBulletToNull(pos);
    510. bullet[i].Y = 30;
    511. }
    512. }
    513. }
    514. }
    515. //击败的敌机清空
    516. void Game::drawThisEnemyToNull(Frame f)
    517. {
    518. drawFrame(f, ' ', ' ');
    519. }
    520. // 该成员函数依次遍历每一架敌机,
    521. // 将每一架敌机依次与每一颗子弹进行检测,
    522. // 判断敌机是否与子弹有接触,如果有接触,则表示击中敌机,
    523. // 此时将敌机和子弹擦除,然后在界面顶部的位置处随机生成一架敌机
    524. void Game::judgeEnemy()
    525. {
    526. for (int i = 0; i < 8; i++)
    527. {
    528. for (int j = 0; j < 10; j++)
    529. {
    530. if (judgeCoordInFrame(enemy[i], bullet[j]))
    531. {
    532. score += 5;
    533. drawThisEnemyToNull(enemy[i]);
    534. COORD a = { 1, 1 };
    535. COORD b = { 45, 3 };
    536. enemy[i].position[0] = random(a, b);
    537. enemy[i].position[1].X = enemy[i].position[0].X + 3;
    538. enemy[i].position[1].Y = enemy[i].position[0].Y + 2;
    539. drawThisBulletToNull(bullet[j]);
    540. bullet[j].Y = 30;
    541. }
    542. }
    543. }
    544. }
    545. // 该成员函数用来响应一次敌机的移动
    546. // 界面上必须同时出现八架敌机,因此,
    547. // 如果有某架敌机运动到下边界处,则重置该敌机的坐标
    548. void Game::enemyMove()
    549. {
    550. for (int i = 0; i < 8; i++)
    551. {
    552. for (int j = 0; j < 2; j++)
    553. enemy[i].position[j].Y++;
    554. // 我们将每架敌机的左上角和右下角坐标的Y值增加1,
    555. // 表示该敌机向下走了一个距离
    556. // 检测向下走一个距离后的敌机的右下角坐标的Y值是否达到24,
    557. // 如果达到,代表敌机已经运动到下边界了,
    558. // 此时需要随机重置该敌机的坐标
    559. if (enemy[i].position[1].Y==24)
    560. {
    561. COORD a = { 1, 1 };
    562. COORD b = { 45, 3 };
    563. enemy[i].position[0] = random(a, b);
    564. enemy[i].position[1].X = enemy[i].position[0].X + 3;
    565. enemy[i].position[1].Y = enemy[i].position[0].Y + 2;
    566. }
    567. }
    568. }
    569. void Game::printScore()
    570. {
    571. if (score <= 120)
    572. {
    573. flag_rank = 1;
    574. }
    575. else if (score > 120 && score <= 360)
    576. {
    577. flag_rank = 2;
    578. }
    579. else if (score > 360 && score <= 480)
    580. {
    581. flag_rank = 3;
    582. }
    583. else if (score > 480)
    584. {
    585. flag_rank = 4;
    586. }
    587. SetPos(60, 6);
    588. cout << score;
    589. SetPos(60, 7);
    590. if (flag_rank == 1)
    591. {
    592. title = "初级飞行员";
    593. }
    594. else if (flag_rank == 2)
    595. {
    596. title = "中级飞行员";
    597. }
    598. else if (flag_rank == 3)
    599. {
    600. title = "高级飞行员";
    601. }
    602. else if (flag_rank == 4)
    603. {
    604. title = "王牌飞行员";
    605. }
    606. cout << title;
    607. }
    608. // 这个成员函数是游戏的主循环函数,
    609. // 定义了整个游戏过程。
    610. void Game::Playing()
    611. {
    612. drawEnemy();
    613. drawPlane();
    614. int flag_bullet = 0;
    615. int flag_enemy = 0;
    616. while (true)
    617. {
    618. Sleep(20);
    619. // 函数名:kbhit()(VC++6.0下为_kbhit())
    620. // 功能及返回值: 检查当前是否有键盘输入,若有则返回一个非0值,否则返回0
    621. // 用法:int kbhit(void);
    622. // 包含头文件: include
    623. // kbhit()在执行时,检测是否有按键按下,有按下返回非0值,没有按下则返回0,是非阻塞函数;
    624. // 不同于getch()的在执行时, 检测按下什么键, 如果不按键该函数不返回,也就不进行下一步操作,是阻塞函数。
    625. if (_kbhit())
    626. {
    627. char x = _getch();
    628. // getch()是编程中所用的函数,这个函数是一个不回显函数,
    629. // 当用户按下某个字符时,函数自动读取,无需按回车
    630. // getch()并非标准C中的函数,不存在C语言中。
    631. // 所在头文件是conio.h,而不是stdio.h。
    632. // 用ch = getch(); 会等待你按下任意键之后,把该键字符所对应的ASCII码赋给ch, 再执行下面的语句。
    633. if ('a' == x || 's' == x || 'd' == x || 'w' == x)
    634. {
    635. drawPlaneToNull(); // 将战机先擦除
    636. planeMove(x); // 根据所输入的操作符,对战机的坐标进行更改
    637. drawPlane(); // 访问类中的数据成员——战机的坐标,在新的坐标处重新绘制战机
    638. judgePlane(); // 判断战机是否有坠毁
    639. }
    640. // 在某一循环当中,如果检测到有'p'键按下,
    641. // 首先在右侧游戏界面输出"暂停中...",然后陷入while()循环一直等待'p'键再次按下,
    642. // 如果'p'键没有按下,就一直处在while()循环内,因此不能执行后面的程序,起到暂停的效果。
    643. else if ('p' == x)
    644. {
    645. Pause();
    646. }
    647. // 如果是检测到'k'键按下,则运行Shoot()函数,
    648. else if ('k' == x)
    649. {
    650. Shoot();
    651. }
    652. // 如果是检测到'k'键按下,则运行GameOver()函数,
    653. // GameOver()函数执行完毕后,执行break;语句跳出while循环(注意不是if (_kbhit()))。
    654. // break语句用于结束最近的while、do while、for或switch语句,并将程序的执行权传递给紧接在
    655. // 被终止语句之后的语句。
    656. else if ('e' == x)
    657. {
    658. //CloseHandle(MFUN)
    659. GameOver();
    660. break;
    661. }
    662. }
    663. // 接下来处理子弹
    664. // 判断子弹状态的程序一直在运行
    665. if (flag_bullet == 0)
    666. {
    667. bulletMove(); // 更新界面上有效子弹的坐标
    668. drawBulletToNull(); // 将处于旧坐标的子弹擦除
    669. drawBullet(); // 绘制出新坐标上的子弹
    670. judgeEnemy(); // 判断敌机是否被子弹击中
    671. }
    672. flag_bullet++;
    673. if (flag_bullet==1)
    674. {
    675. flag_bullet = 0;
    676. }
    677. // 接下来处理敌机
    678. if (flag_enemy==0)
    679. {
    680. drawEnemyToNull(); // 将所有的敌机都擦除
    681. enemyMove(); // 更新敌机的坐标
    682. drawEnemy(); // 绘制出处于新坐标上的敌机
    683. judgePlane(); // 判断敌机是否与战机接触
    684. }
    685. flag_enemy++;
    686. if (flag_enemy >= rank)
    687. {
    688. flag_enemy = 0;
    689. }
    690. /* 输出得分 */
    691. printScore();
    692. }
    693. }

    主函数总不需要我来详细讲解了吧,直接供上(main.cpp):

    1. #define _CRT_SECURE_NO_WARNINGS
    2. #include"game.h"
    3. using namespace std;
    4. int main()
    5. {
    6. system("title 飞机大战");
    7. srand((unsigned int)time(NULL));//随机时间种子
    8. HideCursor();//隐藏光标
    9. Game game;
    10. int a = drawMenu();
    11. if (a == 2)
    12. {
    13. game.rank = 20;
    14. }
    15. system("cls");
    16. drawPlaying();
    17. game.Playing();
    18. }

    文章来之不易,希望各位读者能打赏一下博主😉。

    升级版链接:飞机大战升级版

     

  • 相关阅读:
    iOS CocoaPod 打包:SDK开发、Pod组件生成等
    RK3568平台开发系列讲解(Linux系统篇)kernel config 配置解析
    makesense在线yolov5标注
    Linux进程间通信
    集群配置步骤_java培训
    BCD编码和ASCII码
    PAM从入门到精通(四)
    Java8 Stream流
    走进 Java 接口测试之简单解决写接口脏数据问题
    Thymeleaf流程控制语法
  • 原文地址:https://blog.csdn.net/Sjc158/article/details/132473091