《软件建模与实践》课件-10-俄罗斯方块游戏_第1页
《软件建模与实践》课件-10-俄罗斯方块游戏_第2页
《软件建模与实践》课件-10-俄罗斯方块游戏_第3页
《软件建模与实践》课件-10-俄罗斯方块游戏_第4页
《软件建模与实践》课件-10-俄罗斯方块游戏_第5页
已阅读5页,还剩46页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

俄罗斯方块游戏软件分析与设计过程文档视图结构软件的架构主要内容

理解游戏开发中的逻辑抽象掌握文档视图结构软件的设计课程目的重难点文档视图结构10.1需求分析

俄罗斯方块(Tetris)游戏是由俄罗斯人阿列克谢·帕基特诺夫发明的。Tetris游戏开始后,由4个小方块组成的不同形状的砖块随机出现,之后从屏幕上方中央落下,玩家通过调整砖块的位置和方向,使它们在屏幕底部拼出完整的一条或几条,这些完整的横条会被消除,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束。10.1需求分析

通过分析,Tetris游戏在一个m×n的矩形框内进行。游戏开始时,矩形框的顶部会随机出现一个由四个小方块构成的砖块(七种形状),每过一个很短的时间(我们称这个时间为一个tick),它就会下落一格,直到它碰到矩形框的底部,然后再经过一个tick,它就会固定在矩形框的底部,成为固定块。接着再过一个tick顶部又会出现下一个随机形状,同样每隔一个tick都会下落,直到接触到底部或者接触到下面的固定块时,再过一个tick它也会成为固定块,再过一个tick之后会进行检查,发现有充满方块的行则会消除它,同时顶部出现下一个随机形状。直到顶部出现的随机形状在刚出现时就与固定块重叠,表示游戏结束。10.1需求分析由上可知,系统需要完成的功能如下:(1)方块生成:当游戏运行开始或方块成为固定块后,应能在游戏面板顶部随机生成一个新方块,这样便于玩家提前进行控制处理。(2)方块控制:游戏玩家可以对出现的方块进行移动处理,分别实现左移、右移、旋转、快速下移、自由下落和行满自动消除功能的效果。10.1需求分析(3)更新显示:当在游戏中移动方块时,需要先消除先前的游戏方块,然后在新坐标位置重新绘制该方块。(4)分数与速度更新:设定玩家得分奖励的规则,例如,可以设置消除完整的一行得10分,两行得30分。当分数达到一定数量后,需要给游戏者进行等级上的升级,并升级难度,例如,当玩家级别升高后,方块的下落速度将加快。(5)系统帮助:玩家进入游戏系统后,通过帮助了解游戏的操作方式。10.2设计过程10.2.1

功能设计根据需求分析,俄罗斯方块游戏的功能结构如图10-1所示。图10-1

俄罗斯方块游戏软件功能结构图10.2.1

功能设计1.方块生成新游戏的方块使用随机函数rand()可以产生0~6之间的游戏方块编号。2.方块控制方块的移动控制是整个游戏的重点和难点,具体为:(1)左移处理过程。①判断是否能够左移,判断条件有两个:左移一位后方块不能超越游戏底板的左边线,否则将越界;并且在游戏方块有值(值为1)的位置,游戏底板是不能被占用的(占用时值为1)。10.2.1

功能设计②清除左移前的游戏方块。③在左移一位的位置处,重新显示该游戏方块。(2)右移处理过程。①判断是否能够右移,判断条件有两个:右移一位后方块不能超越游戏底板的右边线,否则将越界;游戏方块有值的位置,游戏底板不能被占用。②清除右移前的游戏方块。③在右移一位的位置处,重新显示该游戏方块。10.2.1

功能设计(3)下移处理过程。①判断是否能够下移,判断条件有两个:下移一位后方块不能超越游戏底板的底边线,否则将越界;游戏方块有值的位置,游戏底板不能被占用。满足上述两个条件后,可以被下移处理。②清除下移前的游戏方块。③在下移一位的位置处,重新显示该游戏方块。10.2.1

功能设计(4)旋转处理过程。①判断是否能够旋转,判断条件有两个:旋转后方块不能超越游戏底板的底边线、左边线和右边线,否则将越界;游戏方块有值的位置,游戏底板不能被占用。②清除旋转前的游戏方块;③

在旋转后的位置处,重新显示该游戏方块。10.2.1

