您的当前位置:首页VC++超级玛丽游戏系统

VC++超级玛丽游戏系统

来源:小侦探旅游网


课程设计说明书 NO.1

超级玛丽游戏系统 1.课程设计的目的 随着科学技术的发展,人们的生活水平的提高,然而也越来越繁忙。在充满竞争的社会条件,压力往往带给人们很多烦恼。为了给大家提供一个合理的解压平台,在本次课程设计中为大家开发了一款适合休闲解压的小游戏,超级玛丽 。 本次设计加深对面向对象程序设计中基础理论和基本知识的理解,培养学生的实践能力,促进理论与实践的结合。通过课程设计,能够熟练运用C++进行面向对象编程,建立对象模型,降低软件的复杂性,改善软件的重用性和维护性,提高软件的生产效率,全面掌握面向对象编程技术。 2.设计方案论证 2.1设计思路 游戏基本功能: 1. 玩家起初只能用普通子弹或踩踏杀死敌人,但是踩踏无法杀死特殊敌人,无法顶碎砖块。被敌人碰到将失去一条生命。 2. 在玩家吃旋风炮弹之后,攻击能力变强,可以轻松杀死普通敌人和特殊敌人。 3. 玩家一共有三条命的机会去闯关超级玛丽游戏,玩家要在一定时间内,通过游戏。如果三条生命都丢掉,将输掉整个游戏。 本游戏的用例模型如图1所示。 -精品-

课程设计说明书 NO.1

图1 用例模型 2.2开发工具选择 代码编写调试使用Visual C++ 6.0。美术工具,Windows自带的画图(开始-程序-附件-画图),Photoshop,记事本或UltraEdit等文本编辑工具。 3.设计结果与分析 一个好的游戏一定要有完整的功能,漂亮的界面,为了设计出美观完善的超级马力游戏码分两部分,图像部分,逻辑部分。图像分两种,矩形图片和不规则图片。 矩形图片有:地面、砖块、水管、血条、血条背景。 不规则图片有:蘑菇(玩家,敌人1,敌人2),子弹、旋风、爆炸效果、金币、撞击金币后的得分、攻击武器(那个从魂斗罗里抠来的东东)、火圈1、火圈2、箭头(用于开始菜单选择)、树木、河流、WIN标志、背景图片(游戏背景和菜单背景)。 3.1类结构 图像层类如表1所示: 表1 图像层 功能 图像基类 游戏背景 类名称与继承关系 MYBITMAP MYBKSKY-精品-

MYBITMAP

课程设计说明书 NO.1

游戏图片 魔法攻击 结构和表相关类如表2所示: MYANIOBJMYANIMAGICMYBITMAP MYBITMAP 表2 结构和表 功能 精灵结构 物品结构 地图信息表 类名称 ROLE MapObject MAPINFO -精品-

课程设计说明书 NO.1

逻辑层类如表3所示: 表3 逻辑层 功能 游戏逻辑 时钟处理 字体处理 跟踪打印 玩家控制 类名称与继承关系 GAMEMAP MYCLOCK MYFONT FILEREPORT MYROLEMYBITMAP 3.2类的实现 3.2.1图片基类MYBITMAP 图片基类MYBITMAP把代码写成一个容易扩展、容易维护、功能全面的“框架”,需要什么功能,就从这个框架中取出相应功能,如果是一个新的功能,比如新的图像显示、新的运动控制,也能方便地实现。所以,这个游戏的代码,是在前几个游戏的基础上扩充起来的。部分函数,部分变量在这款游戏中,根本不用,但要保留,要为下一款游戏作准备。只要理解了各个类,就理解了整个框架。 简单的介绍几个成员函数。 功能: 根据一个位图文件,初始化图像 入参: 应用程序实例句柄 资源ID 横向位图个数 纵向位图个数 void Init(HINSTANCE hInstance,int iResource,int row,int col); 功能: 设置环境信息 入参: 目的DC(要绘制图像的DC),临时DC,要绘制区域的宽 高 -精品-

课程设计说明书 NO.1

