太空入侵者游戏.docx

上传人:b****2 文档编号:2783582 上传时间:2023-05-04 格式:DOCX 页数:38 大小:177.95KB
下载 相关 举报
太空入侵者游戏.docx_第1页
第1页 / 共38页
太空入侵者游戏.docx_第2页
第2页 / 共38页
太空入侵者游戏.docx_第3页
第3页 / 共38页
太空入侵者游戏.docx_第4页
第4页 / 共38页
太空入侵者游戏.docx_第5页
第5页 / 共38页
太空入侵者游戏.docx_第6页
第6页 / 共38页
太空入侵者游戏.docx_第7页
第7页 / 共38页
太空入侵者游戏.docx_第8页
第8页 / 共38页
太空入侵者游戏.docx_第9页
第9页 / 共38页
太空入侵者游戏.docx_第10页
第10页 / 共38页
太空入侵者游戏.docx_第11页
第11页 / 共38页
太空入侵者游戏.docx_第12页
第12页 / 共38页
太空入侵者游戏.docx_第13页
第13页 / 共38页
太空入侵者游戏.docx_第14页
第14页 / 共38页
太空入侵者游戏.docx_第15页
第15页 / 共38页
太空入侵者游戏.docx_第16页
第16页 / 共38页
太空入侵者游戏.docx_第17页
第17页 / 共38页
太空入侵者游戏.docx_第18页
第18页 / 共38页
太空入侵者游戏.docx_第19页
第19页 / 共38页
太空入侵者游戏.docx_第20页
第20页 / 共38页
亲,该文档总共38页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

太空入侵者游戏.docx

《太空入侵者游戏.docx》由会员分享,可在线阅读,更多相关《太空入侵者游戏.docx(38页珍藏版)》请在冰点文库上搜索。

太空入侵者游戏.docx

太空入侵者游戏

太空入侵者游戏

入侵者是一个简单的射击游戏。

游戏运行的初始界面如图3.14所示,游戏的战斗场面如图3.15所示。

 

图3.14入侵者程序初始运行图

 

图3.15入侵者作战运行图

在游戏中,玩家控制飞船,一方面要向敌机射击,另外还需要躲避敌人的子弹。

直到当前关卡中

所有敌机被消灭,玩家才能进入下一关卡。

要编译这个游戏,需要安装DirectX8.0SDK或以上版本。

在这个例子中要着重强调子弹、奖子(弹药、奖金等)、卷屏等设计技巧,以及射击过程是如何开

展的。

相信通过对这个游戏的理解,读者对射击游戏的构建会有更深刻的认识。

3.7.1奖子的设计

奖子是射击类游戏中必有的精灵,奖子实际就是玩家操作的飞船打掉敌机后,电脑奖赏的奖金、

弹药、防弹服等。

不过要想真正拥有这些奖子,玩家操纵的精灵必须在奖子消失(掉落到屏幕之外或

者在规定时间内消失)之前“吃”掉它。

“吃”实际上是一种碰撞检测行为。

而在吃掉奖子之前,奖子

通常会自动下落。

如图3.16所示是入侵者游戏中的奖子原始图。

Extras.bmp图片的分辨率是125⋅500。

 

图3.16入侵者中的奖子源视图extras.bmp

从图中不难看出入侵者中有5种奖子,每种奖子有20帧动画。

由于extras的分辨率是125⋅500,

所以平均每个奖子的每一帧是一幅25⋅25分辨率的图像。

在入侵者中,单独设计了一个奖子类Extra:

classExtra