功能设计3.更新显示当游戏中的方块在进行移动处理时,要清除先前的游戏方块,用新坐标重绘该游戏方块。当消除满行后,要重绘游戏底板的当前状态。4.速度分数更新当行满后,积分变量score会增加一个固定的值,然后将等级变量level和速度变量speed相关联,实现等级越高速度越快的效果。需要一个检查一行是否填满的函数。10.2.2类的设计俄罗斯方块游戏的业务逻辑主要需要对两个类进行类设计,一是游戏面板矩形框类;二是砖块类。1.俄罗斯方块游戏的矩形框类——CBin首先,定义一个CBin类描述俄罗斯方块游戏的矩形框。对矩形框进行分析,它应该有三个私有的数据成员:image、width和height。CBin类将俄罗斯方块游戏的矩形框描述成为一个二维数组image,变量width和height存储了image的维数,如图10-2所示。有砖块的地方的值为砖块的颜色值(例如1为红色,4为蓝色),没有砖块的地方应为0值。10.2.2类的设计图10-2

俄罗斯方块游戏的矩形框接下来为CBin类添加5个成员函数,函数说明如表10-1所示。表10-1CBin类的成员函数说明函数名称函数说明CBin(unsignedintw,unsignedinth)构造函数,用来初始化数据成员width和height,并为image分配空间并初始化~CBin()析构函数,删除在构造函数中为image分配的空间voidgetImage(unsignedint**destImage)将image的数据拷贝到destImage,可以假设destImage指向的空间足够容纳image的数据voidsetImage(unsignedint**srcImage)把srcImage中的数据拷贝到image,你可以假设srcImage是一个合法的指针unsignedintremoveFullLines()检查image,如果任何一行完全填满,则删除这一行,并让上面行的数据下移一行,返回删除的总行数10.2.2类的设计2.俄罗斯方块游戏的砖块类接下来完成游戏的砖块类,常用砖块如图10-3所示。图10-3

俄罗斯方块游戏的矩形框10.2.2类的设计每个砖块的基本形状都是由4个子块组成,在此基础上对这4个子块基于x、y方向上的相对位置进行编码,分别得到以下两组二维数组。

其中,brickX存放了7种砖块中4个子块的x坐标,brickY存放了7种砖块中4个子块的y坐标。staticintbrickX[7][4]={{0,1,2,3},{0,1,1,2},{2,1,1,0},{1,1,2,2},{0,0,1,2},{2,2,1,0},{0,1,1,2}};staticintbrickY[7][4]={{0,0,0,0},{0,0,1,1},{0,0,1,1},{0,1,0,1},{0,1,1,1},{0,1,1,1},{0,0,1,0}};CBrick类的成员函数说明如表10-2所示。表10-2CBrick类的成员函数说明函数名称函数说明CBrick();构造函数unsignedintgetColour()获得填充的颜色voidsetColour(unsignedintnewColour)设置填充颜色boolmove(intoffsetX,intoffsetY,unsignedint**binImage)实现砖块的移动,可向左、向右、向下移动一格boolrotate(unsignedint**binImage);实现砖块的旋转voidoperator>>(unsignedint**binImage);重载运算符>>,通过设置映射到游戏矩形的2维数组binImage设置砖块的颜色,这里假设binImage是一个合法的大小合适的2维数组10.3具体实现采用C++ 面向对象程序设计语言,对俄罗斯方块游戏的业务逻辑类进行实现。1.矩形框类的实现CBin类的类定义和函数实现分别在文件bin.h和bin.cpp中完成,函数代码如下://文件bin.h#ifndefBIN_H#defineBIN_HclassCBin{private:unsignedint**image;unsignedintwidth;unsignedintheight;public:CBin(unsignedintw,unsignedinth);~CBin();unsignedintgetWidth(){returnwidth;};unsignedintgetHeight(){returnheight;};voidgetImage(unsignedint**destImage);voidsetImage(unsignedint**srcImage);unsignedintremoveFullLines();};#endif//实现文件bin.cpp#include"bin.h"CBin::CBin(unsignedintw,unsignedinth){width=w;height=h;image=newunsignedint*[height];for(unsignedinti=0;i<height;i++){image[i]=newunsignedint[width];for(unsignedintj=0;j<width;j++)image[i][j]=0;}}CBin::~CBin(){for(unsignedinti=0;i<height;i++){deleteimage[i];}delete[]image;}voidCBin::getImage(unsignedint**destImage){for(unsignedinti=0;i<height;i++)for(unsignedintj=0;j<width;j++)destImage[i][j]=image[i][j];}voidCBin::setImage(unsignedint**srcImage){for(unsignedinti=0;i<height;i++)for(unsignedintj=0;j<width;j++)image[i][j]=srcImage[i][j];}unsignedintCBin::removeFullLines(){unsignedintflag,EmptyLine=0;unsignedinti,j,m;for(i=0;i<height;i++){flag=0;for(j=0;j<width;j++){if(image[i][j]==0)flag=1;}

