毕业设计-c--俄罗斯方块游戏.doc
课 程 设 计 报 告 课程名称:面向对象程序设计C+ 设计题目: 俄罗斯方块游戏 专 业:计算机科学与技术 姓 名: 学 号: 指导教师:李 晓 虹 2015 年 1 月 10 日目 录.21需求分析.2 1.1需求分析.2 1.1.1游戏需求.2 1.1.2游戏界面需求.2 1.1.3游戏形状(方块)需求.21.2 课程设计目的.31.3 课程设计要求.32系统总体设计.32.1 程序流程图.32.2 定义方块的数据结构.42.3 游戏设计分析.52.4 开发环境.73系统详细设计 .73.1系统主界面的框架 .73.2 正常流程的设计.8 3.2.1定时机制.8 3.2.2定时处理.83.3底部到达的判断与销行的实现.93.4中断操作流程的实现.93.5变形的实现.103.6游戏区域绘制的实现.104测 试.104.1 测试方案.124.2 测试结果.145 结果分析.206总 结.21参考文献.21附 录.21附录1源程序清单.211 需求分析 1.1需求分析 1.1.1 游戏需求 随机给出不同的形状(长条形、Z字形、反Z形、田字形、7字形、反7形、T字型)下落填充给定的区域,若填满一条便消掉,记分,当达到一定的分数时,过关,设置六关,每关方块下落的速度不同,若在游戏中各形状填满了给定区域,为输者。1.1.2游戏界面需求: 良好的用户界面,有关数显示和分数显示。让方块在一定的区域内运动和变形,该区域用一种颜色表明,既用一种颜色作为背景,最好设为黑色。还需用另一种颜色把黑色围起来,宽度适中,要实现美感。 1.1.3游戏形状(方块)需求: 良好的方块形状设计,绘制七种常见的基本图形(长条形、Z字形、反Z形、田字形、7字形、反7形、T字型),各个方块要能实现它的变形,可设为顺时针或逆 时针变形,一般为逆时针。1.2 课程设计目的a)巩固并加深学生对C+语言程序设计知识的理解;b)培养学生面向对象的程序设计思想,使学生认识面向过程和面向对象两种设计方法的区别;c)进一步掌握和应用VC+ 6.0集成开发环境;d)提高运用C+语言解决实际问题的能力;e)初步掌握开发小型实用软件的基本方法,能独立设计、实现具有实际功能的小系统;f)掌握书写程序设计开发文档的能力(书写课程设计实验报告)1.3 课程设计要求课程名称:俄罗斯方块用设计与实现俄罗斯方块游戏。要求包括系统的需求分析;系统总框图及每个模块的设计分析;MFC应用程序架构;框架的扩展;算法的设计与实现;游戏的内部实现;游戏区域绘图的实现;系统存在的问题及错误处理;列出所有定义的函数及说明;附上程序源代码。2 系统总体设计 2.1 程序流程图图2.1程序流程图图2.1.2程序调用图2.2 定义方块的数据结构对于方块在某一瞬间的位置标识,我们采用一个4×2的小数组标识出来,即用4个存储单位空间存储当前下坠物的每一子块的位置,也就是说,用4个存储单位空间存储当前下坠物的每一子块的位置来对整个下坠物件的位置进行标识,而每个存储空间的大小就是一个典的坐标值(x,y),而每个方块按照从左到右的方向 进行编号,并且在编号过程中对于同一列的图2.2方块编号ActiveStatus00和ActiveStatus01则是第0号方块的横坐标x和纵坐标y ;ActiveStatus20和ActiveStatus21则是第2号方块的横坐标x和纵坐标y2.3 游戏设计分析有前面的功能描述可知,我先虚拟出俄罗斯方块游戏的类对象,并抽象出核心的数据属性和操作方法等,然后再作细化,最后将整个虚拟类的外壳脱掉,再移植到视图类中去,其实现如下: CRectGameView : public CView /内部存取数据结构 int m_stateMapMAX_ROWMAX_COL; /初始化操作 GameInitnal(); /游戏的初始化 /用于判断数据相关状态的操作 IsLeftLimit(); /下坠物件是否可向左移动 IsRightLitmit(); / IsBottom(); /是否已经到达了底部 IsGameEnd(); /是否游戏已经结束 /方块物件下坠过程中的操作 RectChange(); /下坠物件变形 RectDown(); /下坠物件正常下落 RectArrow(); /下坠物件方向移动(左,右,下加速) /状态控制操作 GameStart(); /游戏开始 GamePause(); /游戏暂停 GameEnd(); /游戏结束 通过上面的代码可以看出,在虚拟类中抽象出了核心的内部数据和一些基本的操作函数。对于操作函数,可以把它们分为内部实现的基本核心操作(如判断操作)以及明显提供给外部使用的整体模块外部操作(如状态控制操作)。而内部的基本操作又可以分为判断操作和执行操作这样两种类型。2.4 开发环境该程序是在windows系统下的C+语言开发和应用VC+ 6.0集成开发环境。3 系统详细设计3.1系统主界面的框架 首先建立一个项目工程,名为skyblue_Rect,并在AppWizard的架构选择过程中选择单文档方式,其他保持默认选项。其项目的架构类视图信息如图所示:在构架类视图中是MFC基本架构组合:App(应用程序)类、Document(文档)类、View(视图)类、Frame(框架)类和用于提示关于作者的对话框CAboutDlg类,至于COptionDlg类是用作俄罗斯方块参数选择的对话框类对象。3.2 正常流程的设计3.2.1定时制机制 从分析游戏的特性可以知道,定时器的产生与生效应该在游戏开始的时候,而在游戏暂停或者游戏结束时则将已经设定的定时器失效/销亡(对于暂停的情况,使它销亡,当游戏从暂停状态又进入游戏状态时候,则重新创建一个定时器并激活它的运作),所以分别在游戏的开始函数、暂停函数已经结束函数中实现定时器的激活与去激活工作。这里,先在资源编辑器菜单资源里面添加三个菜单选项,分别是游戏的“开始”、“暂停”、和“结束”,然后利用ClassWizard直接在视图类对象Cskyblue_RectView中为它们添加空白的处理函数,具体如表所示:3.2.1菜单选项功能对应表3.2.2定时处理经过定时器的设置后,这里通过利用ClassWizard跳到定时器到时候的处理函数OnTimer()去实现,当固定时间片间隔到达后,先检测当前下坠物是否已经到达了底部,不是则进行RectDown()下坠物向下移动一个单位的操作,是则到底后产生一个新的“下一个下坠物”,并代替旧的,将原先旧的“下一个下坠物”用作当前激活状态下正在使用的下坠物,并对使用后的一些状态进行检测:是否马上到达底部,使则进行销行操作;是否在到达底部的同时到达游戏区域的顶部,从而判定游戏是否因违规而结束。图3.2.2装在方块3.3底部到达的判断与销行的实现 图3.3 处理方块到达图 将新的下坠物放置到游戏区域中去,这时可能出现马上到达底部的情况,因此需要对它进行判断,如果是到达底部,则进行销行处理,并且修改相应的数据状态。而判断是否已经到达了底部,可以通过当前下坠物件所对应的接触面的方块位置为被占用状态(MAP_STATE_NOT_EMPTY=1)来确定,利用数组InterFace记录17种下坠物的14种形态的接触面信息。统计分数:在消行处理里面有一个专门用来统计消行数的变量,然后根据变量的值决定分数的多少,程序统计分数是:消一行得100分,同时消2行得400分,销掉x行,则分数为:x*(x*100)。如果总分数达到过关条件就过关,改变游戏速度,游戏初始化,开启新的一关,然后再加载方块。没有达到过关分数或者没有满行,则加载下一个方块继续游戏。3.4 中断操作流程的实现(1) 处理键盘事件 关于按键命令消息的响应,可以通过对WM_KEYDOWN消息的处理函数进行截获并重写来实现,下面是对该处理函数OnKeyDown()的重写。 / 功能:处理用户的输入,方块的左,右移,加速及变形 void CSkyblue_RectView:OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) switch(nChar) case VK_LEFT: RectArrow(LEFT); break; case VK_RIGHT: RectArrow(RIGHT); break; case VK_UP: RectChange(); break; case VK_DOWN: RectArrow(DOWN); break; CView:OnKeyDown(nChar, nRepCnt, nFlags); 3.5变形的实现当按下向上键时,将会执行方块变化事件(change())。常见的方块有7种(长条形、Z字形、反Z形、田字形、7字形、反7形、T字型),所有图形都是用两个一维数组来统计它的横坐标和纵坐标,每个方块有4种不同的变化形状。 例计算变形后的小方块的坐标和显示的状态值/变形后位置在数组中的存放顺序仍需遵循先左后右,在同一列中先上后下专业综合训练 xx1=x1; xx2=x2; xx3=x3; xx4=x4; yy1=y1; yy2=y2; yy3=y3; yy4=y4; switch(m_currentRect) case1:xx1=x1+1;yy1=y1-1;xx3=x3-1;yy3=y3+1 xx4=x4-2 yy4=y4+2; m_lscurrentRect = 11; break; case11:xx1=x1-1;yy1=y1+1;xx3=x3+1;yy3=y3-1;xx4=x4+2;yy4=y4-2; m_lscurrentRect = 1; break; /省略部分为同类实现的变形后小方块坐标的计算代码 case73:xx2=x2+1;yy2=y2-1;xx3=x3+2;yy3=y3-2;xx4=x4-1;yy4=y4-1; m_lscurrentRect = 7; break; 3.6游戏区域绘图的实现首先将外部位图文件rect.bmp中的位图动态导入(映射)到内存位图里面,根据游戏区域中的二维数组GameStatusMAX_ROWMAX_COL中的内部数据将所有数据状态中为被占用状态MAP_STATE_NOT_EMPTY的小方块区域用指定的小方块图样类型来填充,然后将已经绘制好的游戏区域图像一次性的拷贝到与屏幕关联的设备环境中,从而达到屏幕的显示。4 测试4.1测试方案主页面:图4.1主页面测试方案一:点击开始按钮图4.2点击开始按钮测试方案二:点击暂停按钮:图4.3点击暂停按钮测试方案三:点击结束按钮图4.4点击结束按钮测试方案四:点击设置按钮:图4.5点击设置按钮4.2 测试结果测试结果:点击了开始按钮,玩家可以用下左右键控制方块的位置用上变化方块的图形:图4.6玩家对图形移动图4.7玩家对方快变形状图4.8玩家对图形的移动图4.9玩家游戏结束 5 结果分析这次的俄罗斯方块程序设计总的功能都很好的实现了,从方块的移动到变形再到背景的设计,还包括了背景音乐的插入,这一系列的过程中走让我明白了编写游戏时MFC的画图函数很重要,但同时算法也是一个很重要的部分,方块的变形和转换如果不多加思考和设计的话,将会出现严重的问题。但是唯一的不足还是方块移动的敏捷性以及时间还有待提高。 6 总结这周的课程设计就要结束了。从最开始的做题到现在的报告总结我完成一个过程。在这个过程里我领悟了很多。在最开始的做实验报告时感觉挺难的之前根本就没有写过关于游戏的实验报告,不过还好我以前没事的时候玩过这游戏,所以比较了解这个游戏各个部分。虽然在中间写的过程中还有很多不会的东西,但是通过查看书本和资料还有问同学和老师,基本上都解决了。其中有一部分的程序无法满足题目的要求在老师的帮助下最后得到了解决,通过这件事我感觉自己还有还有很多不足,仍然有一些有待提高的地方。我觉得课程设计的作用一方面是最基本的就是要完成这一科目,差不多也是对自己的一个阶段性的总结;还有就是在整个设计的过程中,让我们认真的独立思考,在和同学交流的过程中也增强了我们的语言组织能力和彼此之间的友谊。通过课程设计让我们不断的发现自己的不足从而去改善,这是一种学习的态度,不仅仅是在这次的课程设计中,在以后的无论生活还是学习方面都应该注意和努力改善。通过这次比较完整的一个程序的设计,我摆脱了单纯的理论知识学习状态,和实际设计的结合锻炼了我的综合运用所学的基础知识,解决实际问题的能力,同时也提高我查阅文献资料、对程序整体的把握等其他能力水平,而且通过对整体的掌控,对局部的取舍,以及对细节的斟酌处理,都使我的能力得到了锻炼,经验得到了丰富。这是我们都希望看到的也正是我们进行课程设计的目的所在。虽然设计内容繁多,过程繁琐但我的收获却更加丰富。各种组件的运用,各种算法的应用,各种控件的利用我都是随着设计的不断深入而不断熟悉并逐步掌握的。和老师以及同学的沟通交流更使我对程序整体的规划与设计有了新的认识也对自己提出了新的要求。提高是有限的但提高也是全面的,正是这一次设计让我积累了许多实际经验,也必然会让我在未来的工作学习中表现出更高的应变能力和理解力。参考文献1谭浩强.C+程序设计(第二版)M.北京:清华大学出版社,2012. 2谭浩强.C+程序设计题解与上机指导(第二版)M.北京:清华大学出版社,2012. 附录附录1 源程序清单 CSkyblue_RectView:CSkyblue_RectView() /第一次开始游戏 m_bFistPlay = TRUE; /缺省为不是游戏暂停状态 m_bGamePaush = FALSE; /缺省为不插放背景音乐 m_bMusic = FALSE; /缺省为画网格线 m_bDrawGrid = TRUE; /总分值清零 m_iPerformance = 0; /测试值:为12行,10列 m_iRow = 12; m_iCol = 10; /左上角X,Y坐标 m_iStartX = 10; m_iStartY = 10; /缺省级别为3级 m_iLevel = 2; /第一种样式 m_iBlockSytle = 0; /缺省方块大小为m_iLarge个象素 m_iLarge = 30; /缺省游戏是结束的 m_bGameEnd = TRUE; int i,j; /赋初值 for (i=0;i<100;i+) for (j=0;j<100;j+) GameStatusij=0; /各种形状方块的接触面数据,参见设计书的接触面表格, /5.判断游戏是否已结束: 碰了底,且第1行有小方块 if (m_isBottom) for (i=0;i<m_iCol;i+) if (GameStatus0i) KillTimer(1); AfxMessageBox("游戏已结束!"); for (j=0;j<m_iRow;j+) for (k=0;k<m_iCol;k+)GameStatusjk=0; Invalidate(FALSE); m_bGameEnd = TRUE; break; else /当前方块下降 RectDown(); CView:OnTimer(nIDEvent); / 函数:产生一个最大值不大于指定值的随机正整数(Random) / 参数:MaxNumber : 随机数的上限 / 返回值: 产生的随机数 int CSkyblue_RectView:Random(int MaxNumber) /布下随机种子 srand( (unsigned)time( NULL ) ); /产生随机数 int random = rand() % MaxNumber; /保证非0 if(random = 0 ) random+; return random; /内部函数:刷新当前的区域 void CSkyblue_RectView:InvalidateCurrent() int i; for (i=0;i<4;i+) CRect rect(m_iStartX+ActiveStatusi1*m_iLarge, m_iStartY+ActiveStatusi0*m_iLarge, m_iStartX+(ActiveStatusi1+1)*m_iLarge+5, m_iStartY+(ActiveStatusi0+1)*m_iLarge); /InvalidateRect(&rect); Invalidate(FALSE); / 内部函数:当前方块下降加速,左移,右移 void CSkyblue_RectView:RectArrow(int m_Type) /获取当前下坠物4个小方块的位置坐标 int x1,x2,x3,x4,y1,y2,y3,y4; x1 = ActiveStatus00; x2 = ActiveStatus10; x3 = ActiveStatus20; x4 = ActiveStatus30; y1 = ActiveStatus01; y2 = ActiveStatus11; y3 = ActiveStatus21; y4 = ActiveStatus31; /对不同的移动命令指示进行分类实现 switch(m_Type) case LEFT: /对每种不同的移动命令指示特性作相应的可移动分析 if ( (ActiveStatus01>0) && IsLeftLimit() && !m_isBottom) /清原来的方块 GameStatusx1y1=MAP_STATE_EMPTY; GameStatusx2y2=MAP_STATE_EMPTY; GameStatusx3y3=MAP_STATE_EMPTYGameStatusx4y4=MAP_STATE_EMPTY; /添加新的移动后数据状态 ActiveStatus01 -= 1; ActiveStatus11 -= 1; ActiveStatus21 -= 1; ActiveStatus31 -= 1; GameStatusx1y1-1=MAP_STATE_NOT_EMPTY; GameStatusx2y2-1=MAP_STATE_NOT_EMPTY; GameStatusx3y3-1=MAP_STATE_NOT_EMPTY; GameStatusx4y4-1=MAP_STATE_NOT_EMPTY; InvalidateCurrent(); break; case RIGHT: if ( (ActiveStatus31< m_iCol-1) && IsRightLitmit() && !m_isBottom) /清原来的方块 GameStatusx1y1=MAP_STATE_EMPTYGameStatusx2y2=MAP_STATE_EMPTY; GameStatusx3y3=MAP_STATE_EMPTY; GameStatusx4y4=MAP_STATE_EMPTY; /添加新的移动后数据状态 ActiveStatus01 += 1; ActiveStatus11 += 1; ActiveStatus21 += 1; ActiveStatus31 += 1; GameStatusx1y1+1=MAP_STATE_NOT_EMPTY; GameStatusx2y2+1=MAP_STATE_NOT_EMPTY; GameStatusx3y3+1=MAP_STATE_NOT_EMPTY; GameStatusx4y4+1=MAP_STATE_NOT_EMPTY; InvalidateCurrent(); break; case DOWN: RectDown(); break; / 内部函数:方块的变形 void CSkyblue_RectView:RectChange() /先预先变形,然后判断变形后的方块是否有空间,如有足够空间,则进行实际变形,否则不变 int xx1,xx2,xx3,xx4,yy1,yy2,yy3,yy4; int m_lscurrentRect; CString lsStr; int x1,x2,x3,x4,y1,y2,y3,y4; x1 = ActiveStatus00; x2 = ActiveStatus10; x3 = ActiveStatus20; x4 = ActiveStatus30; y1 = ActiveStatus01; y2 = ActiveStatus11; y3 = ActiveStatus21; y4 = ActiveStatus31; /变形后位置在数组中的存放顺序仍需遵循先左后右,在同一列中先上后下 xx1=x1; xx2=x2; xx3=x3; xx4=x4; yy1=y1; yy2=y2; yy3=y3; yy4=y4; switch(m_currentRect) case 1: xx1=x1+1; yy1=y1-1; xx3=x3-1; yy3=y3+1; xx4=x4-2; yy4=y4+2; m_lscurrentRect = 11; break; case 11: xx1=x1-1; yy1=y1+1; xx3=x3+1; yy3=y3-1; xx4=x4+2; yy4=y4-2; m_lscurrentRect = 1; break; /省略部分为同类实现的变形后小方块坐标计算代码 case 73: xx2=x2+1; yy2=y2-1; xx3=x3+2; yy3=y3-2; xx4=x4-1; yy4=y4-1; m_lscurrentRect = 7; break; /改变形状代码 m_currentRect = m_lscurrentRect; else /恢复原来状态 GameStatusx1y1 = MAP_STATE_NOT_EMPTY; GameStatusx2y2 = MAP_STATE_NOT_EMPTY; GameStatusx3y3 = MAP_STATE_NOT_EMPTY; GameStatusx4y4 = MAP_STATE_NOT_EMPTY; /判断是否已到底 IsBottom(); /绘图设备环境的初始化 void CSkyblue_RectView:DcEnvInitial(void) if(m_bFistPlay) m_bFistPlay = FALSE; /黑色的黑笔 m_pBlackPen = new CPen(PS_SOLID,1,BLACK); /画刷 m_pGrayBrush = new CBrush(RGB(66,66,66); m_pBlackBrush = new CBrush(BLACK); void CSkyblue_RectView:DCEnvClear(void) /设备环境 m_memDC.DeleteDC(); m_memRectDC.DeleteDC(); /位图资源 DeleteObject(m_memBmp); DeleteObject(m_hMemRectBmp); delete(m_pBlackPen); delete(m_pGrayBrush); delete(m_pBlackBrush); void CSkyblue_RectView:DrawGame(CDC *pDC) int i,j; /选用黑色画刷,绘制整个游戏所在窗口的背景 pDC -> SelectObject(m_pBlackBrush); CRect rect; GetClientRect(&rect); pDC -> Rectangle(rect); /选用灰色画刷,绘制游戏区域的背景 pDC -> SelectObject(m_pGrayBrush); pDC -> Rectangle(m_iStartY ,m_iStartX, m_iStartY + 301, m_iStartX + 360); pDC->SelectObject(m_pBlackPen); /画网格线 if (m_bDrawGrid) /画