飞机大战实训报告

飞机大战实训报告
飞机大战实训报告

实训报告

飞机大战游戏设计与开发

姓名

专业名称:信息与计算科学

班级:信息一班

学号:201101051504

信息科学与工程学院

二零一三年十二月

目录

1. 概述 (3)

2. 相关技术 (3)

2.1关于碰撞检测的相关函数 (3)

2.2 CObList链表 (3)

2.3 设置定时器、销毁定时器 (3)

3. 总体设计与详细设计 (3)

3.1 系统模块划分 (3)

3.2 主要功能模块 (4)

3.2.1规则子系统 (4)

3.2.2 游戏对象子系统 (5)

4. 编码实现 (6)

5. 实训中遇到的主要问题及解决方法 (16)

6. 实训体会 (16)

1. 概述

飞机大战游戏是在Microsoft Visual Studio编程软件的MFC环境下制作的一款桌面游戏,界面简洁流畅、游戏方式简单。

游戏主要实现的功能和规则如下:

(1)利用键盘中的上下左右键控制我方战机,空格键发射子弹。战机初始有2条生命,每条生命100血。

(2)屏幕上方随机产生敌机,战机产生的数量和当前玩家的等级有关,等级越高,产生的敌机越多,游戏难度越大。而且敌机产生的位置和速度是不确定的。只有当我机位于敌机下方时,敌机才会产生子弹,默认情况下是不发射任何子弹的。

(3)战机子弹打中敌机,敌机就会爆炸,同时玩家分数会增加10分,每得1000分过一关,共有10关。

2. 相关技术

在设计该游戏时,用到许多技术,这里介绍几个相对重要的技术。

2.1关于碰撞检测的相关函数

用CRect类的GetRect()函数可以获得当前对象的矩形区域,IntersectRect()并用此函数判断战机与敌机,子弹与战机,我方导弹与敌机,战机与物品是否相撞。

2.2CObList链表

在本程序中使用了CObList链表来分别存储敌机,炸弹,导弹,爆炸效果。CobList类似于一个双向链表,POSITION类型的变量为链表的键。使用POSITION变量,既可以作为链表循环中的循环变量,也可以作为标记某个位置的标签。我们可以通过获得某元素的POSITION来访问它。

本程序中主要用到的函数有:

GetHeadPosition()——获取链表首元素的POSITION;

AddTail()——向链表尾部添加新的元素;

GetNext( POSITION& rPosition )——返回当前rPosition指向的元素,并使rPosition指向下一个元素。

2.3 设置定时器、销毁定时器

SetTimer()函数来设置定时器,控制每隔多少毫秒执行一次什么任务。用KillTime()函数来销毁定时器。

3. 总体设计与详细设计

3.1 系统模块划分

有两个主要的部分组成,分别是规则子系统、游戏对象子系统。

系统的总体结构图如下:

3.2 主要功能模块

3.2.1规则子系统

该子系统主要是实现飞机大战各项游戏规则。实现了需求中的游戏规则。

组成结构如下图所示:

这个系统包含1个重要的模块,人工智能,它实现了敌机对战机的攻击、以及游戏对象碰撞监测规则。

?攻击规则

?敌机在战机上方时发射炸弹,炸弹从上至下射向战机

?碰撞规则

?战机所在矩形区域与奖品所在矩形区域相交时,表示战机获得该奖励。

?导弹或炸弹本身的矩形区域和敌机或战机的矩形区域相交时,表示导弹或炸弹

射中了敌机或战机。

?敌机击中战机,战机掉10血。

?战机击中敌机,敌机被炸毁,战机加10分。

?战机被炸毁,游戏结束。

3.2.2 游戏对象子系统

该子系统包含了各个游戏对象的实现。本子系统的组织结构图如下:

?应用程序对象

●游戏程序的加载

●游戏对象的绘制

●游戏规则的调用

●玩家的键盘事件获取

?游戏对象

●敌机绘图

●战机绘图

●子弹绘图

模块总汇表如下:

类体系如下:

4. 编码实现

由于编写了多个类来实现该游戏功能,所以程序较多,这里只展示核心部分。程序如下:

#include"stdafx.h"

#include"PlaneGame.h"

#include"PlaneGameDoc.h"

#include"PlaneGameView.h"

#include"MyPlane.h"

#include"Enemy.h"

#include"Bomb.h"

#include"Ball.h"

#include"Explosion.h"

#include

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

int imagelocation = 600;//背景图片截取位置初始位置

// CPlaneGameView

IMPLEMENT_DYNCREATE(CPlaneGameView, CView)

BEGIN_MESSAGE_MAP(CPlaneGameView, CView)