{

private:

intx;//奖子在屏幕中的位置

inty;

RECTrcLastPos;//奖子上次出现在屏幕中的包围盒坐标

intiType;//奖子类型,从extras.bmp中可以看出共有5种类型

Extra*pNext;//奖子链表

Extra*pPrev;

public:

intframe;//帧编号,从extras.bmp中可以看出共有20帧

Extra()

{

x=0;

y=0;

pNext=NULL;

pPrev=NULL;

frame=0;

iType=0;

rcLastPos.left=0;

rcLastPos.top=0;

第3章2D游戏开发121

rcLastPos.right=0;

rcLastPos.bottom=0;

};

intGetType()

{

returniType;

}

voidSetType(intiNewType)

{

iType=iNewType;

}

intGetX()

{

returnx;

}

intGetY()

{

returny;

}

voidMove(intQtde)

{

rcLastPos.left=x;

rcLastPos.top=y;

rcLastPos.right=x+25;

rcLastPos.bottom=y+25;

if(rcLastPos.bottom>455)

rcLastPos.bottom=455;

//Qtde是一个负值,因为这种奖子都是垂直下落的,即y值需要不断增加

y-=Qtde;

}

voidSetXY(intnx,intny)

{

rcLastPos.left=nx;

rcLastPos.top=ny;

rcLastPos.right=nx+25;

rcLastPos.bottom=ny+25;

x=nx;

y=ny;

}

Extra*GetNext()

{

returnpNext;

}

122VisualC++游戏开发技术与实例

Extra*GetPrev()

{

returnpPrev;

}

voidSetNext(Extra*nNext)

{

pNext=nNext;

}

voidSetPrev(Extra*nPrev)

{

pPrev=nPrev;

}

BOOLDraw(LPDIRECTDRAWSURFACE7lpOrigin,LPDIRECTDRAWSURFACE7lpSource)

{

RECTrcRect;

HRESULThRet;

intiClipTop;

intiClipBottom;

//子弹在超出屏幕上方(y值小于0)和跨过屏幕下方(y+255>455)的时候需要进行裁剪

if(y<0)

iClipTop=y*-1;

else

iClipTop=0;

if(y+25>455)

iClipBottom=y+25-455;

else

iClipBottom=0;

rcRect.left=frame*25;

rcRect.top=((iType-1)*25)+iClipTop;

rcRect.right=frame*25+25;

rcRect.bottom=((iType-1)*25)+25-iClipBottom;

//帧数编号加1

frame++;

if(frame==20)

frame=0;

while

(1)

{

hRet=lpOrigin->BltFast(x,y,lpSource,&rcRect,DDBLTFAST_SRCCOLORKEY);

if(hRet==DD_OK)

{

break;

}

if(hRet==DDERR_SURFACELOST)

第3章2D游戏开发123

{

returnFALSE;

}

if(hRet!

=DDERR_WASSTILLDRAWING)

break;

}

returnTRUE;

}

};

很显然,Extra中私有成员变量x、y表示奖子方块左上角在屏幕上的像素坐标(事实上这两个变

量并不是必需的);而矩形结构rcLastPos表示奖子上次出现在屏幕时的坐标;整形的iType表示奖子

类型,这里共有5种类型的奖子。

公有变量frame表示帧编号,每种奖子都有20帧。

在Extra中__________读者

可能有疑惑的地方就是成员变量pNext和pPrev,因为从名称上读者就能猜出Extra变成了一种链表结

构。

这么做的理由很简单,因为屏幕中可能同时出现多个奖子,所以为了奖子管理的需要,不妨将其

设置成链表结构。

Extra中成员函数都比较简单,需要关注的只有Move函数和Draw函数。

Move函数中出现的常数25是由奖子图像的长度、宽度(25⋅25)决定的;而语句rcLastPos.bottom>

455是用来判断奖子是否已经掉出屏幕之外(确切的说是屏幕下限);语句y-=Qtde表示奖子是垂直下

落的,参数Qtde实际是一个负值,后面会看到这个值被设置成–3。

Draw函数中,首先碰到的问题就是对奖子的裁剪。

裁剪解决的方法是当精灵在屏幕之外时,计算

实际需要绘制精灵的部分。

在Draw函数里,裁剪操作如下:

if(y<0)

iClipTop=y*-1;

else

iClipTop=0;

if(y+25>455)

iClipBottom=y+25-455;

else

iClipBottom=0;

rcRect.left=frame*25;

rcRect.top=((iType-1)*25)+iClipTop;

rcRect.right=frame*25+25;

rcRect.bottom=((iType-1)*25)+25-iClipBottom;

当y小于0,表示当前奖子在屏幕上方(但不一定完全在上方),所以需要裁剪掉奖子的上半部分,

