基于VC++的MFC框架实现的飞机大战小游戏

Cynicism

发布日期: 2018-10-31 10:44:59 浏览量: 2931
评分:
star star star star star star star star star_border star_border
*转载请注明来自write-bug.com

一、类介绍

1.1 程序使用到的MFC类库中主要的类

  • CDC类

  • CRect类

  • CBitmap类

  • CImageList类

  • mfc框架:app类、wnd类、doc类、view类

1.2 项目包含的对象类

8个游戏类:

  • enemy(敌人)

  • bomb(敌人子弹)

  • missile2(飞机子弹)、missile3(超级子弹)

  • myplane(英雄机)

  • explosion(爆炸)

  • backgroud(背景类)GameObject(游戏对象类(父类))

1.3 主要逻辑程序

  • planefightview.cpp

二、功能介绍

2.1 飞机游戏项目功能简介

飞机大战游戏是基于Windows桌面的射击类游戏,其需要实现的功能为:实现游戏对象的爆炸特效、文字提示功能和界面背景特效,其主要是遵循一定的游戏规则进行游戏。

2.2 游戏规则

游戏中的主要角色可分为如下几个基本部分:战机、敌机、战机的导弹、敌机的子弹。其主要遵循的游戏规则为:战机数量为1,由玩家通过键盘控制(方向键控制位置、空格键发射导弹和shift键发射超级导弹)战机;导弹释放存在间隔,有一定的运行速度;导弹遇到敌机发生爆炸,敌机被炸毁,导弹消失,玩家得分;由计算机控制敌机自动向战机发动攻击;敌机数量可以根据难度大小随机生成,计算机生成敌机时随机选择类别;敌机从游戏区域的上端进入,左右位置随机;普通敌机被导弹攻击即死,敌机行驶期间,不左右移动,不反向移动;运行线路为直线,方向为从上至下,不可左右移动。纵向由发射位置起至游戏区域结束;敌机子弹遇到战机时发生爆炸,战机被炸毁,子弹消失,游戏结束。

游戏描述:游戏关卡10关,生命值50,游戏积分值每击杀1敌人加一分,积分累加到20升一关卡,敌机数量会随着游戏关卡提升而增加。

三、相关技术

此次实训的飞机大战游戏其中的技术主要就是一些函数、双向链表的使用、内存释放和双缓冲技术。

3.1 建立基类CGameObject

建立基于所有类的父类CGameObject,其中定义了两个纯虚函数作为继承CGameObject类的其他类的某些声明,例如:

  1. CGameObject(int x=0,int y=0);
  2. virtual ~CGameObject()
  3. virtual BOOL Draw(CDC* pDC,BOOL bPause)=0;//绘制对象
  4. virtual CRect GetRect()=0;//获得矩形区域
  5. CPoint GetPoint()//获得左上角坐标
  6. {
  7. return m_ptPos;
  8. }
  9. static BOOL LoadImage(CImageList& imgList,UINT bmpID,COLORREF crMask,int cx,int cy,int nInitial);
  10. CPoint m_ptPos;//物体的位置

3.2 透明图片贴图

此次实训中用于贴图的函数:

  1. //此函数主要用于将图片放入图像链表imageList中
  2. static BOOL LoadImage(CImageList& imgList,UINT bmpID,COLORREF crMask,int cx,int cy,int nInitial);
  3. //此函数主要是讲ImageList链表中的图像显示在pDC这个句柄中
  4. ImageList.Draw(pDC,0,m_ptPos,ILD_TRANSPARENT);

ILD_TRANSPARENT表示是透明贴图。其中主要就是CImageList图像列表。它是相同大小图像的一个集合,每个集合中均以0为图像的索引序号基数,图像列表通常由大图标或位图构成,其中包含透明位图模式。可以利用WINDOWS32位应用程序接口函数API来绘制、建立和删除图像,并能实现增加、删除、替换和拖动图像等操作。