// 标准打印命令

ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)

ON_WM_TIMER()

END_MESSAGE_MAP()

// CPlaneGameView 构造/析构

CPlaneGameView::CPlaneGameView():m_pMe(NULL)

{

// TODO: 在此处添加构造代码

}

CPlaneGameView::~CPlaneGameView()

{

}

BOOL CPlaneGameView::PreCreateWindow(CREATESTRUCT& cs)

{

// TODO: 在此处通过修改

// CREATESTRUCT cs 来修改窗口类或样式

return CView::PreCreateWindow(cs);

}

// CPlaneGameView 绘制

void CPlaneGameView::OnDraw(CDC* pDC)

{

CPlaneGameDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

if (!pDoc)

return;

// TODO: 在此处为本机数据添加绘制代码

}

// CPlaneGameView 打印

BOOL CPlaneGameView::OnPreparePrinting(CPrintInfo* pInfo)

{

// 默认准备

return DoPreparePrinting(pInfo);

}

void CPlaneGameView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) {

// TODO: 添加额外的打印前进行的初始化过程

}

void CPlaneGameView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) {

// TODO: 添加打印后进行的清理过程

}

// CPlaneGameView 诊断

#ifdef _DEBUG

void CPlaneGameView::AssertValid() const

{

CView::AssertValid();

}

void CPlaneGameView::Dump(CDumpContext& dc) const

{

CView::Dump(dc);

}

CPlaneGameDoc* CPlaneGameView::GetDocument() const// 非调试版本是内联的{

ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CPlaneGameDoc)));

return (CPlaneGameDoc*)m_pDocument;

}

#endif//_DEBUG

// CPlaneGameView 消息处理程序

void CPlaneGameView::OnInitialUpdate()

{

CView::OnInitialUpdate();

// TODO: 在此添加专用代码和/或调用基类

//初始化游戏

//以下所有程序都是为了双缓存机制而增加

m_pDC=new CClientDC(this);

m_pMemDC=new CDC;

m_pMemDC->CreateCompatibleDC(m_pDC);

m_pMemBitmap=new CBitmap;//建立内存位图

CRect ck;

this->GetClientRect(&ck);

m_pMemBitmap->CreateCompatibleBitmap(m_pDC,ck.Width(),ck.Height());

m_pMemDC->SelectObject(m_pMemBitmap);//将位图选入内存DC

InitGame(); //初始化游戏

}

BOOL CPlaneGameView::InitGame()

{

CRect rc;

GetClientRect(rc);

//产生随机数种子

srand( (unsigned)time( NULL ) );

//建立设备DC

m_pDC = new CClientDC(this);

//建立内存DC

m_pMemDC = new CDC;

m_pMemDC->CreateCompatibleDC(m_pDC);

//建立内存位图

m_pMemBitmap = new CBitmap;

m_pMemBitmap->CreateCompatibleBitmap(m_pDC,GAME_WIDTH,GAME_HEIGHT);

//将位图选入内存DC

m_pMemDC->SelectObject(m_pMemBitmap);

CMyPlane::LoadImage();

CEnemy::LoadImage();

CBomb::LoadImage();

CBall::LoadImage();

CExplosion::LoadImage();

//产生主我方战机

m_pMe = new CMyPlane;

//启动游戏

SetTimer(1,30,NULL);//在MFC程序中SetTimer被封装在CWnd类中,调用就不用指定窗口句柄了

return TRUE;

}

//绘制背景

void CPlaneGameView::UpdateFrame(CDC* pMemDC)

