基于MFC类库实现的飞机大战小游戏

Benjamin

发布日期: 2018-11-05 15:52:29 浏览量: 236
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

1 概述

1.1 简介

本次实训项目是做一个飞机大战的游戏,完成一个界面简洁、操作简单的桌面游戏。该飞机大战项目主要使用的是MFC编程,运用MFC中的类以及自己创建的类,设计好各个类之间的继承关系,实现飞机大战游戏的简单功能。

1.2 基本功能

  • 设置一个战机具有一定的速度,通过键盘,方向键可控制战机的位置,空格键发射子弹

  • 界面中敌机出现的位置,以及敌机炸弹的发射均为随机的,敌机与敌机炸弹均具有一定的速度,且随着关卡难度的增大,数量和速度均增加

  • 对于随机产生的敌机和敌机炸弹,若超过矩形区域,则释放该对象

  • 添加爆炸效果,包括战机子弹打中敌机爆炸、敌机炸弹打中战机爆炸、战机与敌机相撞爆炸以及战机子弹与敌机炸弹相撞爆炸四种爆炸效果。且爆炸发生后敌机、子弹、炸弹均消失,战机生命值减一

1.3 扩展功能

为游戏界面添加了背景图片,并在战机击中敌机、敌机击中战机、以及战机敌机相撞时均添加了背景音效。

  • 为游戏设置了不同的关卡,每个关卡难度不同,敌机与敌机炸弹的速度随着关卡增大而加快,进入第三关以后敌机从上下方均会直接向战机次发射炸弹。战机每打掉一个敌机则增加一分,同时为战机增加一个生命值,当战机得分超过100分则可进入下一关;每进入一关敌机速度都会加快,分别从上下两方飞出,在第四关和第五关有boss,分别以不同的方式发射子弹

  • 在游戏界面输出当前游戏进行信息,包括当前得分、当前关卡、生命值以及boss生命值

  • 增加了鼠标控制战机位置这一效果,战绩的位置随着鼠标的移动而移动

  • 实现了暂停游戏的功能,玩家可通过键盘上的‘Z’键,对游戏进行暂停。‘S’键开始

  • 实现了设置游戏难度的功能,可以通过菜单栏上的设置难度选项设置难度。同时菜单实现了查看游戏说明和重新开始的功能

2 相关技术

2.1 透明贴图

为了解决界面上的图片飞机等闪烁问题,采用了双缓冲技术。先定义一个位图对象和一个设备描述表对象,为设备描述表创建DC,再建立一个与屏幕设备描述表兼容的位图,将位图选入到内存设备描述表中,以后的每个飞机、子弹对象就画在设备描述表中,最后一起画在pDC上,绘图完成后要进行清理。

绘制透明位图的关键就是创建一个“掩码”位图(mask bitmap),这个“掩码”位图是一个单色位图,它是位图中图像的一个单色剪影。在MFC中,绘图需要使用设备描述表,透明贴图时需要创建两个内存设备描述表,一个是用于存放位图的设备描述表(imgDC),一个是用于存放“掩码”位图的设备描述表(maskDC)。在“掩码”位图设备描述表中制作“掩码”位图的方法是先创建一个单色的bitmap,放入掩码设备描述表(maskDC)中,然后使用拷贝粘贴的方式将存放有位图的设备描述表(imgDC)绘制到掩码设备描述表上,这样,掩码设备描述表显示的位图即是“掩码”位图。

整个实现过程如下:

  1. 创建一张大小与需要绘制图像相同的位图作为“掩码”位图

  2. 将新创建的“掩码”位图存储至掩码位图的设备描述表中

  3. 把位图设备描述表的背景设置成“透明色”,即不需要显示的颜色

  4. 复制粘贴位图到“掩码”位图的设备描述表中,这个时候“掩码”位图设备描述表中存放的位图与位图设备描述表中的位图一样

  5. 把需要透明绘制的位图与对话框绘图相应区域的背景进行逻辑异或操作绘制到对话框上

  6. 把“掩码”位图与这个时候对话框相应区域的背景进行逻辑与的操作

  7. 最后一步重复步骤5的操作,把需要透明绘制的位图与对话框绘图相应区域的背景进行逻辑异或操作绘制到对话框上

  8. 删除使用过的GDIObject,释放非空的指针,最后把新建的设备描述表也删除

2.2 Windows定时器技术

由于敌机和敌机的子弹、boss等是随机出现的,所以在new这些对象时要设置定时器,是指不同的定时器,才会产生时差的效果,这样就能实现了随机出现。在MFC的API函数中使用SetTimer()函数设置定时器,设置系统间隔时间,在OnTimer()函数中实现响应定时器的程序。

Windows定时器是一种输入设备,它周期性地在每经过一个指定的时间间隔后就通知应用程序一次。程序将时间间隔告诉Windows,然后Windows给程序发送周期性发生的WM_TIMER消息以表示时间到了。本程序中使用多个定时器,分别控制不同的功能。在MFC的API函数中使用SetTimer()函数设置定时器,设置系统间隔时间,在OnTimer()函数中实现响应定时器的程序。在程序结束时调用OnDestory()函数用KillTimer()函数删除定时器。

2.3 图像列表位置

爆炸效果是连续的显示一系列的图片。如果把每一张图片都显示出来的话,占用的时间是非常多的,必然后导致程序的可行性下降 ,而CImageList是一个“图象列表”是相同大小图象的集合,每个图象都可由其基于零的索引来参考。可以用来存放爆炸效果的一张图片,使用draw函数来绘制在某拖拉操作中正被拖动的图象,即可连续绘制出多张图片做成的爆炸效果。

图像列表控制(CImageList)是相同大小图像的一个集合,每个集合中均以0为图像的索引序号基数,图像列表通常由大图标或位图构成,其中包含透明位图模式。可以利用WINDOWS32位应用程序接口函数API来绘制、建立和删除图像,并能实现增加、删除、替换和拖动图像等操作。图像列表控制提供了控制图像列表的基本方法,如CimageList&imageList建立图像控制对象结构,Create 初始化图像列表并绑定对象。

2.4 获取矩形区域和判断是否相交

在产生爆炸效果时,需要获取两个对象所在位置的矩形区域,然后判断两个矩形区域是否相交,相交则产生爆炸,否则不产生爆炸。

首先,使用CRect定义一个对象,然后使用类的成员函数GetRect()函数,获取对象所在位置的矩形区域。然后使用IntersectRect(&,&))函数来判断两个矩形是否有重合的部分。如果有不为空,则返回非零值;否则,返回0。

2.5 CObject类和CObList类