3.3 CObList链表

  1. //主要是定义一个CObList类的一个链表对象用于存储所有与此种相关的对象,易于后续的添加删除和提取数据
  2. CObList elist;
  3. CObList plist;
  4. CObList bomblist;
  5. CObList miss2list;
  6. CObList miss3list;;
  7. POSITION pos2,pos4,pos5,pos3;//定义指针
  8. POSITION ePos=pDoc->miss2list.GetHeadPosition()//指针读取头结点
  9. missile2 *el2=(missile2 *)pDoc->miss2list.GetNext(ePos);//指针读取下一个节点

3.4 获得矩形区域函数

  1. CRect c;
  2. c.IntersectRect(el1->GetRect(),el2->GetRect())//判断矩形是否有交接
  3. //例子为获得敌机的矩形区域
  4. CRect GetRect()
  5. {
  6. return CRect(m_ptPos,CPoint(m_ptPos.x+35,m_ptPos.y+35));
  7. }

3.5 发射子弹或导弹函数

  1. missile2 *bb2;
  2. if(GetKeyState(VK_SPACE)<0) // 空格发射导弹
  3. {
  4. bb2=new missile2(pDoc->p1.m_ptPos.x+20,pDoc->p1.m_ptPos.y-15); // 导弹
  5. pDoc->miss2list.AddTail(bb2);
  6. }

3.6 添加爆炸效果技术

  1. explosion e1;
  2. e1.Draw(pMemDC);
  3. BOOL Draw(CDC *pDC, BOOL bPause)
  4. {
  5. ImageList.Draw(pDC,0,m_ptPos,ILD_TRANSPARENT);
  6. return TRUE;
  7. }

3.7 对于对象的绘制以及越出边界的处理

  1. //绘制 导弹、爆炸、敌机、
  2. for(POSITION ePos=pDoc->miss2list.GetHeadPosition();(pos3=ePos)!=NULL;)
  3. {
  4. // 获取用于遍历的下一个元素
  5. missile2 *el2=(missile2 *)pDoc->miss2list.GetNext(ePos);
  6. el2->Draw(pMemDC);
  7. //发导弹,若导弹飞出屏幕则删除
  8. if(!c.IntersectRect(el2->GetRect(),ck))
  9. {
  10. pa2= pDoc->miss2list.GetAt(pos3);
  11. pDoc->miss2list.RemoveAt(pos3);
  12. delete pa2;
  13. }
  14. else
  15. {
  16. for(POSITION ePos=pDoc->plist.GetHeadPosition();(pos5=ePos)!=NULL;)
  17. {
  18. enemy *el1=(enemy *)pDoc->plist.GetNext(ePos);
  19. // 导弹打中敌机
  20. if (c.IntersectRect(el1->GetRect(),el2->GetRect()))
  21. {
  22. e1.m_ptPos.x=el2->m_ptPos.x;
  23. e1.m_ptPos.y=el2->m_ptPos.y;
  24. e1.Draw(pMemDC); // 敌机爆炸,显示效果
  25. pa = pDoc->plist.GetAt(pos5);
  26. pDoc->plist.RemoveAt(pos5);
  27. delete pa; //删除敌机
  28. pa2= pDoc->miss2list.GetAt(pos3);
  29. pDoc->miss2list.RemoveAt(pos3);
  30. delete pa2; // 删除导弹
  31. pDoc->p1.grade+=1;
  32. pDoc->p1.level = pDoc->p1.grade/20;
  33. hard = 8 - pDoc->p1.level;
  34. if(hard < 0){
  35. hard = 0;
  36. }
  37. break;
  38. }
  39. }
  40. }
  41. }