void SetDevice(HDC hdest,HDC hsrc,int wwin,int hwin); 功能: 设置图片位置 入参: 设置方法 横纵坐标 void SetPos(int istyle,int x,int y); 功能: 图片显示 入参: 图片显示方式 void Draw(DWORD dwRop); 功能: 图片缩放显示 入参: 横纵方向缩放比例 void Stretch(int x,int y); 功能: 图片缩放显示 入参: 横纵方向缩放比例 缩放图像ID(纵向第几个) 基类的函数实现上,很简单,主要是以下几点: 1. 图片初始化 根据程序实例句柄,由位图文件的资源ID,导入该位图,得到位图句柄。 hBm=LoadBitmap(hInstance,MAKEINTRESOURCE(iResource)); 获取该位图文件的相关信息。 GetObject(hBm,sizeof(BITMAP),&bm); 根据横纵方向的图片个数,计算出每个图片的宽高(对于超级玛丽,宽高信息由派生类处理)。 width=bm.bmWidth/inum; height=bm.bmHeight/jnum; -精品-

课程设计说明书 NO.1

2. 图片显示 各个图片的显示函数基本大同小异,都要先选入一个临时DC,再bitblt到要绘制的dc上。矩形图片可以直接用SRCCOPY的方式绘制。不规则图片,需要先用黑白图与目的区域相“与”(SRCAND),再用“或”的方法显示图像(SRCPAINT),这是一种简单的“去背”方法。 3. 图片缩放 用StretchBlt的方法实现。 void MYBITMAP::Stretch(int x,int y,int id) { } SelectObject(hdcsrc,hBm); StretchBlt(hdcdest,xpos,ypos,width*x,height*y, hdcsrc,0,id*height, width,height, SRCCOPY); 3.2.2游戏背景类MYBKSKY 这是一个专门处理游戏背景的类。在横版游戏或射击游戏中,都有一个背景画面如图2所示,如山、天空、云、星空等等。这些图片一般只有1到2倍屏幕宽度,然后像一个卷轴一样循环移动,连成一片,感觉上像一张很长的图片。这个类就是专门处理这个背景的。在超级玛丽增强版中,主要关卡是3关,各有一张背景图片;从水管进去,有两关,都用一张全黑图片。共四张图。这四张图大小一致,纵向排列在一个位图文件中。MYBKSKY这个类,派生于MYBITMAP。由于背景图片只需要完成循环移动的效果,只需要实现一个功能,而无需关心其他任何问题(例如句柄、dc)。 -精品-

课程设计说明书 NO.1

图2 背景图 技术原理:一张图片像卷轴一样不停移动。假设有一条垂直分割线,把图片分成左右两部分。先显示右边部分,再把左边部分接到图片末尾。不停移动向右移动分割线,图片就会循环地显示。 成员函数功能列表: class MYBKSKY:public MYBITMAP { public: MYBKSKY(); ~MYBKSKY(); //show //功能 显示一个背景. //入参 无 void DrawRoll(); //循环补空 //功能 显示一个背景,并缩放图片 //入参 横纵方向缩放比例 void DrawRollStretch(int x,int y); //功能 指定显示某一个背景,并缩放图片,游戏中用的就是这个函数 //入参 横纵方向缩放比例 背景图片ID(纵向第几个) -精品-

课程设计说明书 NO.1

}; void DrawRollStretch(int x,int y,int id); //功能 设置图片位置 //入参 新的横纵坐标 void MoveTo(int x,int y); //功能 循环移动分割线 //入参 分割线移动的距离 void MoveRoll(int x); //data //分割线横坐标 int xseparate; 函数具体实现代码如下: void MYBKSKY::DrawRollStretch(int x,int y, int id) { //选入句柄 SelectObject(hdcsrc,hBm); //将分割线右边部分显示在当前位置 StretchBlt(hdcdest, xpos,ypos, //当前位置 //缩放比例 (width-xseparate)*x,height*y, hdcsrc, xseparate,id*height, //右边部分的坐标 width-xseparate,height, //右边部分的宽高 SRCCOPY); //将分割线左边部分接在图片末尾 StretchBlt(hdcdest,xpos+(width-xseparate)*x,ypos, xseparate*x,height*y, hdcsrc,0,id*height, xseparate,height, SRCCOPY); -精品-