MFC类库中提供了丰富的CObject类的成员函数,游戏中建立的多个类都是间接继承自CObject类,多个链表都是用到CObList类,此程序主要用到的成员函数如下:

  • 构造函数,为CObject指针构造一个空的列表

  • GetHead(),访问链表首部,返回列表中的首元素(列表不能为空)

  • AddTail(),在列表尾增加一个元素或另一个列表的所有元素

  • RemoveAll(),删除列表中所有的元素

  • GetNext(),返回列表中尾元素的位置

  • GetHeadPosition(),返回列表中首元素的位置

  • RemoveAt(),从列表中删除指定位置的元素

  • GetCount(),返回列表中的元素数

3 需求分析

3.1 功能需求分析

战机数量为1,由玩家通过键盘控制(方向键控制位置、空格键发射导弹)战机,导弹释放存在间隔,有一定的运行速度,导弹遇到敌机发生爆炸,敌机被炸毁,导弹消失,玩家得分,由计算机控制敌机自动向战机发动攻击,敌机数量随机,计算机生成敌机时随机选择类别,敌机从游戏区域的上下两端进入,左右位置随机,敌机行驶期间,不左右移动,不反向移动,当敌机到达战机下方时发射炸弹对战机进行攻击,运行线路为直线,方向为从下至上,或从下至上,不可左右移动。纵向由发射位置起至游戏区域结束,炸弹遇到战机时发生爆炸,战机被炸毁,炸弹消失,游戏结束,炸弹运行时有速度限制,爆炸时的声音。

3.2 数据需求分析

游戏中需要记录战机得分情况,所以具体的的分规则是战机击中敌机后得一分,战机被敌机击中则减一分生命值,战机若与敌机相撞则生命值减五分;战机击中boss后得一分,战机被boss击中则减一分生命值,战机若与boss相撞则生命值减五分;当战机生命值减为0时,游戏结束。

游戏中的数据大部分是随机的例如敌机的个数,发射子弹的个数,出现时间,发射子弹实际都是随机的,所以该游戏对数据的需求不是很明确。

3.3 行为需求分析

每一个函数都有特定的功能,也有一定的联系,大部分的函数是在OnTime函数和OnDraw函数中使用的,例如每个类都有的draw函数和move函数是必须要在这两个函数中使用的,否则每个对象的位置是不会随时间的变化而变化的。

4 总体设计与详细设计

4.1 系统模块划分

主要用到的类

  • CPlaneObject:基类

  • CPlane:战机类

  • Cenemy:敌机类

  • CBomb:战机子弹类

  • Cball:敌机子弹类

  • Cexplosion:爆炸类

  • Cboss:boss类

  • Clevles:设置难度用的事件处理类

各个类的关系如下

设计思路

首先,在OnInitinalUpdate函数里加载需要导入的图片资源文件,同时设置定时器;

然后在OnTimer函数中创建战机对象以及敌机、子弹、炮弹链表,再在OnDraw函数中把战机用draw函数画出来,依次遍历敌机、子弹、炮弹链表,使这些对象画在界面上;

然后再次执行OnTimer函数,依次遍历每一个链表,调用每个对象的move函数,使其移动,在移动的过程中要注意边界控制的问题;再次调用OnDraw函数使其改变的位置显示在界面上,当对象的位置在界面以外时,将其从链表中删除;

在OnTimer函数中判断碰撞问题,取得两个对象所在位置的矩形区域,如果两个区域相交,则产生爆炸,同时将对象从链表中删除,再次用OnDraw函数刷新界面。

飞机大战活动图

4.2 主要功能模块

4.2.1 敌机和子弹的移动

在这个游戏中的基本功能就是敌机从上方和下方两个不同的方向随机出发,然后随机发射子弹,要实现这个功能,就必须设置不同的定时器,让敌机不同时出现,不同时发子弹。首先,在OnTimer()创建一个敌机链表,new出两个不同方向的敌机,并将敌机添加到敌机链表中,再在OnDraw()函数中遍历的将敌机draw出来,再在OnTimer()函数中调用敌机对象中的move函数,使得敌机在游戏界面上移动起来。子弹的设计与敌机相似,但是必须在遍历敌机队列使其移动时,得到一个敌机对象后,就创建一个子弹对象,并将其初始位置设置为敌机的当前位置,然后将其添加到子弹队列中,再在OnDraw()函数中遍历的将子弹draw出来,再在OnTimer()函数中调用子弹对象中的move函数,使得子弹在游戏界面上移动起来。

4.2.2 添加爆炸效果

在战机打中敌机,敌机与战机子相撞,打死boss时都会产生爆炸效果,需要调用每个类的getRect函数取到两个对象的位置,再用IntersectRect函数判断是否有交集,若有则两个对象相撞,产生爆炸效果,若没有交集,则两个对象没有相撞。

爆炸效果是连续的显示一系列的图片。CImageList是一个“图象列表”是相同大小图象的集合,每个图象都可由其基于零的索引来参考。可以用CImageList的对象ImageList来存放爆炸效果的一张图片,使用draw函数来绘制在某拖拉操作中正被拖动的图象,即可连续绘制出多张图片做成的爆炸效果。在产生爆炸效果的同时用PlaySound函数添加声音效果。

4.3 扩展功能设计思路

4.3.1 追踪弹

在这个游戏中我设计了追踪弹,既有战机追踪敌机的追踪弹,也有敌机和boss追踪战机的追踪弹,在设计战机追中敌机的追踪弹时,采用的是追踪距离战机最近的敌机,但是由于位置始终在变化所以位置总是找不准,因此有时追踪的不是距离最近的敌机,准确率也不是很高,但是也能击中敌机。在设计敌机追踪战机的追踪弹时,由于战机只有一个位置比较好控制,所以准确率较高。

设计这个追踪弹的主要思路是,在子弹move时,不再是直上直下的飞,而是将当前你要追踪的位置作为参数传给move,move函数计算你的位置和要追踪的位置的斜率,沿着这条直线的方向移动子弹,子弹就实现了追踪的功能。

4.3.2 战机和boss的多种子弹

战机按空格键发射双排子弹,主要是在OnTimer()函数里创建了一个子弹链表,每隔一段时间就将两个子弹对象添加到链表中,这两个子弹的初始位置是在飞机的两个机翼上,然后再在OnDraw()函数中调用draw函数,将子弹画在游戏界面上,然后再在OnTimer()函数嗲有子弹的move函数,使得子弹在界面上移动起来。战机和boss也可以发射三列和五列的散弹,分别朝着三个和五个不同的方向发射子弹,主要涉及是重写了子弹的构造函数,在构造函数中传了一个参数type,如果type是单数则向左偏,type是双数则向右偏,type等于0则沿竖直方向发射,在偏转时向左和右方向偏转的速度与type有关,因此产生了分散发射的效果。