3.8 字体的个性化输出函数

  1. HFONT font;
  2. // 创建一种有特殊性的逻辑字体,此逻辑字体可以在后面被任何设备选择
  3. font=CreateFont(20,10,0,0,0,0,0,0,0,0,0,100,10,0);
  4. // 设置指定DC的背景混合模式,为透明的
  5. pMemDC->SetBkMode(TRANSPARENT);
  6. pMemDC->SelectObject(font);
  7. pMemDC->SetTextColor(RGB(255,0,0));
  8. // 弹出窗口
  9. pMemDC->TextOutW(470,200,L"GAME OVER",9);
  10. Grade.Format(L"分数:%d",pDoc->p1.grade);
  11. pMemDC->TextOutW(490,250,Grade);

3.9 获取键盘操作函数

GetKeyState()返回一个short型的数,short型是16位有符号的数据类型,如果要查询的键被按下,返回值最高位被置1,则这个数表示负数,所以可以用\<0或\>0来判断。\<0表示被按下,\>0表示没按下。

3.10 用键盘控制战机的移动

  1. //检测四个方向键,移动战机
  2. if(GetKeyState(VK_LEFT)<0)
  3. {
  4. if(pDoc->p1.m_ptPos.x - 15 <= 0){
  5. pDoc->p1.m_ptPos.x = 0;
  6. }else{
  7. pDoc->p1.m_ptPos.x-=15;
  8. }
  9. }
  10. if(GetKeyState(VK_RIGHT)<0)
  11. {
  12. if(pDoc->p1.m_ptPos.x + 15 >= ck.right - 50){
  13. pDoc->p1.m_ptPos.x = ck.right - 50;
  14. }else{
  15. pDoc->p1.m_ptPos.x+=15;
  16. }
  17. }
  18. if(GetKeyState(VK_UP)<0)
  19. {
  20. if(pDoc->p1.m_ptPos.y - 15 <= 0){
  21. pDoc->p1.m_ptPos.y = 0;
  22. }else{
  23. pDoc->p1.m_ptPos.y-=15;
  24. }
  25. }
  26. if(GetKeyState(VK_DOWN)<0)
  27. {
  28. if(pDoc->p1.m_ptPos.y + 15 >= ck.bottom - 60){
  29. pDoc->p1.m_ptPos.y = ck.bottom - 60;
  30. }else{
  31. pDoc->p1.m_ptPos.y+=15;
  32. }
  33. }

3.11 设置定时器

定时器告诉WINDOWS一个时间间隔,然后WINDOWS以此时间间隔周期性触发程序。通常有两种方法来实现:发送WM_TIMER消息和调用应用程序定义的回调函数。

  1. this->SetTimer(1,40,NULL);//刷屏
  2. this->SetTimer(2, 200,NULL);//敌机enemy
  3. this->SetTimer(3,1500,NULL);//子弹bomb
  4. this->SetTimer(4,2,NULL);//刷新导弹

通过定时器可以控制游戏对象的自行移动以及某些定时控制的实现。

3.12 双缓冲技术

关于双缓冲技术主要就是利用缓存的原理进行将所有的东西都先存在一个缓冲得虚拟的区域,然后再一次性的将所有的虚拟缓存中的东西都放入实在的存储器中。

  1. pDC=new CClientDC(this);
  2. pMemDC=new CDC;
  3. //创建内存设备上下文,与另一个设备上下文匹配。可以用它在内存中准备图像
  4. pMemDC->CreateCompatibleDC(pDC);
  5. pMemBitmap=new CBitmap;
  6. CRect ck;
  7. this->GetClientRect(&ck);
  8. //用一个位图初始化对象使之与指定设备兼容
  9. pMemBitmap->CreateCompatibleBitmap(pDC,ck.Width(),ck.Height());
  10. pMemDC->SelectObject(pMemBitmap);
  11. pDC->BitBlt(0,0,ck.Width(),ck.Height(),pMemDC,0,0,SRCCOPY);
  12. delete pDC;
  13. delete pMemDC;
  14. delete pMemBitmap;

3.13 内存释放技术

