资源描述
摘要
本文主要介绍了“俄罗斯方块”游戏的设计与开发过程。该游戏是能够让用户通过键盘中的方向键控制下坠物件的旋转和移动,并且在下坠物件到达一定的空间后判断是否销行,当达到一定的分数后进入下一关,或者下坠物件如果不能在下坠,则游戏结束。游戏主要是基于MFC类库中对话框类的框架上面,将所有功能都封装到了这个对话框上面,使得结构可以很清晰。游戏所用到的数据结构是动态的二维数组,所有规则算法的实施都是基于这个动态数组而展开的。在熟悉基本的消息机制并调用GDI中的一些函数,完成键盘的交互功能和几个快捷键的功能,丰富了游戏内容。并结合设备环境的重要属性位图的一些基本知识实现了游戏界面的绘制。
关键词: MFC,动态数组,VC游戏开发
43
ABSTRACT
This paper is on "Tetris" game design and development. This game is mainly through the keyboard that allows users to key in controlling the rotation and falling objects moving and falling objects in space after landing a Consumers will determine whether, when reached a certain enter scores after the next Commissioner, or if the falling object Not falling, the end of the game. The game is mainly based on the MFC class library in the framework of the dialog above category, will have all the features of the package to this dialog above, the structure can be made very clear. Games used by the data structure is a dynamic two-dimensional array, all the rules-based algorithm for the implementation of this dynamic arrays are initiated. In the news familiar with the basic mechanism called GDI and a number of functions, to complete the interactive features keyboard shortcuts, and several features, enriching the content of the game. With equipment and the importance of environmental attributes of the plan is to achieve some basic knowledge of the game interface mapping.
Keywords MFC, Dynamic arrays, VC game development
目录
摘要 I
ABSTRACT II
第1章 引言 1
1.1 选题的目的 1
1.2 选题的背景和意义 1
1.3 研究的内容 2
1.3.1 主要内容 2
1.3.2 研究的范围 3
1.3.3 待解决的问题 3
第2章 游戏开发的基本介绍 4
2.1 开发工具与资料来源 4
2.2 MFC的基本介绍 4
2.3 GDI原理基础 6
2.3.1设备环境 6
2.3.2常用GDI绘图操作 8
2.4 本章小结 8
第3章“俄罗斯方块”游戏的开发与设计 9
3.1游戏功能描述 9
3.2框架的搭建 10
3.3游戏内部的实现 10
3.3.1算法设计 11
3.3.3正常流程的设计 20
3.3.4正常流程的实现(定时处理) 21
3.3.5中断操作流程的实现 25
3.4游戏区域绘图的实现 26
3.4.1位图资源的准备 26
3.4.2绘制机制设计 26
3.4.3绘制环境资源的初始化和释放 27
3.4.4游戏区域的绘制 27
3.5功能的完善 28
3.5.1 游戏背景音乐的实现 28
3.5.2工具栏快捷键的实现 29
3.6本章小节 29
第4章 结论 30
致谢 31
参考文献 32
附录1部分源代码 33
第1章 引言
1.1 选题的目的
主要是能通过设计“俄罗斯方块”这个小游戏,使自己能够更多的了解以及学习MFC的一些知识,并从中提高自己C++程序设计的实际能力,当然,这样也能让自己四年所学的知识运用到实际应用里去。
1.2 选题的背景和意义
从60年代早期开始,一小部分程序员使用最原始的大型计算机来开发游戏,在MIT的实验室里开发了SpaceWar和其它早期的游戏[1]。70年代的电脑游戏几乎是一片空白,不过,我们无法忽略这个时期,因为在这十年里出现了无数电子游戏业的先驱,第一个电子游戏、第一台业务用游戏机(就是街机)和第一台家用游戏机都是在这段时间诞生的。
真正的电子游戏专用机产生在70年代初。1971年,一个还在MIT(麻省理工学院)学习的叫Nolan Bushnell的家伙设计了世界上第一个业务用游戏机,这个街机游戏的名字叫《电脑空间》(Computer Space)。《电脑空间》的主题是两个玩家各自控制一艘围绕着具有强大引力的星球的太空战舰向对方发射导弹进行攻击。两艘战舰在战斗的同时还必须注意克服引力,无论是被对方的导弹击中还是没有成功摆脱引力,飞船都会坠毁。这台业务机用一台黑白电视机作为显示屏,用一个控制柄作为操纵器,摆在一家弹子房里。不过很可惜,这台祖母业务机遭到了惨痛失败,失败的原因是当时的玩家认为这个游戏太过复杂。
接着用电脑编制电子游戏就开始在程序员之间流行起来。当时的电子游戏大多数还都是编程高手们做出来娱己的绝对的贵族游戏,这是因为当时接触计算机的还只是科技精英,一般的群众是接触不到这种东西的。当然,这些所谓贵族的电子游戏是非常简陋的——简陋到我们现在无法相信。我感觉,一个人想要理解当时的电子游戏,就非得有过人的抽象思维能力不可。在现在的玩家眼里,一个白色的16×16点素的色斑可能代表着马赛克[2]。
后来由于网络的引入,越来越多的网络游戏开始慢慢的侵占了整个电脑游戏市场。越来越多的专业游戏开发商和发行商介入网络游戏,一个规模庞大、分工明确的产业生态环境最终形成。人们开始认真思考网络游戏的设计方法和经营方法,希望归纳出一套系统的理论基础,这是长久以来所一直缺乏的。
1995年,Microsoft推出了Windows 95和windows Game SDK,使得大量的游戏开发转向Windows平台。Internet和Word Wide Web流行,大量的用户上网。大范围的多人游戏已经出现。多媒体、3D和虚拟现实等取代WWW而成为热点,主要的新技术和产品包括Java 1 ShockWare、JavaScript、Netscape 2等。到现在为止,国外的3D游戏技术和质量已经不断的完善,最近风靡的一款游戏《魔兽世界》就非常具有代表现。
游戏自发明到现在,已经极大地改变了人类的生活环境和生活。它已不仅是一个产业,更发展成为一门专门的艺术,就像电影一样。我预言,在未来,游戏将更加深入地渗透到人们的生活中去,成为人们生活中不可缺少的一部分。历史不可逆转,无论你对已逝去的岁月怀有多么深的回忆和眷恋,都无法留在过去的年代,时间总在飞逝,我们必须迎接未来。
选择该课题的意义就在通过对这个小游戏的设计与实现,对游戏的历史背景,开发现状,以及未来的发展趋势都有了很大的认识,通过设计此小游戏,对VC(MFC)更加了解,也让自己学到了更多的知识,另外也将学校所学的知识应用到实践当中来[3]。
1.3 研究的内容
1.3.1 主要内容
1 游戏一些基本算法的设计。
2 游戏基本规则的实现。
3 具体流程的设计,让玩家更容易操作。
4 游戏界面的绘制,主要是位图的载入与输出到屏幕的问题。
5 快捷键功能的实现,其中包括全部重新开始、暂停、游戏设置、游戏难度和帮助等主要功能。
1.3.2 研究的范围
MFC类库中的许多类,如App(应用程序)类、Document(文档)类、View(视图)类、Frame(框架)类和用语提示关于作者的对话框CAboutDlg类。用到比较多的是其中的GDI库(Graphics Device Interface,图形设备接口)。调用了一些绘制函数,并运用设备环境的一个重要属性位图进行界面的绘制。动态二维数组的应用和基本的Windows消息机制的熟悉,几个关键算法的设计与实现。
1.3.3 待解决的问题
开始制作游戏时,主要有这么几个问题: 如何搭建一个基本的框架;如何设计一个比较适中的界面;怎么布置游戏里的一些功能按键;如何处理游戏中随即生成的方块的种类;对游戏涉及到的几个算法应该做如何修改处理;如何判断游戏中的方块是否不能在移动;如何进行几个基本快捷键功能的实现;如何进行游戏区域的绘制;以及如何进行位图的传输等问题。
第2章 游戏开发的基本介绍
2.1 开发工具与资料来源
运用VC6.0开发,Visual C++是Windows环境下非常优秀的C++编译器之一,它是Mircrosoft公司开发的Visual Studio系列产品的一部分,因为VisualC++6.0对计算机的硬件环境要求比较低,而且能够胜任几乎所有的Windows应用程序的开发工作,对计算机的软硬件环境要求比较低,所以这里运用Visual C++编写并编译,因为一直以来都使用这个工具,故使用起来也是比较熟悉。
还有一个重要的文档就是文档MSDN。MSDN是VC开发必不可少的东西,MSDN是微软程序员为帮助开发人员而提供的一系列在线或者离线的服务。MSDN中包含了编程信息,技术论文,文档,程序代码等。在安装Visual C++ 6.0时,将得到一个包括API文件的联机帮助系统。可以通过订阅MSDN或使用Microsoft的基于Web的联机帮助系统更新该文件。连接到 Library Online。 在Visual C++6.0中,从Help菜单项选择Contents项,调用MSDN窗口,API文档按树形结构组织,查找标有Platform SDK的部分,大多所需的文档都来自于该部分[4]。
2.2 MFC的基本介绍
MFC是放置Windows API的面向对象的包装的C++类库。MFC 6.0版本封装了大约200个类,其中的一些您可以直接使用,而另一些则主要作为您自己的类的基础类。一些MFC类极其简单,例如CPoint类,它代表一个点。有些类较复杂,例如CWnd类,它封装了窗口的功能。在MFC程序中,您不经常直接调用Windows API,而是从MFC类创建对象并调用属于这些对象的成员函数。在类库定义的成员函数有几百个,其中许多是Windows API的简单封装,甚至与相应的API函数具有一样的名字。这就使的使用MFC成员函数稍微显得轻松了一些。
MFC也是一个应用程序的框架结构。MFC不仅仅是一个类的集合,它还帮助定义了应用程序的结构并为应用程序处理许多杂务。在这里,需要知道的是两个基本的类。
1 、App(应用程序)类
MFC应用程序的核心就是基于CWinApp类的应用程序对象。CWinApp提供了消息循环来检索消息调度给应用程序的窗口。它还包括可被覆盖的、用来自定义应用程序行为的主要虚函数。一旦包含头文件Afxwin.h,就可以将CWinApp以及其他MFC 类引入应用程序中。一个MFC应用程序可以有且仅有一个应用程序的对象,此对象必须声明为在全局范围内有效,以便它在程序开始时即在内存中被实例化。在创建的过程中将使用到实体初始化函数InitInstance(),该函数基本负责其他物件的创建以及显示功能。InitInstance的目的是为应用程序提供一个自身初始化的机会。由InitInstance返回的值决定了框架结构接下来要执行的内容。从InitInstance返回FALSE将关闭应用程序。如果初始化正常,InitInstance将返回TRUE以便允许程序继续进行。InitInstance是用来执行每次开始时都需要进行初始化工作的最好地方。至少,这意味着创建在屏幕上表现应用程序的窗口。
2、 Frame(框架)类
Frame类物件主要用于外部窗口的框定、对其他物件的容纳,包括菜单、工具栏、视图和子窗体等。
在类视图中可以看到,在CMainFrame窗体类有一个重要的成员函数OnCreate(),该函数的主要作用是在窗体类创建过程中,对你容纳的物件(工具栏、状态栏、菜单栏等)进行创建和初始化,例如,从类试图中看到的工具栏m_wndToolBar成员物件的创建以及停泊设置就是通过OnCreate()函数实现的。窗的容纳特性对子窗体类同样有效,例如,可以将一个窗口分成多个子窗口,然后该窗口以这子窗口母体的形式去承建这些子物件。
3、Doc-View(文档-视图)
我们将Doc和View类物件放在一起对它们进行简单的描述。Doc物件主要用于项目中核心数据运算以及数据的序列化存取等工作,而View物件则是对这些核心数据按照特定的方式进行图像展现。由于项目中的核心数据以及结构比较复杂,所以有必要对这些信息进行集中管理,这样有利于分清系统的骨架,便于日后的体系维护以及升级管理等[5]。
在一般应用程序中,我们经常会看到这样的情况,对于同一部分内部数据,外面会用不同的方式去做绘图的展现组数据。在文档-视图模式下,对表现同一内部数据的所有视图,在文档类物件中会有一个管理链将它们串起来,以便对这些关联的表达同一物件的视图进行同意管理。具体的实现中,我们通过成员函数GetFirstViewPosition()来获取该管理链的第一个关联的视图控制权,而对于其他视图的控制,则通过GetNextView()函数来逐一对比罗列。关于这些与文档的视图对数据的绘制更新,可以通过UpdateAllView()函数来对它们进行通知。在View一方,对于数据的显示绘制,一般在OnDraw()绘制函数中实现。对于数据的访问权,则通过调用视图的成员函数GetDocument()来获取容纳数据的文档控制权。
2.3 GDI原理基础
GDI主要负责在显示屏幕和打印设备等方面输出图像信息,是一组通过C++类实现的应用程序编程接口。作为一个设备接口,它可以使得开发人员在将信息输出于屏幕或打印机之时无需考虑具体的目标输出设备的硬件特性,只需调用GDI库的一些方法进行操作即可,而具体的绘图工作则由特定的设备驱动程序开完成,从而使开发人员能轻松地在不同的硬件中做图像绘制输出。
2.3.1设备环境
设备环境是一个由图形物件群集合而成的数据结构,这些绘图物件有各自的特征属性和图像绘制模式。这个图形物件群包含众多绘图物件,如可以画线的铅笔,可以用做填充和绘制的画刷,可以进行拷贝操作的位图或者屏幕的一部分,用作定义有效颜色的调色板,可以剪裁或者进行其他类似操作的区域等。对于某一个设备环境,可以从图形物件群,图形模式,设备环境类型这3个主要特征属性来对其进行理解。
1、 图形物件群
Pen,brush,bitmap,palette,font,region,path等是组成关于设备的众多图形物件。当一个应用程序创建了一个DC,操作系统就会自动存储一系列默认的图形物件(除位图和路径外)。应用程序通过调用GetCurrentObject()或者GetObject()函数来获取这些默认物件属性,而且应用程序可以通过先创建一个新的物件,然后再将新物件选进DC中来改变当前相关物件的属性配置。这些物件的选顶则SelectObject()函数作接口来实现。
2、图形模式
Windows支持5种图形模式,包括background, drawing, mapping, polygon-fill stretching。这5种图形模式允许应用程序去定义绘图的颜色、输出到哪里才显示、输出的卷动方式等。而这些将储存在一个DC的图形模式。
3、 设备环境类型
这里主要有两种,一种是视频内存设备环境。当用户需要在视频区域进行绘制操作的时候,必须先通过某些函数接口来对与该视频相关的DC进行获取(如通过BeginPaint()函数,GetDC函数或者GetDCEx ()函数)。一般只有在对某物件的子区域进行绘制的时候才对这些DC进行获取,并且当图象绘制完成后,必须通过调用EndPaint()或者ReleaseDC()函数来释放已经获取的DC。
另外一种变是内存设备环境。需要对图象做一系列临时的绘制处理,然后再根据其特性对其他问题进行处理,这时候并不需要把图象绘制到一个真实的设备环境上,可以通过某种特殊的设备环境来实现,而这种特殊的设备环境被称为内存设备环境。设备环境可以被看作虚拟的设备环境,它是在内存中分配出特定空间来存储一般设备环境所包含的图形物件的一个虚拟环境。并且在内存设备环境进行绘制操作前,必须选定该虚拟环境的绘制目标,而这一目标物件,通常是一个已经分配好预定大小空间的位图,称之为内存位图。对于内存设备环境的创建,可以通过调用CreateCompatibleDC()函数来实现,在创建完毕后,还需要将这一设备通过调用SelectObject()函数来宣布它与另一目标位图物件的关联,以便以后的绘制操作可以关联到位图上,而关于具有特定尺寸大小的该内存位图的创建,可以通过CreateBitmap()函数来实现[7]。
2.3.2常用GDI绘图操作
绘图操作主要包括绘图物件的属性设定和绘图操作的实现两部分。
对于一个图形物件属性的设定,可以先创建一个具有用户预先设想属性的新图形物件,然后调用SelectObject()函数选中该图形物件进行属性设置。
对于图像的绘制操作,一般调用Windows提供的相关API来实现。
2.4 本章小结
本章主要介绍了游戏开发的工具与技术文档,MFC的基本介绍及其中应用程序类和对话框类的介绍,并详细的介绍了GDI中的设备环境这个基本概念。VC6.0开发工具因其使用简单而普遍受欢迎,并且在开发过程中如果碰到困难也可以通过查阅MSDN来获得帮助。在游戏中,因为其主要框架是建立在对话框上的,就务必要介绍应用程序类和对话框类。这两个类组成了整个游戏框架,便从不同的属性如图形物件,图形模式,设备环境类型来对其进行介绍,来达到对设备环境的理解,为下面的界面绘制做准备。
第3章“俄罗斯方块”游戏的开发与设计
3.1游戏功能描述
对于俄罗斯方块,其功能描述如下:按下开始按钮,游戏开始,游戏区域上方不断地出现预定义形状的下坠物件,下坠物件可以通过旋转改变其显示形态,并且不断地往下坠,直到它接触到游戏区域底部或者其他之前已经累叠起的下坠物件。当一个下坠物件到达底部后,其位置则确定下来并占有该空间的位置区域。当游戏区域的某一行被下坠物件完全填充,则消除该行的所有下坠物,垒在其上的物件将掉下代替该行空间。游戏的结果是以下坠物件的顶部到达游戏区域顶部作为判断依据,表示装满溢出。并且在某一瞬间,下一个下坠物件的形态在游戏区域的右上方有预先提示,同时每抵消一行空间积分自增100,如图3-1所示。
图3-1 游戏操作界面
游戏的操作是通过键盘中的方向键控制下坠物件的旋转和移动的,包括各种显示状态的旋转变幻,方块的左右移动和玩家确认下坠物件安放位置后的快速下坠。
3.2框架的搭建
整个游戏是基于MFC做的。首先建立一个项目工程,名为skyblue_Rect,并在AppWizard的架构选择过程中选择单文档方式,其他可以保持默认选项。
根据这个游戏的特性,其所有功能都可以放在视图类中实现。在使用MFC机制搭建好的框架中,每个类对象都有它独特的功能特性,如Frame类对象用作窗口框架的承载,Document类对象用作关于数据的出席和序列化村区功能的承载,而View类对象是用作视图显示方面的功能。
前面已经将整个游戏的大致框架建立好,并且打算在视图类上面进行功能拓展,也就是说继承该视图类,生成具有俄罗斯方块操作属性的视图类对象。对游戏的核心实现一般都是直接设计出一个原始的基础功能类对象,然后使用这些核心操作,在屏幕显示和用户交互的时候才以分配成员变量的形式去实现。与以往不同的是,俄罗斯方块这个游戏是把实现的核心直接嵌入到关系密切的一个类对象里面,再对它的功能模块进行拓展。
文档类对象物件有这样的优点:可以对多个与数据关联的视图进行统一的管理;多个视图中操作的数据试题都为文档本身,使得表达同一内部数据的视图可以通过文档关联;文档可以做到高效率的序列化存取操作。并且俄罗斯方块这个项目为单视图(没有必要同时存在两个游戏视图),该游戏还不需任何磁盘文件的存取,这里将俄罗斯方块的功能特性直接拓展在视图类对象里面。
3.3游戏内部的实现
游戏的内部实现,主要包括游戏的基本算法设计,以及界面交互与内部处理流程之间的整合与规划等。
3.3.1算法设计
1.类结构的方式选定
在对一个类对象进行继承并且做功能拓展的时候,一般会有下面这样两种操作方法和属性出现:
(1) 操作方法和属性是该扩展功能所特有的,不与基类(父类)
的任何操作和属性有直接的关联,它们只被新增的方法所使用。
(2) 操作方法和属性与基类的某些操作方法或属性相关联,它们之间存在访问的关系。
对于第1种情况,一般是拓展功能的算法和实现核心功能,它们偏向于该特性功能的运算实现,数据的处理等,而第2种情况一般是相对于已经成型的一些功能模块的大操作方法,它们包含了该拓展功能的内部实现的封装,供其他模块的操作方法直接访问。
2.前期游戏分析
在抽象出方法和属性前,要分析下整个游戏的具体实现。首先,游戏有开始、暂停、结束等操作接口,而在游戏过程中,随着用户的按键交互,会出现方法的形态变化、方块快速下坠、安放该下坠物件、销毁填满的行、产生下一将要出现的方块等功能。整个游戏的整体状况,如图3-2所示。
图3-2 游戏行列分配
3.核心数据的抽象与设计
从上图可以看到,游戏区域可以看作是有许多个等面积的小方块构成的大面积区域。而这些区域的状态只有2种,被下坠物占据和空闲(没有被下坠物占据),因此,对于整个游戏区域的数据,用一块空间来标识即可。其状态只有两种;被占据1和空闲0。这块空间的分配,可以采用动态分配的方式或者静态分配的方式去实现,至于数据结构的选择,可以是链表、队列或则数据组等,为了方便理解,这里选择静态分配并划分一个足够大的二维数组的方式来实现。
整个游戏区域的地图设计好后,再来看看具体的下坠物,通过分析它们的特性,还可以抽象出一些物件来。在每个下坠物在下坠的过程中,总是占有地图中的某一区域,那么怎样表达这些下坠物件的形态和它们占有的位置关系呢?先看看它们的基本类型,如图3-3。
图3-3七种基本图形
从图3-3可以看到,这些下坠物的形状一共有7种。这些众多下坠物间存在一个基本的共性,就是它们占有的位置空间是一样的,都是四个小方块区域。对于它们在某一瞬间的位置标识,我们可以采用一个4*2的小数组标识出来。如图3-4。
图3-4二维数组标识
也就是说,用4个存储单位空间存储当前下坠物的每一个子块的位置来对整个下坠物件的位置进行标识,而每个存储空间的大小就是一个点的坐标值(x,y)。于是定义ActiveStatus[][]来存储每个下坠物的四个子防快的坐标位置,其实在游戏过程中,变动的下坠物件除了当前拥护正在操作的下坠物件外,项目程序还要显示出下一个下坠物件的形状,于是定义一个数组NextStatus[][]来存储下坠物的形态。对于四个小方块还需要定义如下规则:编号从下坠物的左边起往右编排,而在同一列的方块中,则从上往下的顺序开始编号。它们的对应关系如图3-5所示。
图3-5基本图象对应的数组
如图3-5所示的两个不同的下坠物,每个防快按照从左到右的方式进行编号,并且在编号过程中对于同一列的方块实行从上到下进行编号,而该方块所在游戏区域的位置坐标则存储在下标与编号所对应的ActiveStatus[][]二维数组的每个单位里面,如:
ActiveStatus[0][0]和ActiveStatus[0][1]则表示第0号方块的横坐标x和纵坐标y。
ActiveStatus[2][0]和ActiveStatus[2][1]则表示第2号方块的横坐标x和纵坐标y。
对于同一个物件的不同形态,则使用这样的方法来解决:用一个宏去标识,第一位的数值变化范围为1~7,用语标识下坠物的种类(7种),第二位标识该种类下坠物的不同形态,不同的下坠物,如“田”字形的下坠物就一种形态,所以没第二位。其他的不同形态则从1~4(最多有四种形态)按顺序标识即可,如第二种下坠物的第三种形态,其宏标识为23,如图3-6所示。
图3-6形态宏标识
4.操作方法的抽象与设计
当完成整个游戏的基本核心数据的表达并进行抽象与设计后,接下来,就可以根据游戏的特性对核心的基本操作方法进行抽象,进而实现这些功能。
首先是游戏开始,应该有个事件触发游戏的开始,譬如菜单栏选项等,相应地,也可以类推出游戏暂停与游戏结束等操作功能。
游戏开始后,产生一个下坠物件,并且在右上角生成下坠物件的基本形态,因此有随机物件产生这个操作。在游戏过程中,方块会自动随着时间的推移而向下移动,因此有向下移动的操作。在游戏过程中,用户可以通过按键对下坠物的功能进行操作,如变形操作、加速下降操作、向左移动操作、向右移动操作等。上面说到的几点大多是从与用户交互的角度去观察分析的,而从游戏的规则方面,也可以作一下分析,当下坠物响应用户的指示向左或向右移动的时候,需要判断该下坠物件是否已经到达了游戏区域的边界而无法向左或向右移动;当下坠物件的形态改变的时候,需要计算出应该改变成何种形态;当下坠物向下移动的时候,需要判断是否已经到达了底部等。
根据前面的方法,先虚拟出该游戏的类对象,并抽象出核心的数据属性和操作方法等,然后再作细化,最后将整个虚拟类的外壳脱掉,再移植到视图类中去。对于操作函数。可以把它们分成内部实现的基本核心操作(如判断操作)以及明显提供给外部使用的整体模块外部操作(如状态控制操作)。而内部的基本操作又可以分为判断操作和执行操作这样两种类型。可以从下表中理解。
表3-1操作函数及功能描述
编号
属性和操作
功 能 描 述
1
外部操作
1.1
状态控制操作组
1.1.1
GameStart()
游戏进入开始状态,进行相关数据的初始化以及关联操作
1.1.2
GamePause()
游戏进入暂停状态,进行数据的保持与其他关联的操作
1.1.3
GameEnd()
游戏进入结束状态,进行数据的清楚以及资源的销毁和释放操作
2
内部基本核心操作
2.1
判断操作组
2.1.1
IsLeftLimit()
向左偏移前,判断该下坠物件是否已经到达了游戏区域的最左边界(不可以在左移)
2.1.2
IsRightLimit()
向右偏移前,判断该下坠物件是否已经到达了游戏区域的最左边界(不可以在右移)
2.1.3
IsBottom()
向下偏移加速或者下坠物件自动向下偏移前,判断是否已经到达了底部(这里对底部定义是游戏区域底部或下面有其他下坠物)
2.1.4
IsGameEng()
判断下坠物是否到达了游戏区域底部或下面有其他下坠物
2.2
执行操作组
2.2.1
RectChange()
下坠物件的状态变形,方法为在原来的状态下逆时针旋转90度
2.2.2
RectDown()
该下坠物的4个小方块所有坐标位置向下偏移一个单位
2.2.3
RectArrow()
直接响应用户用语按键,向左或向右或向下偏移一个单位
3
其他操作
3.1.1
GameInitnal()
游戏的初始化操作
整个游戏的概要流程如图3-7所示。
2.生成新的“下一个下坠物”
1.硝行操作
6.游戏结束处理
5.硝行操作
开始
到达底部
到达底部
3.将新生的下坠物代替旧的“下一个下坠物”
4.将旧的“下一个下坠物”用作当前下坠物
结束
7.下降一个单位
到达顶部,游戏结束
否
是
否
是
图3-7游戏流程图
在正常情况下,游戏的整个流程是由定时器的事件所驱动的,每到定时器预定的时间后就要执行上面的流程,知道游戏结束/暂停,将定时器停止。
首先检查当前的下坠物是否已经到达了底部,如果不是的话就将下坠物到了该定时器间隔的时间后整体往下移动一个单位,否则,进行到达底部后的操作。对于到达底部后的操作,可以分成以下几个步骤来处理。
(1) 先对进行到底部后的所有可以抵消的进行销行处理。
(2) 生成一个新的“下一个下坠物”,这个下坠物的形态需要随机的生成。
(3) 在将旧的“下一个下坠物”置换成新的下一个下坠物,并在屏幕右上方显示。
(4) 将当前的“下坠物”换成以前产生的旧的“下一个下坠物”,并且马上使用,并且马上使用。由于在这个过程中已经重新使用了一个新的下坠物件,在刚使用时并且还没有下降前,应该判断它是否已经不可以下降到底部(也就是说,在方块已经堆砌到接近游戏区域顶部的时候,下坠物件一旦被使用,就已经到达了底部),如果是刚好到达底部则实行销行检测和操作,并且判断顶部(第一行)的某些区域是否已经被占用了,是则表示游戏已经触发结束的标志时间,应该进入游戏结束状态。
(5) 在前面判断为到达底部后,进行销行检测与操作。
(6) 在下坠物到达底部的同时,判断是否已经到达了游戏的顶部,是则表示游戏结束,进行游戏结束的资源释放与数据环境清理工作(包括定时器的暂停,循环流程退出)。
3.3.2中断操作流程的设计
上面的分析是机遇正常情况下,项目程序在无人操作的情况下由定时器驱动的游戏流程。它的工作不受外界的任何影响,与外界的控制基本不存在直接的关联。
当用户进行一定的操作交互的时候,运行程序可以根据用户的操作指示进行当前下坠物的控制。如图3-8所示。
时间轴
定时器到达的固定时间片
正常流程操作
用户按键中断操作
OnTime()
{
//正常流程操作
……
}
OnKeyDown()
{
//按键“中断”操作
……
}
图3-8中断操作流程
从上图可看到,整个游戏开始后的运行过程中分成了很多个间隔相等的时间片,而每经过一个时间片后,就会触发一个定时的操作。这个操作就是前面提到的定时器触发的正常流程操作,因此,除了定时器到达后的正常流程工作外,其他情况可以看作是空闲,也就是说,在这些情况下一般是没有做任何事情。而在这条时间轴上,由于用户需要对当前下坠物进行操作,因此需要做没有特定规律的按键指示操作,这些操作可以用作是插入在一些时间片中的“中断”操作它们的插入是随机的,是由用户控制的,所以它们在时间轴上的分布是不规则的。它们的关系基本是并列的,一起处理整个流程。
接下来对这个中断性的用户指示操作进行分析。用户的按键指示操作还是比较简单的,它们无非是根据功能按键的不同,去响应不同的操作罢了,其流程图如下3-9所示。
开始
1.解释用户指示命令
2.根据不同的指示做出相应功能的操作
3.1向左移动一个单位
3.2向右移动一个单位
3.3向下移动一个单位
3.4变形
结束
图3-9用户指示命令中断操作流程图
用户指示命令中断操作的流程图主要分成3大步骤,而第三步则是根据不同的功能按键响应做相应的功能操作。
(1) 接收到拥护功能按键后分析传入的参数,并解释出该功能的键值。
(2) 对按键的键值进行判断,构建一个大的逻辑分析流程,并对相应按键的功能模块进行操作。
(3) 具体功能模块操作的实现(包括左移,右移,变形,加速下降等)。
经过对整个游戏运行的两大框架的流程(定时器正常流程和按键中断流程)进行分析后,就可以对它们进行实现了。
3.3.3正常流程的设计
从分析游戏的特性可以知道,定时器的产生与生效应该在游戏开始的时候,而在游戏暂停或者游戏结束时将已经社顶的定时器失效/销亡(对于暂停的情况,使它销亡,当游戏从暂停状态又进入游戏状态的时候,则重新创建一个定时器并激活它的运作),所以分别在游戏的开始函数、暂停函数以及结果函数中实现定时器的激活与去激活工作。这里,先在资源编辑器菜单资源里面添加3个菜单选项,分别是游戏的“开始”、“暂停”和“结束”(由于这3个功能是外部给用户操作的功能,所以可以预先在菜单栏中添加相应的功能选项,然后在视图类中实现它们即可,当它们找到直接关联的物件后对虚拟类的功能操作预先脱壳),然后利用ClassWizard直接在视图类对象CSkyblue_RectView中为它们添加空白的处理函数,具体如表3-2所示。
表3-2菜单选项功能对应表
菜单选项名称
快捷键
资源ID
响应处理函数
开始游戏
&S.
ID_GAME_START
OnGameStart()
暂停游戏
&P.
ID_GAME_PAUSH
OnGamePaush()
结束游戏
&E.
ID_GAME_END
OnGameEnd()
具体实现如下所示:
void CSkyblue_RectView::OnGameStart()
{
m_bGamePaush = FALSE;
SetTimer(1,1500-250*m_iLevel,NULL);
}
//游戏暂停
void CSkyblue_RectView::OnGamePaush()
{
m_bGamePaush = TRUE;
KillTimer(1);
}
void CSkyblue_RectView::OnGameEnd()
{
m_bGameEnd = TRUE;
m_bGamePaush = FALSE; //清除游戏暂停状态
KillTimer(1);
}
从上面的3个函数代码中可以看到,在添加这3个函数时,视图类还添加了涉及到游戏状态的3个成员变量,他们分别是用作判定当前游戏级别的m_iLevel、记录游戏状态的m_bGamePaush(是否暂停)和m_bGameEnd(是否结束游戏)。这里将定时器的标识定为1,将定时器ID放在视图类的一个成员变量中保存起来,因为如果定时器设定成功时,第一个参数的值就是返回的ID值。
3.3.4正常流程的实现(定时处理)
经过定时器的设置后,就可以在定时器预定时间到达后对正常流程进行实现了。这里通过利用ClassWizard跳到定时器到时后的处理函数OnTimer()去实现。OnTimer()函数的实现基本跟正常流程操作一样,当固定时间片间隔到达后,先检测当前下坠物是否已经到达了底部,不是则进行RectDown()下坠物向下移动一个单位的操作,是则激活状态下正在使用的下坠物,并且对使用后的一些状态进行检测。
(1) 是否马上到达底部,是则进行削行操作。
(2) 是否在到达底部的同时到达游戏区域的顶部,从而判定游戏是否因违规而
展开阅读全文