5 编码实现

  1. void CPlaneGameView::OnDraw(CDC* pDC)
  2. {
  3. CPlaneGameDoc* pDoc = GetDocument();
  4. ASSERT_VALID(pDoc);
  5. if (!pDoc)
  6. return;
  7. // TODO: 在此处为本机数据添加绘制代码
  8. this->GetClientRect(&lpRect);//取得当前的窗口大小
  9. //加载图片背景
  10. CBitmap bitmap_BackGround;
  11. bitmap_BackGround.LoadBitmap(IDB_BACKGROUND);
  12. BITMAP bimap2;//位图图像
  13. bitmap_BackGround.GetBitmap(&bimap2);
  14. CDC cdc_BackGround;//定义一个兼容的DC
  15. cdc_BackGround.CreateCompatibleDC(pDC);//创建DC
  16. CBitmap*Old=cdc_BackGround.SelectObject(&bitmap_BackGround);
  17. //定义一个位图对象
  18. //双缓冲
  19. CBitmap MemBitmap;
  20. CDC MemDC;
  21. //这时还不能绘图,因为没有位图的设备描述表是不能绘图的
  22. //只有选入了位图的设备描述表才有地方绘图,画到指定的位图上
  23. MemDC.CreateCompatibleDC(pDC);
  24. //下面建立一个与屏幕设备描述表(或者内存设备描述表)兼容的位图
  25. MemBitmap.CreateCompatibleBitmap(pDC,lpRect.right,lpRect.bottom);
  26. CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);
  27. //将位图选入到内存设备描述表
  28. MemDC.StretchBlt(0,0,lpRect.Width(),lpRect.Height(),&cdc_BackGround,0,0,bimap2.bmWidth,bimap2.bmHeight,SRCCOPY);
  29. //绘图完成后的清理
  30. plane.draw(&MemDC);
  31. //画战机的子弹
  32. POSITION pos,position;
  33. Cbomb *bombomb1;
  34. for(pos=pDoc->bomblist1.GetHeadPosition(); pos!=NULL;)
  35. {
  36. bombomb1=(Cbomb*)pDoc->bomblist1.GetNext(pos);
  37. bombomb1->draw(&MemDC);
  38. }
  39. //画敌机
  40. Cenemy *enemy1,*enemy2;
  41. for(pos=pDoc->enemylist1.GetHeadPosition(); pos!=NULL;)
  42. {
  43. enemy1=(Cenemy*)pDoc->enemylist1.GetNext(pos);
  44. enemy1->draw(&MemDC);
  45. }
  46. for(pos=pDoc->enemylist2.GetHeadPosition(); pos!=NULL;)
  47. {
  48. enemy2=(Cenemy*)pDoc->enemylist2.GetNext(pos);
  49. enemy2->draw(&MemDC);
  50. }
  51. //画敌机的子弹
  52. Cball *ball1,*ball2;
  53. for(pos=pDoc->balllist1.GetHeadPosition(); pos!=NULL;)
  54. {
  55. ball1=(Cball*)pDoc->balllist1.GetNext(pos);
  56. ball1->draw(&MemDC);
  57. }
  58. for(pos=pDoc->balllist2.GetHeadPosition(); pos!=NULL;)
  59. {
  60. ball2=(Cball*)pDoc->balllist2.GetNext(pos);
  61. ball2->draw(&MemDC);
  62. }
  63. //画爆炸
  64. Cexplosion *explosion;
  65. for(pos=pDoc->explosionlist.GetHeadPosition(); pos!=NULL;)
  66. {
  67. position=pos;
  68. explosion=(Cexplosion*)pDoc->explosionlist.GetNext(pos);
  69. if(explosion->draw(&MemDC)==0){
  70. pDoc->explosionlist.RemoveAt(position);
  71. delete explosion;
  72. }
  73. }
  74. //画boss和boss的子弹
  75. if(level>=4){
  76. if(boss!=NULL){
  77. boss->draw(&MemDC);
  78. }
  79. Cball *balllist3;
  80. for(pos=pDoc->balllist3.GetHeadPosition(); pos!=NULL;)
  81. {
  82. balllist3=(Cball*)pDoc->balllist3.GetNext(pos);
  83. balllist3->draw(&MemDC);
  84. }
  85. }
  86. CString t;
  87. wchar_t ttt[100],tt[100],tttt[100],ttttt[100];
  88. wsprintf(ttt,L"当前得分:%d",score);
  89. wsprintf(tt,L"当前生命值:%d",lifenum);
  90. wsprintf(tttt,L"当前关卡:%d",level);
  91. wsprintf(ttttt,L"当前boss生命值:%d",bosslife);
  92. t.SetString(tttt);
  93. SetBkMode(MemDC,TRANSPARENT);
  94. SetTextColor(MemDC,RGB(255,255,255));
  95. MemDC.TextOut(0,0,t);
  96. t.SetString(tt);
  97. MemDC.TextOut(0,20,tt);
  98. CPen pen(PS_SOLID,3,RGB(255,0,0)),*pOldPen;
  99. CBrush brush(RGB(255,0,0)),*pOldBrush;
  100. pOldBrush=(CBrush*)pDC->SelectObject(&brush);
  101. pOldPen=(CPen*)pDC->SelectObject(&pen);
  102. MemDC.Rectangle(120,20,120+lifenum,40);
  103. pDC->SelectObject(pOldPen);
  104. pDC->SelectObject(pOldBrush);
  105. t.SetString(ttttt);
  106. MemDC.TextOut(0,40,t);
  107. MemDC.Rectangle(130,40,130+bosslife,60);
  108. t.SetString(ttt);
  109. MemDC.Rectangle(90,60,90+score,80);
  110. MemDC.TextOut(0,60,t);
  111. pDC->BitBlt(lpRect.left,lpRect.top,lpRect.right,lpRect.bottom,&MemDC,0,0,SRCCOPY);
  112. MemBitmap.DeleteObject();
  113. MemDC.DeleteDC();
  114. }
  115. void CPlaneGameView::OnTimer(UINT_PTR nIDEvent)
  116. {
  117. // TODO: 在此添加消息处理程序代码和/或调用默认值
  118. CPlaneGameDoc* pDoc = GetDocument();
  119. ASSERT_VALID(pDoc);
  120. if (!pDoc)
  121. return;
  122. if(level>=4&&boss==NULL){
  123. boss = new Cboss;
  124. }
  125. plane.setSpeed(level*5);
  126. if(GetKeyState(VK_LEFT)<0&&isPause==0)
  127. {
  128. if(plane.getPosition().x>lpRect.left)
  129. {
  130. plane.move(1);
  131. }
  132. }
  133. if(GetKeyState(VK_RIGHT)<0&&isPause==0)
  134. {
  135. if(plane.getPosition().x+50<lpRect.right)
  136. {
  137. plane.move(2);
  138. }
  139. }
  140. if(GetKeyState(VK_UP)<0&&isPause==0)
  141. {
  142. if(plane.getPosition().y>lpRect.top)
  143. {
  144. plane.move(3);
  145. }
  146. }
  147. if(GetKeyState(VK_DOWN)<0&&isPause==0)
  148. {
  149. if(plane.getPosition().y+100<lpRect.bottom)
  150. {
  151. plane.move(4);
  152. }
  153. }
  154. if(GetKeyState('Z')<0){//暂停
  155. isPause=1;
  156. }
  157. if(GetKeyState('S')<0){//开始
  158. isPause=0;
  159. }
  160. if(isPause == 0)
  161. {
  162. CPoint point =plane.getPosition();
  163. switch(nIDEvent)
  164. {
  165. case 1:
  166. {
  167. //战机发射子弹
  168. if(GetKeyState(VK_SPACE)<0)
  169. {
  170. Cbomb *bomb1 = new Cbomb();
  171. Cbomb *bomb2 = new Cbomb();
  172. bomb1->setPosition(plane.pos.x,plane.pos.y);
  173. bomb2->setPosition(plane.pos.x+40,plane.pos.y);
  174. pDoc->bomblist1.AddHead(bomb1);
  175. pDoc->bomblist1.AddHead(bomb2);
  176. }
  177. if(GetKeyState('X')<0)
  178. {
  179. Cbomb *bomb1 = new Cbomb(0,1);
  180. bomb1->setPosition(plane.pos.x+25,plane.pos.y-60);
  181. pDoc->bomblist1.AddHead(bomb1);
  182. }
  183. if(GetKeyState(VK_CONTROL)<0&&level>=4)
  184. {
  185. for(int i=0;i<3;i++){
  186. Cbomb *bomb1 = new Cbomb(i,0);
  187. bomb1->setPosition(plane.pos.x+20,plane.pos.y);
  188. pDoc->bomblist1.AddHead(bomb1);
  189. }
  190. }
  191. if(GetKeyState(VK_SHIFT)<0&&level==5)
  192. {
  193. for(int i=0;i<5;i++){
  194. Cbomb *bomb1 = new Cbomb(i,0);
  195. bomb1->setPosition(plane.pos.x+20,plane.pos.y);
  196. pDoc->bomblist1.AddHead(bomb1);
  197. //PlaySound((LPCTSTR)IDR_WAVE3, AfxGetInstanceHandle(), SND_RESOURCE |SND_ASYNC);
  198. }
  199. }
  200. //战机子弹移动
  201. POSITION pos,position;
  202. Cbomb *bomb1;
  203. int i=0;
  204. for(pos=pDoc->bomblist1.GetHeadPosition(); pos!=NULL;)
  205. {
  206. position=pos;
  207. bomb1=(Cbomb*)pDoc->bomblist1.GetNext(pos);
  208. if((bomb1->getPosition().y)>lpRect.top){
  209. if(GetKeyState('X')<0){
  210. POSITION pos1,tpos,tpos2;
  211. double min=999999,d;
  212. Cenemy *enemy1=new Cenemy(-1);
  213. for(pos1=pDoc->enemylist1.GetHeadPosition();pos1!=NULL;){
  214. tpos=pos1;tpos2=pDoc->enemylist1.GetHeadPosition();
  215. enemy1=(Cenemy*)pDoc->enemylist1.GetNext(pos1);
  216. d=(enemy1->getPosition().x-bomb1->getPosition().x)*(enemy1->getPosition().x-bomb1->getPosition().x)+(enemy1->getPosition().y-bomb1->getPosition().y)*(enemy1->getPosition().y-bomb1->getPosition().y) ;
  217. d=sqrt(d);
  218. if (d<min)
  219. {
  220. min=d;
  221. tpos2=tpos;
  222. }
  223. bomb1->move(((Cenemy*)pDoc->enemylist1.GetNext(tpos2))->getPosition());
  224. }
  225. }
  226. else
  227. bomb1->move();
  228. }
  229. else
  230. {
  231. if(pDoc->bomblist1.GetHeadPosition()==position){
  232. pDoc->bomblist1.RemoveHead();
  233. delete bomb1;
  234. }
  235. else if(pDoc->bomblist1.GetTailPosition()==position){
  236. pDoc->bomblist1.RemoveTail();
  237. delete bomb1;
  238. }
  239. else if(pDoc->bomblist1.GetAt(position)!=NULL)
  240. {
  241. pDoc->bomblist1.RemoveAt(position);
  242. delete bomb1;
  243. }
  244. }
  245. }
  246. this->Invalidate(TRUE);
  247. break;
  248. }
  249. case 2:
  250. {
  251. //产生敌机
  252. POSITION pos1,position1;
  253. int direction1=-1,direction2=1;
  254. Cenemy *enemy1 = new Cenemy(direction1);
  255. Cenemy *enemy2 = new Cenemy(direction2);
  256. enemy1->setPosition(rand()%1000,lpRect.top+35);
  257. enemy2->setPosition(rand()%1000,lpRect.bottom-35);
  258. if(level<=2){
  259. if(pDoc->enemylist1.GetCount()<5*level&&rand()%10==level)
  260. {
  261. enemy1->setSpeed(1+2*level);
  262. pDoc->enemylist1.AddHead(enemy1);
  263. }
  264. if(pDoc->enemylist2.GetCount()<5*level&&rand()%10==level)
  265. {
  266. enemy2->setSpeed(1+2*level);
  267. pDoc->enemylist2.AddHead(enemy2);
  268. }
  269. }
  270. else{
  271. if(pDoc->enemylist1.GetCount()<10&&rand()%10==level)
  272. {
  273. enemy1->setSpeed(5);
  274. pDoc->enemylist1.AddHead(enemy1);
  275. }
  276. if(pDoc->enemylist2.GetCount()<10&&rand()%10==level)
  277. {
  278. enemy2->setSpeed(5);
  279. pDoc->enemylist2.AddHead(enemy2);
  280. }
  281. }
  282. //产生敌机的子弹,敌机移动
  283. for(pos1=pDoc->enemylist1.GetHeadPosition(); pos1!=NULL;)
  284. {
  285. position1=pos1;
  286. enemy1=(Cenemy*)pDoc->enemylist1.GetNext(pos1);
  287. if(pDoc->balllist1.GetCount()<=5){
  288. Cball *ball1 = new Cball();
  289. ball1->setPosition(enemy1->getPosition().x+20,enemy1->getPosition().y+30);
  290. ball1->setSpeed(level*2+30);
  291. pDoc->balllist1.AddHead(ball1);
  292. }
  293. if((enemy1->getPosition().y)<lpRect.bottom)
  294. enemy1->move(direction1);
  295. else
  296. {
  297. if(pDoc->enemylist1.GetHeadPosition()==position1){
  298. pDoc->enemylist1.RemoveHead();
  299. delete enemy1;
  300. }
  301. else if(pDoc->enemylist1.GetTailPosition()==position1){
  302. pDoc->enemylist1.RemoveTail();
  303. delete enemy1;
  304. }
  305. else if(pDoc->enemylist1.GetAt(position1)!=NULL)
  306. {
  307. pDoc->enemylist1.RemoveAt(position1);
  308. delete enemy1;
  309. }
  310. }
  311. }
  312. for(pos1=pDoc->enemylist2.GetHeadPosition(); pos1!=NULL;)
  313. {
  314. position1=pos1;
  315. enemy2=(Cenemy*)pDoc->enemylist2.GetNext(pos1);
  316. if(pDoc->balllist2.GetCount()<=5){
  317. Cball *ball2 = new Cball();
  318. ball2->setPosition(enemy2->getPosition().x+20,enemy2->getPosition().y-30);
  319. ball2->setSpeed(level*2+30);
  320. pDoc->balllist2.AddHead(ball2);
  321. }
  322. if((enemy2->getPosition().y)>lpRect.top)
  323. enemy2->move(direction2);
  324. else
  325. {
  326. if(pDoc->enemylist2.GetHeadPosition()==position1){
  327. pDoc->enemylist2.RemoveHead();
  328. delete enemy2;
  329. }
  330. else if(pDoc->enemylist2.GetTailPosition()==position1){
  331. pDoc->enemylist2.RemoveTail();
  332. delete enemy2;
  333. }
  334. else if(pDoc->enemylist2.GetAt(position1)!=NULL)
  335. {
  336. pDoc->enemylist2.RemoveAt(position1);
  337. delete enemy2;
  338. }
  339. }
  340. }
  341. //设置boss的子弹
  342. if(level==4&&boss!=NULL){
  343. for(int i=0;i<5;i++){
  344. Cball *ball3 =new Cball(i,0);
  345. ball3->setPosition(boss->getPosition().x+45,boss->getPosition().y+125);
  346. pDoc->balllist3.AddTail(ball3);
  347. }
  348. }
  349. if(level==5&&boss!=NULL){
  350. for(int i=0;i<5;i++){
  351. Cball *ball3 =new Cball(i,0);
  352. ball3->setPosition(boss->getPosition().x+10*i,boss->getPosition().y+125);
  353. pDoc->balllist3.AddTail(ball3);
  354. }
  355. /*for(int i=0;i<=5;i++){
  356. Cball *ball3 = new Cball();
  357. ball3->setPosition(boss->getPosition().x+)
  358. }*/
  359. }
  360. this->Invalidate(TRUE);
  361. break;
  362. }
  363. case 3:
  364. {
  365. //敌机子弹的移动
  366. POSITION pos2,position2;
  367. CPoint point;
  368. Cball *ball1;
  369. Cball *ball2;
  370. int direction1=-1,direction2=1;
  371. for(pos2=pDoc->balllist1.GetHeadPosition(); pos2!=NULL;)
  372. {
  373. position2=pos2;
  374. ball1=(Cball*)pDoc->balllist1.GetNext(pos2);
  375. if((ball1->getPosition().y)<lpRect.bottom){
  376. if(level>=3)
  377. ball1->move(plane.getPosition());
  378. else
  379. ball1->move(direction1);
  380. //if(point!=plane.getPosition())
  381. // ball1->type=1;
  382. }
  383. else
  384. {
  385. if(pDoc->balllist1.GetHeadPosition()==position2){
  386. pDoc->balllist1.RemoveHead();
  387. delete ball1;
  388. }
  389. else if(pDoc->balllist1.GetTailPosition()==position2){
  390. pDoc->balllist1.RemoveTail();
  391. delete ball1;
  392. }
  393. else if(pDoc->balllist1.GetAt(position2)!=NULL)
  394. {
  395. pDoc->balllist1.RemoveAt(position2);
  396. delete ball1;
  397. }
  398. }
  399. }
  400. for(pos2=pDoc->balllist2.GetHeadPosition(); pos2!=NULL;)
  401. {
  402. position2=pos2;
  403. ball2=(Cball*)pDoc->balllist2.GetNext(pos2);
  404. if((ball2->getPosition().y)>lpRect.top){
  405. if(level>=3)
  406. ball2->move(plane.getPosition());
  407. else
  408. ball2->move(direction2);
  409. //ball2->move(direction2);
  410. //if(point!=plane.getPosition())
  411. // ball2->type=1;
  412. }
  413. else
  414. {
  415. if(pDoc->balllist2.GetHeadPosition()==position2){
  416. pDoc->balllist2.RemoveHead();
  417. delete ball2;
  418. }
  419. else if(pDoc->balllist2.GetTailPosition()==position2){
  420. pDoc->balllist2.RemoveTail();
  421. delete ball2;
  422. }
  423. else if(pDoc->balllist2.GetAt(position2)!=NULL)
  424. {
  425. pDoc->balllist2.RemoveAt(position2);
  426. delete ball2;
  427. }
  428. }
  429. }
  430. this->Invalidate(TRUE);
  431. break;
  432. }
  433. case 4:{
  434. //boss子弹移动
  435. if(level>=4){
  436. int i=1;
  437. Cball *ball3;
  438. POSITION pos2,position2;
  439. int direction1=-1;
  440. for(pos2=pDoc->balllist3.GetHeadPosition(); pos2!=NULL;)
  441. {
  442. position2=pos2;
  443. ball3=(Cball*)pDoc->balllist2.GetNext(pos2);
  444. ball3->setSpeed(40);
  445. if((ball3->getPosition().y)<lpRect.bottom)
  446. if(level==5)
  447. ball3->move(plane.getPosition());
  448. else
  449. ball3->move();
  450. else
  451. {
  452. if(pDoc->balllist3.GetHeadPosition()==position2){
  453. pDoc->balllist3.RemoveHead();
  454. delete ball3;
  455. }
  456. else if(pDoc->balllist3.GetTailPosition()==position2){
  457. pDoc->balllist3.RemoveTail();
  458. delete ball3;
  459. }
  460. else if(pDoc->balllist3.GetAt(position2)!=NULL)
  461. {
  462. pDoc->balllist3.RemoveAt(position2);
  463. delete ball3;
  464. }
  465. }
  466. }
  467. }
  468. //boss移动
  469. if(level>=4&&boss!=NULL){
  470. boss->move();
  471. }
  472. this->Invalidate(TRUE);
  473. break;
  474. }
  475. }
  476. }
  477. shot();//射击
  478. pass();//通关
  479. CView::OnTimer(nIDEvent);
  480. }
  481. //射击
  482. int CPlaneGameView::shot(void)
  483. {
  484. CPlaneGameDoc* pDoc = GetDocument();
  485. ASSERT_VALID(pDoc);
  486. if (!pDoc)
  487. return 1;
  488. if(isPause == 0){
  489. CRect tempRect;
  490. //战机子弹打中向下走的敌机
  491. POSITION pos,position,pos1,position1;
  492. Cenemy *enemy1,*enemy2;
  493. Cbomb *bomb1;
  494. for(pos=pDoc->enemylist1.GetHeadPosition();pos!=NULL;){
  495. position=pos;
  496. enemy1=(Cenemy*)pDoc->enemylist1.GetNext(pos);
  497. CRect eneRect = enemy1->getRect();
  498. for(pos1=pDoc->bomblist1.GetHeadPosition();pos1!=NULL;){
  499. position1=pos1;
  500. bomb1=(Cbomb*)pDoc->bomblist1.GetNext(pos1);
  501. CRect bomRect = bomb1->getRect();
  502. //判断两个矩形区域是否有交接
  503. if(tempRect.IntersectRect(&bomRect,eneRect))
  504. {
  505. //将爆炸对象添加到爆炸链表中
  506. Cexplosion *explosion=new Cexplosion((enemy1->getPosition().x+17),(enemy1->getPosition().y+17));
  507. pDoc->explosionlist.AddHead(explosion);
  508. PlaySound((LPCTSTR)IDR_WAVE2, AfxGetInstanceHandle(), SND_RESOURCE |SND_ASYNC);
  509. //删除子弹
  510. if(pDoc->bomblist1.GetHeadPosition()==position){
  511. pDoc->bomblist1.RemoveHead();
  512. delete bomb1;
  513. }
  514. else if(pDoc->bomblist1.GetTailPosition()==position){
  515. pDoc->bomblist1.RemoveTail();
  516. delete bomb1;
  517. }
  518. else if(pDoc->bomblist1.GetAt(position1)!=NULL){
  519. pDoc->bomblist1.RemoveAt(position1);
  520. delete bomb1;
  521. }
  522. //删除敌机
  523. if(pDoc->enemylist1.GetHeadPosition()==position1){
  524. pDoc->enemylist1.RemoveHead();
  525. delete enemy1;
  526. }
  527. else if(pDoc->enemylist1.GetTailPosition()==position1){
  528. pDoc->enemylist1.RemoveTail();
  529. delete enemy1;
  530. }
  531. else if(pDoc->enemylist1.GetAt(position)!=NULL){
  532. pDoc->enemylist1.RemoveAt(position);
  533. delete enemy1;
  534. }
  535. score++;
  536. lifenum++;
  537. break;
  538. }
  539. }
  540. }
  541. //战机子弹打中向上走的敌机
  542. for(pos=pDoc->enemylist2.GetHeadPosition();pos!=NULL;){
  543. position=pos;
  544. enemy2=(Cenemy*)pDoc->enemylist2.GetNext(pos);
  545. CRect eneRect = enemy2->getRect();
  546. for(pos1=pDoc->bomblist1.GetHeadPosition();pos1!=NULL;){
  547. position1=pos1;
  548. bomb1=(Cbomb*)pDoc->bomblist1.GetNext(pos1);
  549. CRect bomRect = bomb1->getRect();
  550. //判断两个矩形区域是否有交接
  551. if(tempRect.IntersectRect(&bomRect,eneRect))
  552. {
  553. //将爆炸对象添加到爆炸链表中
  554. Cexplosion *explosion=new Cexplosion((enemy2->getPosition().x+17),(enemy2->getPosition().y+17));
  555. pDoc->explosionlist.AddHead(explosion);
  556. PlaySound((LPCTSTR)IDR_WAVE2, AfxGetInstanceHandle(), SND_RESOURCE |SND_ASYNC);
  557. //删除子弹
  558. if(pDoc->bomblist1.GetHeadPosition()==position){
  559. pDoc->bomblist1.RemoveHead();
  560. delete bomb1;
  561. }
  562. else if(pDoc->bomblist1.GetTailPosition()==position){
  563. pDoc->bomblist1.RemoveTail();
  564. delete bomb1;
  565. }
  566. else if(pDoc->bomblist1.GetAt(position1)!=NULL){
  567. pDoc->bomblist1.RemoveAt(position1);
  568. delete bomb1;
  569. }
  570. //删除敌机
  571. if(pDoc->enemylist2.GetHeadPosition()==position1){
  572. pDoc->enemylist2.RemoveHead();
  573. delete enemy2;
  574. }
  575. else if(pDoc->enemylist2.GetTailPosition()==position1){
  576. pDoc->enemylist2.RemoveTail();
  577. delete enemy2;
  578. }
  579. else if(pDoc->enemylist2.GetAt(position)!=NULL){
  580. pDoc->enemylist2.RemoveAt(position);
  581. delete enemy2;
  582. }
  583. score++;
  584. lifenum++;
  585. break;
  586. }
  587. }
  588. }
  589. Cball *balllist1,*balllist2,*ball3;
  590. //敌机向下的子弹打中战机
  591. for(pos=pDoc->balllist1.GetHeadPosition();pos!=NULL;){
  592. position=pos;
  593. balllist1=(Cball*)pDoc->balllist1.GetNext(pos);
  594. CRect ballRect = balllist1->getRect();
  595. CRect planeRect = plane.getRect();
  596. //判断两个矩形区域是否有交接
  597. if(tempRect.IntersectRect(&ballRect,planeRect))
  598. {
  599. if(position==pDoc->balllist1.GetHeadPosition()){
  600. pDoc->balllist1.RemoveHead();
  601. delete balllist1;
  602. }
  603. else if(position==pDoc->balllist1.GetTailPosition()){
  604. pDoc->balllist1.RemoveTail();
  605. delete balllist1;
  606. }
  607. else if(pDoc->balllist1.GetAt(position)!=NULL)
  608. {
  609. pDoc->balllist1.RemoveAt(position);
  610. delete balllist1;
  611. }
  612. lifenum--;
  613. break;
  614. }
  615. }
  616. //敌机向上的子弹打中战机
  617. for(pos=pDoc->balllist2.GetHeadPosition();pos!=NULL;){
  618. position=pos;
  619. balllist2=(Cball*)pDoc->balllist2.GetNext(pos);
  620. CRect ballRect = balllist2->getRect();
  621. CRect planeRect = plane.getRect();
  622. //判断两个矩形区域是否有交接
  623. if(tempRect.IntersectRect(&ballRect,planeRect))
  624. {
  625. if(position==pDoc->balllist2.GetHeadPosition()){
  626. pDoc->balllist2.RemoveHead();
  627. delete balllist2;
  628. }
  629. else if(position==pDoc->balllist2.GetTailPosition()){
  630. pDoc->balllist2.RemoveTail();
  631. delete balllist2;
  632. }
  633. else if(pDoc->balllist2.GetAt(position)!=NULL){
  634. pDoc->balllist2.RemoveAt(position);
  635. delete balllist2;
  636. }
  637. lifenum--;
  638. break;
  639. }
  640. }
  641. //战机与向下走的敌机相撞
  642. for(pos=pDoc->enemylist1.GetHeadPosition();pos!=NULL;){
  643. position=pos;
  644. enemy1=(Cenemy*)pDoc->enemylist1.GetNext(pos);
  645. CRect eneRect = enemy1->getRect();
  646. CRect planeRect = plane.getRect();
  647. //判断两个矩形区域是否有交接
  648. if(tempRect.IntersectRect(&eneRect,planeRect))
  649. {
  650. ////将爆炸对象添加到爆炸链表中
  651. Cexplosion *explosion=new Cexplosion((enemy1->getPosition().x+17),(enemy1->getPosition().y+17));
  652. pDoc->explosionlist.AddHead(explosion);
  653. PlaySound((LPCTSTR)IDR_WAVE2, AfxGetInstanceHandle(), SND_RESOURCE |SND_ASYNC);
  654. if(position==pDoc->enemylist1.GetHeadPosition()){
  655. pDoc->enemylist1.RemoveHead();
  656. delete enemy1;
  657. }
  658. else if(position==pDoc->enemylist1.GetTailPosition()){
  659. pDoc->enemylist1.RemoveTail();
  660. delete enemy1;
  661. }
  662. else if(pDoc->enemylist1.GetAt(position)!=NULL)
  663. {
  664. pDoc->enemylist1.RemoveAt(position);
  665. delete enemy1;
  666. }
  667. lifenum=lifenum-5;
  668. break;
  669. }
  670. }
  671. //战机与向上走的敌机相撞
  672. for(pos=pDoc->enemylist2.GetHeadPosition();pos!=NULL;){
  673. position=pos;
  674. enemy2=(Cenemy*)pDoc->enemylist2.GetNext(pos);
  675. CRect eneRect = enemy2->getRect();
  676. CRect planeRect = plane.getRect();
  677. //判断两个矩形区域是否有交接
  678. if(tempRect.IntersectRect(&eneRect,planeRect))
  679. {
  680. ////将爆炸对象添加到爆炸链表中
  681. Cexplosion *explosion=new Cexplosion((enemy2->getPosition().x+17),(enemy2->getPosition().y+17));
  682. pDoc->explosionlist.AddHead(explosion);
  683. PlaySound((LPCTSTR)IDR_WAVE2, AfxGetInstanceHandle(), SND_RESOURCE |SND_ASYNC);
  684. //删除敌机
  685. if(position==pDoc->enemylist2.GetHeadPosition()){
  686. pDoc->enemylist2.RemoveHead();
  687. delete enemy2;
  688. }
  689. else if(position==pDoc->enemylist2.GetTailPosition()){
  690. pDoc->enemylist2.RemoveTail();
  691. delete enemy2;
  692. }
  693. else if(pDoc->enemylist2.GetAt(position)!=NULL)
  694. {
  695. pDoc->enemylist2.RemoveAt(position);
  696. delete enemy2;
  697. }
  698. lifenum=lifenum-5;
  699. break;
  700. }
  701. }
  702. if(level>=4){
  703. //boss的子弹打中战机
  704. for(pos=pDoc->balllist3.GetHeadPosition();pos!=NULL;){
  705. position=pos;
  706. ball3=(Cball*)pDoc->balllist3.GetNext(pos);
  707. CRect ballRect = ball3->getRect();
  708. CRect planeRect = plane.getRect();
  709. //判断两个矩形区域是否有交接
  710. if(tempRect.IntersectRect(&ballRect,planeRect))
  711. {
  712. //删除boss子弹
  713. if(position==pDoc->balllist3.GetHeadPosition()){
  714. pDoc->balllist3.RemoveHead();
  715. delete ball3;
  716. }
  717. else if(position==pDoc->balllist3.GetTailPosition()){
  718. pDoc->balllist3.RemoveTail();
  719. delete ball3;
  720. }
  721. else if(pDoc->balllist3.GetAt(position)!=NULL){
  722. pDoc->balllist3.RemoveAt(position);
  723. delete ball3;
  724. }
  725. lifenum--;
  726. break;
  727. }
  728. }
  729. //boss与战机相撞
  730. if(boss!=NULL){
  731. CRect bossRect = boss->getRect();
  732. CRect planeRect = plane.getRect();
  733. if(tempRect.IntersectRect(&bossRect,planeRect))
  734. {
  735. lifenum=lifenum-10;
  736. //bosslife=bosslife-5;
  737. }
  738. //战机子弹打中boss
  739. for(pos1=pDoc->bomblist1.GetHeadPosition();pos1!=NULL;){
  740. position1=pos1;
  741. bomb1=(Cbomb*)pDoc->bomblist1.GetNext(pos1);
  742. CRect bomRect = bomb1->getRect();
  743. //判断两个矩形区域是否有交接
  744. if(tempRect.IntersectRect(&bomRect,bossRect))
  745. {
  746. //删除子弹
  747. if(pDoc->bomblist1.GetHeadPosition()==position){
  748. pDoc->bomblist1.RemoveHead();
  749. delete bomb1;
  750. }
  751. else if(pDoc->bomblist1.GetTailPosition()==position){
  752. pDoc->bomblist1.RemoveTail();
  753. delete bomb1;
  754. }
  755. else if(pDoc->bomblist1.GetAt(position1)!=NULL){
  756. pDoc->bomblist1.RemoveAt(position1);
  757. delete bomb1;
  758. }
  759. score++;
  760. lifenum++;
  761. bosslife--;
  762. break;
  763. }
  764. }
  765. }
  766. }
  767. }
  768. if(bosslife<=0&&boss!=NULL){
  769. //将爆炸对象添加到爆炸链表中
  770. Cexplosion *explosion=new Cexplosion((boss->getPosition().x+17),(boss->getPosition().y+17));
  771. pDoc->explosionlist.AddHead(explosion);
  772. PlaySound((LPCTSTR)IDR_WAVE2, AfxGetInstanceHandle(), SND_RESOURCE |SND_ASYNC);
  773. delete boss;
  774. boss=NULL;
  775. bosslife=0;
  776. }
  777. return 0;
  778. }
  779. int CPlaneGameView::pass(void)
  780. {
  781. CPlaneGameDoc* pDoc = GetDocument();
  782. ASSERT_VALID(pDoc);
  783. if (!pDoc)
  784. return 1;
  785. //过关
  786. if(score>=100&&isPause==0){
  787. isPass = 1;
  788. }
  789. if(isPass==1){
  790. if(level>=5){
  791. KillTimer(1);
  792. KillTimer(2);
  793. KillTimer(3);
  794. KillTimer(4);
  795. if(AfxMessageBox(L"恭喜你已通关!是否重新开始?",MB_YESNO)==6)
  796. {
  797. pDoc->enemylist1.RemoveAll();
  798. pDoc->enemylist2.RemoveAll();
  799. pDoc->bomblist1.RemoveAll();
  800. pDoc->balllist1.RemoveAll();
  801. pDoc->balllist2.RemoveAll();
  802. pDoc->balllist3.RemoveAll();
  803. score=0;
  804. lifenum=100;
  805. bosslife=30;
  806. isPause=0;
  807. isPass=0;
  808. level=1;
  809. boss=new Cboss;
  810. SetTimer(1,100,NULL);
  811. SetTimer(2,60,NULL);
  812. SetTimer(3,50,NULL);
  813. SetTimer(4,500,NULL);
  814. }
  815. else
  816. exit(1);
  817. }
  818. else if(level<5)
  819. {
  820. KillTimer(1);
  821. KillTimer(2);
  822. KillTimer(3);
  823. KillTimer(4);
  824. if(AfxMessageBox(L"恭喜你已过关!是否进入下一关?",MB_YESNO)==6)
  825. {
  826. pDoc->enemylist1.RemoveAll();
  827. pDoc->enemylist2.RemoveAll();
  828. pDoc->bomblist1.RemoveAll();
  829. pDoc->balllist1.RemoveAll();
  830. pDoc->balllist2.RemoveAll();
  831. pDoc->balllist3.RemoveAll();
  832. score=0;
  833. level++;
  834. lifenum=100*level;
  835. bosslife=50;
  836. isPause=0;
  837. isPass=0;
  838. boss=new Cboss;
  839. SetTimer(1,100,NULL);
  840. SetTimer(2,60,NULL);
  841. SetTimer(3,50,NULL);
  842. SetTimer(4,500,NULL);
  843. }
  844. else
  845. exit(1);
  846. }
  847. }
  848. if(lifenum<=0&&isPass==0&&isPause==0){
  849. KillTimer(1);
  850. KillTimer(2);
  851. KillTimer(3);
  852. KillTimer(4);
  853. if(AfxMessageBox(L"GAME OVER!是否重新开始?",MB_YESNO)==6)
  854. {
  855. pDoc->enemylist1.RemoveAll();
  856. pDoc->enemylist2.RemoveAll();
  857. pDoc->bomblist1.RemoveAll();
  858. pDoc->balllist1.RemoveAll();
  859. pDoc->balllist2.RemoveAll();
  860. pDoc->balllist3.RemoveAll();
  861. score=0;
  862. lifenum=100;
  863. bosslife=50;
  864. isPause=0;
  865. isPass=0;
  866. level=1;
  867. SetTimer(1,100,NULL);
  868. SetTimer(2,60,NULL);
  869. SetTimer(3,50,NULL);
  870. SetTimer(4,500,NULL);
  871. }
  872. else
  873. exit(1);
  874. }
  875. return 0;
  876. }