{

wchar_t lifeleft[20];

wchar_t Score[20];

wchar_t Level[20];

CBitmap bmp;

bmp.LoadBitmapW(IDB_Background); //创建位图

m_pMemDC->SelectObject(&bmp);

pMemDC->BitBlt(0,0,800,600,m_pMemDC,0,imagelocation,SRCCOPY);//设置动态背景

if(imagelocation < 0)

imagelocation = 600;

imagelocation-= 2;

DeleteObject(&bmp);

//绘制状态栏

if(m_pMe!=NULL)

{

pMemDC->SetBkMode(TRANSPARENT);//文字背景透明

wsprintf(lifeleft, L"生命值:%d", m_pMe->HP );

pMemDC->SetTextColor( RGB(255, 255,0) ); //字体颜色

pMemDC->TextOutW(10,10, lifeleft, _tcslen(lifeleft));//字体位置 wsprintf(lifeleft, L"剩余命数:%d", m_pMe->life );

pMemDC->SetTextColor( RGB(255, 255,0) );

pMemDC->TextOutW(10,35, lifeleft, _tcslen(lifeleft));

wsprintf(Score, L"得分:%d", m_pMe->score );

pMemDC->SetTextColor( RGB(255, 0, 0) );

pMemDC->TextOutW(10,60, Score, _tcslen(Score));

wsprintf(Level, L" 第%d 关", m_pMe->level );

pMemDC->SetTextColor( RGB(255, 255, 255) );

pMemDC->TextOutW(10,85, Level, _tcslen(Level));

}

//绘制我方战机

if(m_pMe!=NULL)

{

m_pMe->Draw(m_pMemDC,FALSE);

}

else

{ //游戏结束提醒

CString str=_T("Game Over!");

pMemDC->SetBkMode(TRANSPARENT);

pMemDC->SetTextAlign(TA_CENTER);

pMemDC->SetTextColor(RGB(255,0,0));

pMemDC->TextOut(GAME_WIDTH/2,GAME_HEIGHT/2,str);

}

if(m_pMe!=NULL && m_pMe->score >= 10000)

{ //游戏胜利提醒

CString str=_T("You Win!");

pMemDC->SetBkMode(TRANSPARENT);

pMemDC->SetTextAlign(TA_CENTER);

pMemDC->SetTextColor(RGB(255,0,0));

pMemDC->TextOut(GAME_WIDTH/2,GAME_HEIGHT/2,str);

}

//绘制飞机炮弹、敌机、奖励、

for(int i=0;i<11;i++)

{

POSITION pos1,pos2;

for( pos1 = m_ObjList[i].GetHeadPosition(); ( pos2 = pos1 ) != NULL; )

{

CGameObject* pObj = (CGameObject*)m_ObjList[i].GetNext( pos1 );

if(!pObj->Draw(pMemDC,FALSE))

{

m_ObjList[i].RemoveAt(pos2);

delete pObj;

}

}

}

//复制内存DC到设备DC

m_pDC->BitBlt(0,0,GAME_WIDTH,GAME_HEIGHT,m_pMemDC,0,0,SRCCOPY);

}

void CPlaneGameView::shengcheng()

{

//随机产生敌机

if(m_pMe!=NULL && m_pMe->score < 10000 ) //控制生成条件

{

static int nCreator = rand() %5;

if(nCreator<=m_pMe->level)

{

nCreator = rand()%5+10;

m_ObjList[enEnemy].AddTail(new CEnemy);

}

nCreator--;

}

if(m_pMe==NULL || m_pMe->score >= 10000) //通关或者没有生命剩余,不再产生敌机return;

//检测四个方向键,移动战机

for(int i=0;i<4;i++)

{

int nMeMotion=0;

m_pMe->SetVerMotion(0);

m_pMe->SetHorMotion(0);

nMeMotion = GetKey(VK_UP);

if(nMeMotion==1)

m_pMe->SetVerMotion(1);

nMeMotion = GetKey(VK_DOWN);

if(nMeMotion==1)

m_pMe->SetVerMotion(-1);

nMeMotion = GetKey(VK_RIGHT);

if(nMeMotion==1)

m_pMe->SetHorMotion(1);

nMeMotion = GetKey(VK_LEFT);

if(nMeMotion==1)

m_pMe->SetHorMotion(-1);

}

//产生战机导弹

if(GetKey(VK_SPACE)==1)//按下了空格键

{

CPoint pt = m_pMe->GetPoint();//获取我方战机方位

}

}

//敌机发射子弹

CPoint PlanePt = m_pMe->GetPoint(); //获取我方战机方位

for(POSITION ePos=m_ObjList[enEnemy].GetHeadPosition();ePos!=NULL;)