课程设计说明书 NO.1

} 3.2.3跟踪打印类FILEREPORT 前面介绍了图片显示、时钟控制、字体管理几项基本技术。这是所有游戏都通用的基本技术。剩下的问题就是游戏逻辑,例如益智类、运动类、射击类、格斗类等等。函数具体实现很简单,只是简单的文件写入。要说明的是两部分。 第一部分:计数功能,有时要统计某个事情发生多少次,所以用一个整数数组,通过putnum让指定数字累加。 第二部分:显示功能,让跟踪信息,立刻显示在屏幕上。具体的代码实现见附件。 3.3精灵结构struct ROLE 逻辑层:struct ROLE。这个结构用来存储两种精灵:敌人(各种小怪)和子弹(攻击方式)。敌人包括两种蘑菇兵和两种火圈。子弹包括火球和旋风。 精灵的结构实现需要定义精灵的横纵坐标,精灵的运动左右界限,运动速度,生命值等等。 游戏中的子弹处理,包括存储、生成、销毁。 子弹的存储:所有的子弹存储在一个数组中,如下。 struct ROLE FireArray[MAX_MAP_OBJECT]; 所有的动态元素都有从生成到销毁的过程。 首先,玩家按下z键:发出子弹,调用函数: int GAMEMAP::KeyProc(int iKey)。 如果正在发子弹,代码结束。否则,设置iBeginFire为1,表示开始发子弹。连续两个子弹要间隔一定时间。 至此, 游戏中已经生成了一个子弹. 由图像层,通过子弹的id,坐标在屏幕上绘制出来.-精品-

课程设计说明书 NO.1

子弹已经显示在屏幕上, 接下来, 就是让它移动, 碰撞, 销毁。如图3所示子弹发射图。 图3 子弹发射图 3.4子弹的显示和帧的刷新 上次讲所有的子弹存储到一个数组里。用一个游标(数组下标)表示新生产的子弹存储的位置。设数组为a,长度为n。游戏开始,一个子弹存储在a0,然后是a1,a2,...,a(n-1).然后游标又回到0,继续从a0位置存储.数组长度30,保存屏幕上所有的子弹足够了。 子弹的显示功能由图像层完成。如同图像处理中讲的。显示一个子弹(所有图片都是如此),只需要子弹坐标,子弹图片id,图片帧。 函数:void GAMEMAP::ShowAniObj(MYANIOBJ & bmobj) 用一个函数不停地检测所有子弹,,如果某个子弹碰到了小怪,小怪消失,子弹消失。 实现由函数: int GAMEMAP::CheckAni(int itimeclip) 至此,玩家按下攻击键然后子弹生成,显示运动,碰到小怪,子弹消失,小怪消失。这些功能全部完成。如果只做成这样,不算本事.攻击方式分两种:子弹和旋风。小怪包括:两种蘑菇兵、两种火圈。同时火圈能产生两种蘑菇兵。而旋风的攻击效果明显高于普-精品-

课程设计说明书 NO.1

通子弹。 3.5旋风攻击,小怪运动,火圈 作为魔法攻击方式的旋风,和子弹大同小异。旋风的存储与子弹同存储在一个数组中即: struct ROLE FireArray[MAX_MAP_OBJECT]; 旋风生成函数: int GAMEMAP::CheckAni(int itimeclip) 这和子弹生成的处理相同。唯一区别是旋风不移动,所以movex属性最后设置为0。在幕上的绘制和子弹相同,函数void GAMEMAP::ShowAniObj(MYANIOBJ & bmobj)。代码部分和子弹相同。但是旋风的帧刷新有些特殊处理,函数是: void GAMEMAP::ChangeFrame(int itimeclip) 至此,旋风将会显示,动画播放结束后消失。旋风不涉及运动。碰撞检测的处理和子弹相同,唯一区别是:当旋风和小怪碰撞,旋风不消失,如图4旋风与小怪的碰撞。 图4 旋风与小怪的碰撞 小怪可以分为:蘑菇兵和火圈两种。 存储是我问题.和攻击方式处理相同, 用数组加游标的方法.蘑菇兵和火圈存储在同-精品-

课程设计说明书 NO.1