6 测试情况说明

6.1 主要模块测试情况(白盒)

6.1.1 战机模块测试

上方向键 战机向上移动 到达边界时停止
下方向键 战机向下移动 到达边界时停止
左方向键 战机向左移动 到达边界时停止
右方向键 战机向右移动 到达边界时停止
空格键 战机双排子弹 到达边界和击中敌机时消失
Ctrl键 战机三列散弹 到达边界和击中敌机时消失
Shift键 战机五列散弹 到达边界和击中敌机时消失
X键 战机追踪弹 到达边界和击中敌机时消失

6.1.2 敌机测试模块

向上飞的敌机 从下边界出发,到达上边界和被击中时消失
向下飞的敌机 从上边界出发,到达下边界和被击中时消失
向上飞的敌机发射向上的子弹 从某一敌机出发,到达上边界或已使用消失
向下飞的敌机发射向下的子弹 从某一敌机出发,到达下边界或已使用消失

6.1.3 BOSS测试模块

随机出现 在游戏界面某一不确定位置出现
向下移动 boss出现后向下移动
到达一定点后不再移动 到达中间位置后不再移动
三列散弹 发射三列散弹攻击战机
追踪弹 追踪战机的位置并向其发射追踪弹

6.2 主要功能测试情况(黑盒)