//当一行被完全填满if(flag==0){for(j=0;j<width;j++){image[i][j]=0; //当前行清零,即删除}

//上面的砖块下落for(m=i;m>0;m--){for(j=0;j<width;j++){image[m][j]=image[m-1][j];}}for(j=0;j<width;j++){image[0][j]=0;}EmptyLine++; //记录被删除的行数i--; }}returnEmptyLine;}2.砖块类的实现#ifndefBRICK_H#defineBRICK_H#include"bin.h"staticintbrickX[7][4]={{0,1,2,3},{0,1,1,2},{2,1,1,0},{1,1,2,2},{0,0,1,2},{2,2,1,0},{0,1,1,2}};staticintbrickY[7][4]={{0,0,0,0},{0,0,1,1},{0,0,1,1},{0,1,0,1},{0,1,1,1},{0,1,1,1},{0,0,1,0}};CBrick类的说明如下(文件brick.h):classCBrick{protected:unsignedintcolour;unsignedintx[4];unsignedinty[4];public:CBrick();unsignedintgetColour(){returncolour;};voidsetColour(unsignedintnewColour){colour=newColour;};boolmove(intoffsetX,intoffsetY,unsignedint**binImage); //移动boolrotate(unsignedint**binImage); //旋转voidoperator>>(unsignedint**binImage); //输出图像};#endif#include"stdafx.h"#include"brick.h"#include<string.h>#include<malloc.h>CBrick::CBrick(){colour=(rand()%7)+1;for(inti=0;i<4;i++){x[i]=brickX[colour-1][i];y[i]=brickY[colour-1][i];}}CBrick的三个函数类外实现代码如下(brick.cpp):boolCBrick::move(intoffsetX,intoffsetY,unsignedint**binImage){inti;intX[4],Y[4];for(i=0;i<4;i++) //针对每一个小方格的移动{X[i]=x[i]+offsetX;Y[i]=y[i]+offsetY;if(X[i]<0||X[i]>=10||Y[i]<0||Y[i]>=20)//判断是否能够移动成功returnfalse;if(binImage[Y[i]][X[i]]!=0)returnfalse;}for(i=0;i<4;i++){x[i]=X[i];y[i]=Y[i];}returntrue;}boolCBrick::rotate(unsignedint**binImage){inti;intxt[4],yt[4];for(i=0;i<4;i++){

//进行顺时针90度坐标变换xt[i]=y[i]+x[1]-y[1];yt[i]=x[1]+y[1]-x[i];if(xt[i]<0||xt[i]>=10||yt[i]<0||yt[i]>=20)returnfalse;if(binImage[yt[i]][xt[i]]!=0)returnfalse;}for(i=0;i<4;i++){x[i]=xt[i];y[i]=yt[i];}returntrue;}voidCBrick::operator>>(unsignedint**binImage){for(inti=0;i<4;i++)binImage[y[i]][x[i]]=colour;}10.3具体实现3.可视化的设计在VisualStudio2010平台下完成可视化的设计。(1)用MFC应用程序向导新建一个单文档应用程序NewTetris。在新建对话框中进行设置,项目类型选择Singledocument,语言选择中文简体,去掉UseUnicodelibraries的勾选,项目风格选择MFCstandard,其他页面采用默认值,如图10-4所示。图10-4

MFC应用程序向导新建对话框(2)将bin.h、bin.cpp、brick.h、brick.cpp文件加入NewTetris工程。并在NewTetrisView.h文件前部添加:#include"bin.h"#include"brick.h"(3)游戏界面的可视化主要在视图类中实现。在视图类的类定义中添加如下成员变量:CBin*bin; //定义游戏矩形框指针CBrick*activeBrick; //定义指向当前下落砖块的指针intgameOver; //判断游戏是否结束intbrickInFlight; //判断砖块是否处于下落状态intbrickType; //砖块类别unsignedintinitOrientation; //初始状态intnotCollide; //冲突否