在此次实训项目中有太多的对象的生成和运用,造成了内存的极度的紧张,当游戏进行到一定的程度的时候就会出现内存溢出现象。解决此问题的技术就是内存释放。内存释放技术的实现主要是通过释放各个满足一定条件的对象和链表来实现的。

  1. //删除敌机
  2. pDoc->plist.RemoveAt(pos4);
  3. delete pa4;

四、总结

4.1 功能设计总结

本次实训由于时间问题还有些扩展功能可进行增加,主要有:

  • 给游戏增加敌机类型,丰富画面,增加难度

  • 给游戏进行增加boss,通过boss的添加进行游戏的关卡和难度的更加进一步的提升。主要思路是:再增加一个类让它继承敌机类,然后再敌机类的基础上进行增加其生命值和被打死后的得分。当战机得分到达一定的值以后boss出现并进行和敌机一个队伍发射另类的子弹进行攻击战机

  • 完善暂停/重新开始/继续游戏等功能

  • 设置关卡,关卡越往后,难度越大,增加游戏的挑战性

增添多种游戏道具产生各种效果从而增加游戏的趣味性。主要思路:可以设计增加新的道具类并让其继承敌机类在满足一定条件时随机从游戏上方落下然后检测道具与战机的碰撞,然后实现道具功能即可。

4.2 结构设计体会

我们认为可以将敌机类、导弹类、道具类等继承CGameObject类的所有类设计成大体规格相同的形式,然后对于View中的函数等模块加以注释区分,将类结构明显的突出出来以便于扩充新功能,其次若要改为通用对战类游戏,只需改变贴图,某些函数的参数使移动轨迹改变,然后对游戏框架进行合理的,灵活的运用。

4.3 困难和解决的办法

此次实训中遇到的问题一共有五个:

  • C++的面向对象的思想和逻辑思路的还是没有掌握好,后来主要是通过向老师和同学进一步理解了些面向对象的思想和思路以及其好处

  • 一些关于C++的语法问题,例如静态成员和静态变量的声明问题,最后还是在同学那里理解到其不仅需要在头文件中声明,其在源文件中也是需要声明的,应为其在头文件中进行声明在源文件中不进行声明则表示其根本就没有被分配内存空间,致使出现错误

  • 还有在运行过程中经常发生中断,后来经过多次调试发现是链表的声明问题,有时候想当然的随便使用链表,结果发现还没有声明,解决后才发现在此次飞机大战中用了多个链表,觉得对于链表的认识有了加强

  • 对于在碰撞检测中避免游戏对象多次删除的问题,虽然我们没有遇到的,但是我们觉得还是代码规范的问题,只要在使用完游戏对象后及时删除并对其进行检查应该就可以避免了

  • 战机会移出界面,原因是没有设置边界,需要在绘制战机时判断战机位置,当位置大于窗体边界时要进行判断,为战机位置赋值为窗体边界

4.4 项目体会

对于c++的不熟悉导致对程序制作和运行的陌生,很多东西都是现学现用,还有很多问的同学才勉强解决。这次实训让我们认识到,知识在于积累,平时不努力,临时是要付出代价的,以后应认真学习知识,不能临时抱佛脚。

五、演示

游戏界面1

游戏界面2

游戏结束

上传的附件 cloud_download 飞机大战游戏源码和开发报告.7z ( 49.25mb, 227次下载 )

keyboard_arrow_left上一篇 : 基于QT和websocket协议的多线程文件传输 基于JSP和SQL Server 2012实现的影视创作论坛系统 : 下一篇keyboard_arrow_right



Cynicism
2018-10-31 10:46:00
使用MFC实现的飞机大战,其中绘图部分使用了双缓冲绘图技术
鱼柒
2020-11-19 16:09:27
你好,有没有压缩包呀
hang127
2020-11-19 16:14:39
好的好的,不错的资源
咸鱼
2021-01-05 13:12:01
炫酷
nzsd
2021-06-28 20:48:17
有没有BOSS

发送私信

白色衣服的背影

5
文章数
4
评论数
eject