iClipTop指定了在25⋅25图像中,实际要绘制的顶部y值。

当y+25>455,表示当前奖子已经掉落到

屏幕的下方(但不一定完全掉落),所以需要裁剪掉奖子的下半部分,iClipBottom指定了在25⋅25图像

中,实际要绘制的底部y值。

如果这两个条件都不满足,则说明奖子完全落在屏幕内,需要完整绘制。

裁剪计算完成后,需要调整帧数,每种奖子都有20帧。

frame++;

if(frame==20)

frame=0;

当然,这段代码可以简化成“(frame++)%20”。

帧调整完成后,绘制奖子。

124VisualC++游戏开发技术与实例

lpOrigin->BltFast(x,y,lpSource,&rcRect,DDBLTFAST_SRCCOLORKEY);

在入侵者的主框架中定义了一个全局奖子对象Extra*pExtra。

因为Extra是一种链表结构,所以在

pExtra中记录了当前屏幕中所有奖子。

而主框架中绘制奖子的函数DrawExtra定义如下。

voidDrawExtra()

{

//绘制屏幕中所有奖子(奖金、弹药等)

Extra*pFirstExtra;

Extra*pNextExtra=NULL;

Extra*pLastExtra=NULL;

Extra*pPrevExtra=NULL;

//保存这个全局指针。

因为后面的操作会改变这个指针,而最后还要恢复它

pFirstExtra=pExtra;

//因为当前屏幕中可能有多个奖子,所以这里使用while语句就是要将所有的奖子都绘制出来

//当前屏幕中的奖子都被保存在同一个奖子链表中,所以绘制所有奖子时要求遍历整个奖子队列

while(pExtra!

=NULL)

{

//传入的是-3,即每次掉落3个像素点

pExtra->Move(-3);

//绘制当前节点奖子,lpExtra是extras.bmp的DDraw的Surface

pExtra->Draw(lpBackBuffer,lpExtra);

//如果当前的奖子已经掉落到屏幕以外,则调整奖子列表,删除当前奖子节点

if(pExtra->GetY()>455)

{

//调整奖子链表节点和相关指针

if(pPrevExtra!

=NULL)

pPrevExtra->SetNext(pExtra->GetNext());

pNextExtra=pExtra->GetNext();

if(pNextExtra!

=NULL)

pNextExtra->SetPrev(pPrevExtra);

//如果当前奖子节点位于链表中的第一位置,则还需要再调整链表

if(pExtra==pFirstExtra)

pFirstExtra=pExtra->GetNext();

//删除当前奖子节点

delete(pExtra);

//重置当前奖子节点

pExtra=pNextExtra;

}

else

{

//遍历奖子链表:

当前奖子节点指向链表中下一个节点

pPrevExtra=pExtra;

第3章2D游戏开发125

pExtra=pExtra->GetNext();

}

}

pExtra=pFirstExtra;

}

事实上,DrawExtra中很大部分的工作都是对Extra链表的操作。

最后的问题就是如何“吃”奖子?

在前面已经提过了,这是一个碰撞检测问题。

在主框架中定义

了“吃”奖子函数CheckHitExtra。

通常“吃”奖子的碰撞检测采用的是最简单的矩形包围盒技术,这

里正是用了这种方法。

当然“吃”完了奖子后,必须把它从奖子链表中删除。

intCheckHitExtra(void)

{

Extra*pFirstExtra,*pPrevExtra,*pNextExtra;

intiReturn=0;

pPrevExtra=NULL;

pNextExtra=NULL;

pFirstExtra=pExtra;

//检测所有奖子是否和飞船发生了碰撞

while(pExtra!

=NULL)

{

//如果奖子在飞船的矩形包围盒内,则说明发生了“吃”的操作

//一旦吃掉,则需要删掉该奖子,同时调整链表

if((pExtra->GetX()>=iShipPos&&

pExtra->GetX()

pExtra->GetY()>385&&

pExtra->GetY()<425)&&

(pExtra->GetType()!

=0))

{

if(pPrevExtra!

=NULL)

pPrevExtra->SetNext(pExtra->GetNext());

pNextExtra=pExtra->GetNext();

if(pNextExtra!

=NULL)

pNextExtra->SetPrev(pPrevExtra);

if(pExtra==pFirstExtra)

pFirstExtra=pExtra->GetNext();

iReturn=pExtra->GetType();

delete(pExtra);

pExtra=pFirstExtra;

returniReturn;

}

pPrevExtra=pExtra;

pExtra=pExtra->GetNext();

}

pExtra=pFirstExtra;

returniReturn;

}