unsignedintnumLines; //消的行数unsignedint**outputImage;intdifficulty; //定义游戏难度(4)在视图类的构造函数中添加初始化代码:CNewTetrisView::CNewTetrisView(){bin=newCBin(10,20);activeBrick=NULL;gameOver=1;brickInFlight=1;brickType=0;initOrientation=0;notCollide=0;numLines=0;difficulty=500;outputImage=newunsignedint*[20];for(inti=0;i<20;i++){outputImage[i]=newunsignedint[10];}bin->getImage(outputImage); }(5)在析构函数中添加如下代码:CNewTetrisView::~CNewTetrisView(){

deletebin;}(6)在视图类定义(NewTetrisView.h)中添加公有的成员函数的声明:voidCNewTetrisView::DrawImage(CBin*bin,unsignedint**image,CDC*pDC){unsignedintwidth,i,j;unsignedintheight;width=bin->getWidth();height=bin->getHeight();intnSize=20;CRectrc;COLORREFBrickColor[8]={0xFFFFFF,0xFF0000,0x00FF00,0x0000FF,0x00FFFF,0xFFFF00,0x800000,0x800080}; for(i=0;i<height;i++){for(j=0;j<width;j++){rc=CRect(j*nSize,i*nSize,(j+1)*nSize,(i+1)*nSize);

//绘制面板if(image[i][j]!=0){pDC->FillRect(rc,&CBrush(BrickColor[image[i][j]]));}}}}在视图类的实现文件(NewTetrisView.cpp)中,添加DrawImage函数的实现代码:voidDrawImage(CBin*bin,unsignedint**image,CDC*pDC);(7)为工程添加如下菜单:顶级菜单ID_Game_Start开始(&S)难度(&D)属性选择pop-up,子菜单为:ID_DIFF_EASY 容易ID_DIFF_MID 中等ID_DIFF_SUP 高级(8)为ID_Game_Start、ID_DIFF_EASY、ID_DIFF_MID、ID_DIFF_SUP在视图类中添加消息响应函数。并加入如下代码:voidCNewTetrisView::OnGameStart(){gameOver=0;brickInFlight=0;numLines=0;for(unsignedinti=0;i<20;i++){for(unsignedintj=0;j<10;j++)outputImage[i][j]=0;}bin->setImage(outputImage);SetTimer(0,difficulty,NULL);}voidCNewTetrisView::OnDiffEasy(){difficulty=500;OnGameStart();}voidCNewTetrisView::OnDiffMid(){difficulty=350;OnGameStart();}voidCNewTetrisView::OnDiffSup(){difficulty=150;OnGameStart();}(9)为视图类添加WM_TIMER的消息响应函数,并添加如下代码:voidCNewTetrisView::OnTimer(UINTnIDEvent){unsignedintbinWidth,binHeight;unsignedinti=0;unsignedintj=0;CDC*pDC=GetDC();binWidth=bin->getWidth();binHeight=bin->getHeight();//startthegameif(!brickInFlight&&!gameOver){activeBrick=newCBrick;bin->getImage(outputImage);notCollide=activeBrick->move(binWidth/2,0,outputImage);if(notCollide){brickInFlight=1;activeBrick->operator>>(outputImage);Invalidate(FALSE);}else{

//程序结束gameOver=1;deleteactiveBrick;brickInFlight=0;}}if(brickInFlight&&!gameOver){bin->getImage(outputImage);notCollide=activeBrick->move(0,1,outputImage); //下落if(notCollide){activeBrick->operator>>(outputImage);}else{brickInFlight=0;//bin->getImage(outputImage);activeBrick->operator>>(outputImage);bin->setImage(outputImage);Invalidate(FALSE);numLines=numLines+bin->removeFullLines();bin->getImage(outputImage);}Invalidate(FALSE);}if(gameOver){KillTimer(0);if(MessageBox("输了吧,还玩么","提示",MB_YESNO)==IDYES)OnGameStart();else//exit(0);PostQuitMessage(0);

//这两种方法都可以退出程序}CView::OnTimer(nIDEvent);}(10)在视图类的OnDraw函数中添加如下代码:voidCNewTetrisView::OnDraw(CDC*pDC)//去掉参数pDC两边的注释符{CNewTerisDoc*pDoc=GetDocument();ASSERT_VALID(pDoc);pDC->Rectangle(0,0,200,400);charbuf[100];sprintf(buf,"分数:%d",numLines*10);pDC->TextOut(220,20,buf);DrawImage(bin,outputImage,pDC);}(11)为视图类添加WM_KEYDOWN的消息响应函数,并添加如下代码:voidCNewTetrisView::OnKeyDown(UINTnChar,UINTnRepCnt,UINTnFlags){bin->getImage(outputImage);if(nChar==VK_RIGHT&&!gameOver)activeBrick->move(1,0,outputImage); //向右elseif(nChar==VK_LEFT&&!gameOver)activeBrick->move(-1,0,outputImage);elseif(nChar==VK_UP&&!gameOver)activeBrick->rotate(outputImage);elseif(nChar==VK_DOWN&&!gameOver)while(activeBrick->move(0,1,outputImage));

//一键下落activeBrick->operator>>(outputImage);//输出图形//updatethedisplayif(!gameOver)Invalidate(FALSE);CView::OnKeyDown(nChar,nRepCnt,nFlags);}(12)编译、链接、运行。4.实现砖块的三维化前面的程序的砖块绘制出来是平面的,看起来不是很美观,我们添加一些函数,使得砖块看起来有三维的效果。在视图类定义(NewTetrisView.h)中添加两个公有的成员函数的声明:COLORREFGetLightColor(COLORREFm_crBody);COLORREFGetDarkColor(COLORREFm_crBody);在NewTetrisView.cpp文件前部添加:#defineCOLOR_CHANGE60在视图类的实现文件(NewTetrisView.cpp)中,添加这两个函数的实现代码:COLORREFCNewTetrisView::GetLightColor(COLORREFm_crBody){BYTEr=GetRValue(m_crBody);BYTEg=GetGValue(m_crBody);BYTEb=GetBValue(m_crBody);r=r+COLOR_CHANGE>255?255:r+COLOR_CHANGE;g=g+COLOR_CHANGE>255?255:g+COLOR_CHANGE;b=b+COLOR_CHANGE>255?255:b+COLOR_CHANGE;returnRGB(r,g,b);}COLORREFCNewTetrisView::GetDarkColor(COLORREFm_crBody){BYTEr=GetRValue(m_crBody);BYTEg=GetGValue(m_crBody);BYTEb=GetBValue(m_crBody);r=r-COLOR_CHANGE<0?0:r-COLOR_CHANGE;g=g-COLOR_CHANGE<0?0:g-COLOR_CHANGE;b=b-COLOR_CHANGE<0?0:b-COLOR_CHANGE;returnRGB(r,g,b);}修改视图类的DrawImage函数,添加如下黑体代码:voidCNewTetrisView::DrawImage(CBin*bin,unsignedint**image,CDC*pDC){//省略前面代码//绘制面板if(image[i][j]!=0){pDC->FillRect(rc,&CBrush(BrickColor[image[i][j]]));pDC->Draw3dRect(rc,GetLightColor(BrickColor[image[i][j]]),GetDarkColor(BrickColor[image[i][j]]));}

//省略后面代码}图10-5

编译、链接、运行结果编译、链接、运行结果如图10-5所示。5.使用双缓冲技术解决屏幕闪烁运行程序,会发现程序有些闪烁,这是因为程序受WM_TIMER消息触发,调用OnTimer函数,OnTimer函数中调用的Invalidate函数会触发对OnDraw函数的调用,从而不停地重绘窗口的结果。在VC++ 的文档/视图结构中,CView的OnDraw函数用于实现绝大部分图形绘制的工作。如果用户改变窗口尺寸,或者显示隐藏的区域,OnDraw函数都将被调用来重绘窗口。并且,当程序文档中的数据发生改变时,一般必须通过调用视图的Invalidate(或InvalidateRect)成员函数来通知Windows所发生的改变,对Invalidate的调用也会触发对OnDraw函数的调用。正因为OnDraw函数被频繁调用,所以在其执行时,每次都刷新填充一次客户视图区域,便会使屏幕不稳定,产生闪烁现象。采用双缓冲方式可以消除屏幕闪烁。普通绘图方式与双缓冲绘图方式的区别在于:普通绘图方式可以看作是在屏幕上直接绘制图形,双缓冲绘图方式是先在内存中创建的“虚拟屏幕”上绘制,然后将绘制完成的图形一次性“拷贝”到屏幕上。修改视图类的OnDraw函数:voidCNewTetrisView::OnDraw(CDC*pDC){CNewTetrisDoc*pDoc=GetDocument();ASSERT_VALID(pDoc);intm_nWidth,m_nHeight; CDCm_memDC;CBitmapm_memBmp;//1.用于映射屏幕的内存设备环境//获取游戏窗口的大小用于下面设置内存位图的尺寸CRectwindowRect;GetClientRect(&windowRect);m_nWid

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论