6.2.1 移动功能测试

战机 战机按方向键可以正常上下左右移动
敌机 敌机从上下两个方向移动
战机子弹 战机子弹沿直线移动
敌机子弹 敌机子弹沿竖直方向移动
boss boss上下移动
boss子弹 boss的子弹沿直线移动

6.2.2 爆炸功能测试

战机与敌机相撞 敌机爆炸并产生声音效果
战机子弹击中敌机 敌机爆炸并产生声音效果
战机打死boss boss爆炸并产生声音效果

6.2.3 得分功能测试

战机击中敌机 得一分
战机与敌机相撞 减生命值五分
战机击中boss 得一分
战机与boss相撞 减生命值十分
敌机击中战机 减生命值一分
Boss击中战机 减生命值一分

7 实训中遇到的主要问题及解决方法

在刚开始设置难度的时候老是出错,后来重新再次添加了事件处理函数之后就解决了这个问题。

在第四关时有一个大boss,在将其打死之后,delete boss时出错,后来发现是在删除之后boss就空了,所以在使用boss对象的成员函数时,先判断一下boss是否为空,问题解决。
在测试中发现当战机的子弹打中敌机之后没有消失,主要原因是子弹的发射速度太快以及定时器设置的不合理,画面上虽然显示可能是两颗子弹,但是实际上并不只有两颗,所以子弹没有消失,经过修改定时器和子弹发射速度解决了这个问题。