126VisualC++游戏开发技术与实例

3.7.2子弹(Bullet)的设计

在入侵者中子弹的设计几乎是和奖子的设计一样的,只是在图像的宽度和高度上需要进行调整。

读者不妨查看类Bullet,然后再和Extra类做个比较。

不过子弹的设计并不是一成不变的。

在游戏设计中,子弹的设计方式和方法很多,这里只是采用

了静态的直线传递方法。

子弹的弹道通常决定了子弹设计的方式。

例如,很多游戏中的追踪弹、散花

弹和激光弹等。

对于追踪弹来说,它会自动锁定目标,当然这并不意味着一定能够击落敌机。

通常情

况下,敌机如果可以将追踪弹引出屏幕之外就算追踪弹无效,这是很公平的决策。

在追踪弹设计中,

弹道的设计需要平滑,因为折线式的追踪轨迹并不现实。

平滑的方法很多,样条曲线就是一个不错的

选择,在第4章中会介绍到一种3次样条曲线的设计方法。

对于散花弹而言,可以考虑成由多个单一

的子弹组成,不过这种方法需要考虑效率。

而激光弹是一种特殊的子弹,它的有效攻击范围实际是会

变化的。

这种子弹的设计需要考虑真实世界的激光。

当打开激光源的时候,光会快速的形成一条射线,

在这条轨迹内的任何物体实际上都会被激光照射,而当关闭激光源的时候,激光总是消失在远处。

据这种物理规律,才能够设计出比较合理的激光子弹。

3.7.3卷屏(Scroll)的设计

在入侵者游戏中,为了体现游戏中的飞船处于飞行状态,采用了一种简单的卷屏技术。

这种卷屏

技术是利用背景图片的变化实现的。

如图3.17所示是入侵者的背景图片。

它是640⋅480分辨率的图片。

而该图片中底部的状态栏占据了25个像素的高度。

图3.17背景图片

卷屏函数DrawInvalidBackGround定义如下:

voidDrawInvalidBackGround()

{

//重绘背景

RECTrcRect;

HRESULThRet;

Staticy=0;

constintiFim=455;

//新关卡

第3章2D游戏开发127

if(lastTickCount=0)

{

y=0;

}

//向下卷屏一个像素

y+=1;

//如果已经卷完455个像素(即一个屏幕),则y值重新置0。

if(y>iFim)

y=0;

//上半屏的矩形区域

rcRect.left=0;

rcRect.top=0;

rcRect.right=640;

rcRect.bottom=iFim-y;

while

(1)

{

hRet=lpBackBuffer->BltFast(0,y,lpBkGround,

&rcRect,DDBLTFAST_NOCOLORKEY);

if(hRet==DD_OK)

{

break;

}

if(hRet==DDERR_SURFACELOST)

{

hRet=RestoreSurfaces();

if(hRet!

=DD_OK)

break;

}

if(hRet!

=DDERR_WASSTILLDRAWING)

break;

}

//下半屏的矩形区域

rcRect.left=0;

rcRect.top=iFim-y;

rcRect.right=640;

rcRect.bottom=iFim;

while

(1)

{

hRet=lpBackBuffer->BltFast(0,0,lpBkGround,

&rcRect,DDBLTFAST_NOCOLORKEY);

if(hRet==DD_OK)

{

break;

}

if(hRet==DDERR_SURFACELOST)

{

hRet=RestoreSurfaces();

128VisualC++游戏开发技术与实例

if(hRet!

=DD_OK)

break;

}

if(hRet!

=DDERR_WASSTILLDRAWING)

break;

}

}