一数组中,如下: struct ROLE MapEnemyArray[MAX_MAP_OBJECT]; int iMapEnemyCursor; 小怪是由地图文件设定好的。比生成子弹要复杂一些,尤其是火圈。火圈要不停地生成蘑菇兵,所以“分区管理”,数值前一半存储蘑菇兵,后一半存储火圈。 3.6玩家动作控制MYROLE 玩家移动:把行走和跳跃看成两个状态,各自用不同的变量表示横纵方向的速度。 相关属性: 行走:横向速度为movex,纵向不移动。 跳跃:横向速度为jumpx,纵向速度为movey。当前跳跃高度jumpheight。 运动方向:idirec 思路: 第一步:玩家按键,按键处理函数设置这些属性。按键松开,清除动作属性。 第二步:用一个函数不停检测这些变量,控制玩家移动。 1. 按键触发: 按键处理函数:int GAMEMAP::KeyProc(int iKey) 按键松开处理函数:void GAMEMAP::KeyUpProc(int iKey) 2. 控制移动: 动作检测函数:WndProc 时间片的处理中,根据不同状态,调用各种检测函数。每45毫秒产生一个WM_TIMER消息,在GAME_IN状态下,调用各种检测函数。其中rmain.Move()就是不断检测玩家动作属性,实现移动。 -精品-

课程设计说明书 NO.1

玩家移动函数:void MYROLE::MoveOffset(int x,int y) 3. 碰撞检测 无论行走、跳跃,都是用函数MoveOffset操纵玩家坐标。这时,就要判断是否碰到物体。如果正在行走,则不能前进;如果是跳跃上升,则开始下落。 函数:int GAMEMAP::RoleCanMove(int xoff, int yoff) 玩家移动的过程中,要不断检测是否站在地图物品上。如果在行走过程中,且没有站在任何物品上,则开始下落。函数:int GAMEMAP::CheckRole()。 至此,玩家在这个虚拟世界可以做出各种动作跳跃、行走、攻击。如图5玩家跳跃。 图5玩家跳跃 3.7玩家动作控制 玩家在进出水管的时候,需要进入水管、从水管中升起两个动画。当动画播放结束后,切换到新的地图。 动画播放过程中,禁止键盘响应,即玩家不能控制移动。 1. 玩家进水管。 地图物品中,水管分两个,进水管(玩家进入地图)和出水管(从别的地图返回)。两种水管对应不同的图片ID: #define ID_MAP_PUMP_IN 9 -精品-

课程设计说明书 NO.1

#define ID_MAP_PUMP_OUT 10 玩家进入水管的检测: 函数:int GAMEMAP::KeyProc(int iKey) 2. 玩家进入水管后,切换地图 调用函数:WndProc 3. 从水管中上升,根据不同的动画方式,播放动画 动画播放函数:void MYROLE::PlayAni() 如图6进入水管。 图6进入水管 3.8运行 一个程序,需要进行最后一步的验收那就是,对其进行测试。根据弄能操作超级玛丽游戏,观察是否能实现其设计要求。 基本操作:z-子弹 x-跳 方向键左-左移动 方向键右-右移动。 首先,打开mario01.exe程序。弹出运行界面如图7所示。 -精品-

课程设计说明书 NO.1

图7 运行界面 经过实际操作游戏,各个按键均实现其功能。 当与怪物接触。玛丽死掉。当与金币接触,得到金币,如图8得到金币 。MONEY:0 变成 MONEY:40。 图8 吃金币 则经过测试 ,该游戏功能完整 。运行良好。满足设计要求,本次超级玛丽游戏设计完毕。 4.设计体会 通过这次课程设计,使我充分的了解了VC++的理论与实践相结合的重要性,在充-精品-

课程设计说明书 NO.1