{

CEnemy* pEnemy = (CEnemy*)m_ObjList[enEnemy].GetNext(ePos);

if(!pEnemy->Fired()) //如果敌机没处于发射状态,继续循环

continue;

CPoint ePt = pEnemy->GetPoint(); //获取敌方战机方位

BOOL fa=FALSE; //标记是否发射

//敌机在战机前面

if(pEnemy->GetMontion()==1 && ePt.y

fa= TRUE;

//敌机在战机后面

//if(pEnemy->GetMontion()==-1 && ePt.y>PlanePt.y)

// fa= TRUE;

if(fa && ePt.x >= PlanePt.x && ePt.x

m_ObjList[enBall].AddTail(new CBall(ePt.x+10,ePt.y+10,pEnemy->GetMontion()));

}

}

//敌机子弹炸掉战机

POSITION bPos1=NULL,bPos2=NULL;

CRect mRect = m_pMe->GetRect();

for(bPos1=m_ObjList[enBall].GetHeadPosition();( bPos2 = bPos1 ) != NULL;)

{

CBall* pBall = (CBall*)m_ObjList[enBall].GetNext(bPos1);

CRect bRect = pBall->GetRect();

CRect tmpRect;

//IntersectRect(eRect,mRect)) 如果交不为空,则返回非零值;否则,如果交为空则返回if(tmpRect.IntersectRect(&bRect,mRect))

{

//删除子弹

m_ObjList[enBall].RemoveAt(bPos2);

delete pBall;

//添加爆炸效果

m_ObjList[enExplosion].AddTail(new CExplosion(mRect.left,mRect.top));

m_pMe->HP -= 10;

if(m_pMe->HP <= 0)

{

m_pMe->life--;

m_pMe->HP = 100;

}

if(m_pMe->life <= 0)

{

//删除战机

delete m_pMe;

m_pMe=NULL;

break;

}

}

}

//撞机

POSITION ePos1=NULL,ePos2=NULL;//用于记录敌我两级位置

for(ePos1=m_ObjList[enEnemy].GetHeadPosition();(ePos2=ePos1)!=NULL;)

{

CEnemy* pEnemy = (CEnemy*)m_ObjList[enEnemy].GetNext(ePos1);

CRect eRect = pEnemy->GetRect();

CRect tmpRect;

if(tmpRect.IntersectRect(eRect,mRect)) //如果交不为空,则返回非零值;否则,如果交为空则返回

{

//删除敌机

m_ObjList[enEnemy].RemoveAt(ePos2);//从索引中删除

delete pEnemy;

//添加爆炸效果

m_ObjList[enExplosion].AddTail(new CExplosion(mRect.left,mRect.top));

m_pMe->HP -= 20;

if(m_pMe->HP <= 0)

{

m_pMe->life--;

m_pMe->HP = 100;

}

if(m_pMe->life <= 0)

{

//删除战机

delete m_pMe;

m_pMe=NULL;

break;

}

//还原子弹

m_pMe->Bkind = 1;

}

}

//添加爆炸效果

m_ObjList[enExplosion].AddTail(

new CExplosion(mRect.left,mRect.top)

);

//删除敌机

m_ObjList[enEnemy].RemoveAt(ePos2);

delete pEnemy;

}

}

}

POSITION baPos1=NULL,baPos2=NULL;

for(baPos1=m_ObjList[enBallA].GetHeadPosition();(baPos2=baPos1)!=NULL;)

{

CBallA* pBallA = (CBallA*)m_ObjList[enBallA].GetNext(baPos1);

CRect prRect = pBallA->GetRect();

CRect tmpRect;

}

//战机导弹炸掉敌机

POSITION mPos1=NULL,mPos2=NULL;

for(mPos1=m_ObjList[enBomb].GetHeadPosition();(mPos2=mPos1)!=NULL;)

{

CBomb* pBomb = (CBomb*)m_ObjList[enBomb].GetNext(mPos1);

CRect bRect = pBomb->GetRect();

POSITION ePos1=NULL,ePos2=NULL;

for(ePos1=m_ObjList[enEnemy].GetHeadPosition();(ePos2=ePos1)!=NULL;)

{

CEnemy* pEnemy = (CEnemy*)m_ObjList[enEnemy].GetNext(ePos1);

CRect mRect = pEnemy->GetRect();

CRect tmpRect;

if(tmpRect.IntersectRect(&bRect,mRect))

{

m_pMe->score += 10;

//int x=m_pMe->level;

if(m_pMe->score >= 1000*(m_pMe->level)) //&& (m_pMe->level)%2 == 1

m_pMe->level++;

//添加爆炸效果

m_ObjList[enExplosion].AddTail(

new CExplosion(mRect.left,mRect.top)

);

//删除导弹

if(m_pMe->Bkind != 2)

{

m_ObjList[enBomb].RemoveAt(mPos2);

delete pBomb;

}

//删除敌机

m_ObjList[enEnemy].RemoveAt(ePos2);

delete pEnemy;

break;

}

}

}

}

void CPlaneGameView::OnTimer(UINT_PTR nIDEvent)

{

//刷新游戏帧画面: 在内存DC上绘图

UpdateFrame(m_pMemDC);

shengcheng();

CView::OnTimer(nIDEvent);

}

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

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

4.程序运行时间长会卡对CObList中没用的数据要函数RemoveAt()进行清除。

5. 开始战机以及敌机的导弹没有间隔的发出,通过控制m_nWait,来进行控制

6. 在各种对象添加进ObjType后,在删除的时候忘记删除链表中的元素

通过RemoveAt来删除已删除的对象

6. 实训体会

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

相关主题
相关文档
最新文档