1.8 游戏项目开发
在一个商业化的游戏项目中,开发工作始于完整的游戏开发方案(并非我们这里的简单术语描述),并以项目或基本规范说明书的形式延续下去。尽管开发方案是以自然语言形成的——以便于每个人都能理解它、批准它(包括游戏开发负责人,他可以批准或否决该项目的预算),但是项目规范说明书必须包含编程细节,它将在整个编码期间指导开发团队工作。
这里,我们的目的不是要说明哪些内容应该包含在项目规范说明书里(它主要依赖于开发团队所使用的研发方法),并且这里我们也没有必要创建一个非常完整的项目,因为它不是这本书的重点。但是,如果不创建任何项目就开始编码显然是不明智的。因此,我们将快速浏览一下项目情况以便使某些实现细节更加明朗。
VB.Net游戏编程入门经典(8)提示
当然,即便没有任何项目规范,我们也可以开始编码工作。但是,即使是个人单独进行工作的情况下,从一个项目规范开始工作仍然是最佳选择,因为它可以帮助您组织自己的思想、发现某些在落笔之前无法查明的细节问题。即使该项目规范仅仅是一些粗略方案或注释内容,然而它的使用仍将帮助您提高平均编码质量。事实上,项目规范越详细,编码工作做得就越好,因为它将帮助您在陷入某些陷阱或错误之前就能够识别出它们。
面向对象(OO)编程技术是最适合游戏项目开发使用的技术,因为在游戏中经常会处理一些代表真实世界事物的东西,这一点正如OO技术所倡导的一样。例如,在“街霸(Street Fighter)”游戏中,屏幕上并没有真正的斗士,只有一些可以移动的图片,这些图片由游戏者或计算机控制产生搏斗的幻觉。把OO方法应用到游戏项目开发中非常类似于如下做法:由开发者本人从真实世界事物中提取希望在程序中进行表达的重要特征,然后记录下它们。这里,我们暂不深入讨论这个问题,但是您可以获得一些很好的关于这方面内容的书籍。请参见附录A中的推荐书目和文献。
这是第一个要编写的程序,因此我们将逐步引导您完成该程序的编写过程,以此来说明如何从最初的游戏开发方案发展到最终的编码阶段;在后面的章节中,您将学到一种更为直接的方法。接下来的内容里,您将看到一些初步的类图、游戏主程序的伪代码,然后我们再回到程序的类图并为其添加一些更为精巧的内容。
1.8.1 类图:最初设计
我们从一个简单的类图(如图1-20所示)开始,它描述了程序涉及到的对象的基本结构,然后我们就可以为其添加一些细节并不断精化它,直到得到一个完整的类图形式。几乎所有的面向对象分析方法都表明了这种循环方法,它非常适合用来说明一个游戏设想如何从草案阶段发展到完整的具有某些特色的项目。
从游戏开发方案可以发现两个类:Block,它代表一个游戏部件;Square,它是Block的基本组成部件。
图1-20 类图——最初设计
基于游戏开发方案,我们可以确定Block类的某些方法(method)或函数(function)、属性(property)或变量(variable),如表1-1所示。
表1-1 Block类成员
类 型 | 名 称 | 描 述 |
方法 | Down | 使方块在屏幕上下落 |
方法 | Right | 使方块向右移动 |
方法 | Left | 使方块向左移动 |
方法 | Rotate | 使方块按顺时针方向旋转 |
属性 | Square1 | 指定组成方块的其中一个正方形 |
属性 | Square2 | 指定组成方块的其中一个正方形 |
属性 | Square3 | 指定组成方块的其中一个正方形 |
属性 | Square4 | 指定组成方块的其中一个正方形 |
每个block由4个来自Square类的对象组成,Square类的描述如表1-2所示。
表1-2 Square类成员
类 型 | 名 称 | 描 述 |
方法 | Show | 在指定的坐标位置(Location属性),使用指定的色彩(ForeColor属性)、指定的尺寸(Size属性)绘制正方形,并使用背景色(BackColor)填充它 |
方法 | Hide | 从屏幕上删除正方形 |
属性 | ForeColor | 指定正方形的前景色 |
VB.Net游戏编程入门经典(8) 属性 | BackColor | 指定正方形的背景色 |
属性 | Location | 指定正方形在屏幕上的x、y坐标位置 |
属性 | Size | 指定正方形的高度和宽度 |
比较上述两个表,可以发现:类中存在显示正方形和隐藏正方形的方法。因为正方形是由Block对象绘制的,所以Block类也必须包含相应的方法和属性。我们可以把第一个类图进行调整以产生如图1-21所示的类图。
图1-21 类图—— 第二次草图
我们使用SquareSize作为block的size属性,block的尺寸是无关紧要的,但是block必须知道正方形的尺寸,以便于block能够创建正方形。
如果有必要,我们可以在后面的内容中再次回到类图的讨论并对它进行调整。现在让我们把目光转向游戏引擎,接下来的部分将对其进行描述。
1.8.2 游戏引擎
使用VB的事件驱动机制,可以考虑编写3个主要的事件来实现游戏开发方案中描述的行为:
(1) 当窗体加载时,可以创建第一个方块。
(2) 在窗体的KeyPress事件中,可以处理来自用户的键盘输入。
(3) 使用一个定时器控件可以实现“在每个时钟设置点调用Down方法”的功能,从而产生出预想的方块下落效果。正如后面将要介绍的内容所述,当创建高速运行的游戏时,我们并不推荐使用定时器控件。但是这里的情况有所不同。
编写伪代码非常有助于验证类图的正确性,可以查看程序是否使用了全部的方法和属性,并验证是否能够利用这些类成员获得游戏开发方案所声明的结果。本游戏程序的伪代码如以下代码段所示:
Form_Load
Creates an object (named currentBlock) of block class
因为要在其他所有的事件中使用currentBlock对象,所以该对象的使用范围必须与窗体的使用范围一致。
Form_KeyPress
If Left Arrow was pressed, call Left method of CurrentBlock
If Right Arrow was pressed, call Right method of CurrentBlock
If Up Arrow was pressed, call Rotate method of CurrentBlock
If Down Arrow was pressed, call Down method of CurrentBlock
在上面的伪代码中,使用键盘上的向上箭头键来旋转方块、使用向下箭头键来强制方块加速下落、使用向右箭头键和向左箭头键使方块在水平方向上移动。
游戏引擎的核心是定时器事件。回顾一下游戏开发方案,我们就可以确定此处必须执行的操作:使方块下落、根据游戏规则停止方块下落、查看是否存在完整的水平线、检查游戏是否结束。执行这些操作的参考伪代码如下所示:
If there is no block below CurrentBlock,
and the CurrentBlock didn't reach the bottom of the screen then
Call the Down method of CurrentBlock
Else
Stop the block
If it's at the top of the screen then
The game is over
If we filled any horizontal lines then
Increase the game score
Erase the line
Create a new block at the top of the screen
分析以上代码,可以发现有些特征是当前类图所没有考虑到的。例如,怎么查看当前方块下是否存在其他方块?如何删除游戏者努力形成的水平线?关于这些内容,我们将在接下来的章节讨论。
1.8.3 类图:最终设计
为了检查早先下落的方块所处的位置,以确认当前方块下面是否存在其他方块或确认是否存在完整的水平线,我们必须采取某种方法来存储和检测方块的每个正方形,这种方法应该独立于早先下落的方块(切记,当删除一条水平线时,我们只能删除某个给定方块的一个或两个正方形)。可以通过创建一个新的类来做到这一点,这个类表示游戏区域,它存储了所有正方形的有关信息,并包含一些方法允许在其他特殊条件下删除水平线。经过认真考虑之后,我们把这个新的类加入到模型中,那么就发展成了如图1-22所示的类图。
图1-22 最终的类图
表1-3列举了新建类的方法和属性,并附带有简短的说明信息。
表1-3 Game Field类成员
类 型 | 名 称 | 描 述 |
属性 | Width and Height | 这两个属性分别表示游戏区域的宽度和高度,使用正方形的个数计算 |
属性 | SquareSize | 该属性表示每个正方形的大小,因此可以把像素个数换算成正方形个数 |
属性 | ArrGameField | 建立一个数组来存储所有已经停止下落的方块所包含的正方形 |
方法 | CheckLines | 该方法检查是否存在完整的水平线,如果存在,则删除它们,并返回已删除的水平线个数,从而使得主程序可以增加游戏者的得分 |
方法 | IsEmpty | 该方法检查某个特定位置(给定x、y坐标值)上的正方形是否为空,从而告知我们:什么时候某个方块正在工作 |
方法 | Redraw | 该方法强制性重新绘制游戏区域。当某个水平线被删除或者其他窗口重叠了该窗口时,使用该方法 |
在实际的项目开发过程中,所做的工作可能远非这些,需要不断地精化所有的方法以涵盖全部接口内容(接收的参数和返回值),并指定属性的数据类型,所有这些都可能导致类图的再次修改。这里,我们只是给出了最基本的处理思想,这才是我们的主要目的。