分的理论基础上必须有实际上的应用,才能灵活应用课堂上所学的理论知识。我们不仅能够把以前所学的知识联系起来,并且学会了一些新的知识。虽然我们小组的分工明确,但是每一个人对设计的各个阶段都相当的了解。 此次的课程设计中给我体会最深的是我们的协作关系。我们是一个整体,通过互相学习,互相帮助,翻阅大量资料完成了这次有意义的超级玛丽游戏设计。不仅增强了我们彼此之间的友谊,更让我们了解到团体力量的伟大。 在本系统的开发过程中,在知识、经验方面都存在着不足。今后会通过我们的努力进一步完善超级玛丽游戏。 5.参考文献 [1] 郑阿奇 Visual C++实训 [M]. 北京:清华大学出版社,2005 [2]丁振凡.C++语言实用教程[M].北京:北京邮电大学出版社,2005.2:105-121 [3]张广彬,孟红蕊,张永宝.C++课程设计案例精编[M].北京:清华大学出版社,2005.2:355-363 [4]朱喜福.C++程序设计[M].北京:人民邮电出版社,2005.9:179-203 [5]宛延闿.实用C++程序设计教程[M].北京:机械工业出版社,2006.4:234-250 -精品-

课程设计说明书 NO.1

附录 1. GAMEMAP void GAMEMAP::ShowAniObj(MYANIOBJ & bmobj) 代码部分: //显示子弹,魔法攻击 for(i=0;i课程设计说明书 NO.1

//功能 向日志文件写入一个字符串,两个整数 //入参 字符串 整数a 整数b void put(char *p,int a,int b); //功能 计数器计数, 并写入一个提示字符串 //入参 计时器id 字符串 void putnum(int i,char *p); //功能 判断一个dc是否为null, 如果是,写入提示信息 //入参 dc句柄 字符串 void CheckDC(HDC h,char *p); //功能 设置显示跟踪信息的dc和文本坐标 //入参 显示dc 横纵坐标 void SetDevice(HDC h,int x,int y); //功能 设置要显示的跟踪信息 //功能 提示字符串 整数a 整数b void Output(char *p,int a,int b); //功能 在屏幕上显示当前的跟踪信息 void Show(); private: //跟踪文件指针 FILE *fp; //计数器组 int num[5]; //显示dc HDC hshow; //跟踪文本显示坐标 int xpos; int ypos; //当前跟踪信息 char info[50];}; -精品-

课程设计说明书 NO.1

3. 精灵结构struct ROLE struct ROLE { int x;//横坐标 int y;//纵坐标 int w;//图片宽度 int h;//图片高度 int id;//精灵id int iframe;//图片当前帧 int iframemax;//图片最大帧数 //移动部分 int xleft;//水平运动的左界限 int xright;//水平运动的右界限 int movex;//水平运动的速度 //人物属性 int health;//精灵的生命值 int show; //精灵是否显示 }; 4.发射子弹 //发子弹 if(iBeginFire) { //发子弹的时间到了(连续两个子弹要间隔一定时间) if(0 == iTimeFire ) { //设置子弹属性: 可见, 动画起始帧:第0帧 FireArray[iFireNum].show=1; FireArray[iFireNum].iframe = 0; //子弹方向 //如果人物朝右 if(0==rmain.idirec) -精品-

课程设计说明书 NO.1

{//子弹向右 FireArray[iFireNum].movex=1; } else {//子弹向左 FireArray[iFireNum].movex=-1; } //区分攻击种类: 子弹,旋风 switch(iAttack) {//普通攻击: 子弹 case ATTACK_NORMAL: //精灵ID: 子弹 FireArray[iFireNum].id=ID_ANI_FIRE; //设置子弹坐标 FireArray[iFireNum].x=rmain.xpos; FireArray[iFireNum].y=rmain.ypos; //设置子弹宽高 FireArray[iFireNum].w=FIREW; FireArray[iFireNum].h=FIREH; //设置子弹速度: 方向向量乘以移动速度 FireArray[iFireNum].movex*=FIRE_SPEED; break; 5. 函数int GAMEMAP::CheckAni(int itimeclip) //子弹移动 for(i=0;i课程设计说明书 NO.1

FireArray[i].x+=FireArray[i].movex; //判断子弹是否超出了屏幕范围,如果超出,子弹消失(设置为不可见) } if( FireArray[i].x > viewx+VIEWW || FireArray[i].x课程设计说明书 NO.1

//子弹消失 FireArray[j].show=0; } -精品-

因篇幅问题不能全部显示,请点此查看更多更全内容