DrawInvalidBackGround函数使用的卷屏方法是将背景图片从水平方向分成两个部分,然后分别绘

制上下部分的图片。

背景图片的分割线是从关卡开始后背景下降的y值累加之和。

每次绘制背景的时

候y值都要增加1个像素,直到y值等于455(480中包括了25个像素高度的状态栏)后重置为0。

通过这样的两次背景绘制,屏幕就动了起来。

需要指出的是,对于一些复杂的横板卷屏游戏而言,这种方法就完全行不通了。

因为横板游戏的

背景中包含了复杂的地形,这种地形是影响游戏发展的,而不像在入侵者中背景对整个游戏进程毫无

影响。

上面的DrawInvalidBackGround函数是在UpdateFrame中被调用的,而UpdateFrame函数负责的

是整个游戏状态的刷新。

其中屏幕刷新的时候,遵守一个刷新顺序:

背景刷新、奖子刷新、敌机刷新、

子弹刷新、飞船刷新。

下面来看看UpdateFrame函数是如何工作的。

UpdateFrame函数在开始部分定义了一些局部变量:

voidUpdateFrame(void)

{

intddrval;

DWORDthisTickCount;

RECTrcRect;

DWORDdelay=18;

staticintframe=0;

HBITMAPhbm;

DDBLTFXddbltfx;

HRESULThRet;

接着,它判断飞船是否已经爆炸了一段时间。

如果是这样,则载入游戏结束的画面,重置一些全

局变量。

thisTickCount=GetTickCount();

if(thisTickCount-dwShipExplode>2000&&iShipState==3)

{

Ovni*auxpUFO;

Bullet*auxpBullet;

while(pUFO!

=NULL)

{

auxpUFO=pUFO->GetNext();

delete(pUFO);

pUFO=auxpUFO;

}

while(pBullet!

=NULL)

{

auxpBullet=pBullet->GetNext();

第3章2D游戏开发129

delete(pBullet);

pBullet=auxpBullet;

}

ShowGameOver();

lastTickCount=0;

iAppState=0;

iShipState=0;

}

下面判断当前游戏处于何种状态。

其中APP_MAINMENU标志表示当前游戏处于主菜单状态;

APP_GAMESCREEN标志表示游戏已经开始;APP_CREDITS表示游戏处于显示积分的状态;

APP_HELPSCREEN表示游戏处于帮助屏幕状态。

switch(iAppState)

{

caseAPP_MAINMENU:

//如果时间间隔过短,则返回

if((thisTickCount-lastTickCount)<=delay)

return;

//如果是主菜单界面,则需要做一些初始化工作

if(lastTickCount==0)

{

iOption=0;

frame=0;//设置飞船光标帧标号为0

//载入初始界面的bmp文件

hbm=(HBITMAP)LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE

(IDB_INVASION),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);

if(NULL==hbm)

return;

//将初始界面拷贝到前台缓冲中

ddrval=DDCopyBitmap(lpFrontBuffer,hbm,0,0,640,480);

if(ddrval!

=DD_OK)

{

DeleteObject(hbm);

return;

}

//将初始界面拷贝到后台缓冲中

ddrval=DDCopyBitmap(lpBackBuffer,hbm,0,0,640,480);

if(ddrval!

=DD_OK)

{

DeleteObject(hbm);

return;

}

//画出4个菜单,STARTGAME、CREDITS、HELP和QUIT。

bltText("STARTGAME\0",190,280);

130VisualC++游戏开发技术与实例

bltText("CREDITS\0",190,320);

bltText("HELP\0",190,360);

bltText("QUIT\0",190,400);

}

ddbltfx.dwSize=sizeof(ddbltfx);

ddbltfx.dwFillColor=dwFillColor;

rcRect.left=130;

rcRect.top=270;

rcRect.right=190;

rcRect.bottom=460;

while

(1)

{

//填充后台缓冲的某个矩形区域为黑色

hRet=lpBackBuffer->Blt(&rcRect,NULL,NULL,DDBLT_COLORFILL,&ddbltfx);

if(hRet==DD_OK)

{

break;

}

if(hRe

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 党团工作 > 入党转正申请

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2