8 实训体会

通过这次的C++实训,深刻体会了MFC的类库以及其消息传递机制,使我的编程能力有了很大的提高,同时更重要的是提高了自学能力即包括资料检索、阅读理解等能力。

刚开始的时候不是很了解MFC中的类有哪些,对如何添加类也不太会,后来,向同学询问和自己查资料熟悉了一些常用的MFC类,对于一些函数的使用也有了更加深刻的理解。

通过这次的实训更加加深了我对C++的了解,是我了解到在课堂上学到的C++知识只是小小的一部分,经过这次的实训使我提高了不少,增强了我掌握和利用C++进行程设计的能力, 而且进一步理解和运用结构化程设计的思想和方法。初步掌握了开发系统应用程序的基本方法和步骤。

在实现飞机大战的过程中,确实感觉到自己知识的薄弱,很多知识不会,例如visual studio2008中的很多功能的作用,这次实习使我对编程有了新的认识和定义,其实程序的编程不是枯燥和乏味的,而是包含着轻松和愉悦,每当自己解决了一个刺手的问题后,自己都有一种成功感。

9 游戏演示

游戏初始弹窗

游戏说明

游戏画面1

游戏画面2

游戏结束

上传的附件 cloud_download 基于VC++的MFC类库实现的飞机大战游戏.7z ( 2.82mb, 7次下载 )
error_outline 下载需要9点积分

发送私信

你愿苦其自身,必将掌声雷动

8
文章数
11
评论数
最近文章
eject