1.1 项目名称
俄罗斯方块游戏
1.2
平台
Visual C++ 6.0, 它是以C++语言作为其基本语言的一种可视化编程工具。
1.3程序设计思想
从游戏的基本玩法出发,主要就是俄罗斯方块的形状和旋转,我们在设计中在一个图片框中构造了一个4*4的网状小块,由这些小块组合成新的形状,每四个小块连接在一起就可以构造出一种造型,因此我们总共设计了7中造型,每种造型又可以通过旋转而变化出2到4种形状,利用随机函数在一个预览窗体中提前展示形状供用户参考,然后将展示的形状复制到游戏窗体中进行摆放,在游戏窗体中用户就可以使用键盘的方向键来控制方块的运动,然后利用递归语句对每一行进行判断,如果有某行的方块是满的,则消除这行的方块,并且使上面的方块自由下落,其中,方块向下的速度是有时钟控件控制的,在游戏中,用户也可以使用向下键加快下落速度,定义一个变量,对消除的函数进行记录,最后就可以得出用户的分数,用if 语句对分数判断,达到一定的积分就可以升级到下一个档次。
俄罗斯方块游戏设计的主要步骤为以下6个方面:
(1)游戏界面的设计。
(2)俄罗斯方块的造型。
(3)俄罗斯方块的旋转。
(4)俄罗斯方块的运动情况(包括向左,向右和向下)。
(5)俄罗斯方块的自动消行功能。当不同的方块填满一行时可以消行,剩余方块向下移动并统计分数。
(6)游戏得分的计算。当达到一定的分数时过关。设置六关,每关方块下落的速度不同。
1.4主要实现的功能
我们开发的俄罗斯方块游戏,主要实现了以下几种功能:
1.可以灵活控制方块在图形框中运动。
2.游戏过程中方块可以自由旋转。
3.当某一行的方块排列满时,将自动将这一行方块消除,然后将上面所有方块向下移动,可以支持连续消行。
4.游戏前可以选择游戏的速度和游戏的等级,游戏速度既为方块下落速度,游戏等级为初始游戏时在基层随机生成一定行数的无规律方块,对于游戏高手来说,无疑不是一个新的挑战。
2.1 需求分析
随着信息技术的迅速发展,给人们带来了各种各样的信息和更多更新的娱乐。其中游戏又最为常见和普遍。所以我们实现了一个俄罗斯方块的小游戏,来对所学到的知识进行实践。
2.2 关于游戏界面
我们制作了一个良好的用户界面,有关数显示和分数显示。让方块在一定的区域内运动和变形,该区域用一种颜色
明,既用一种颜色作为背景,最好设为黑色。还需用另一种颜色把黑色围起来,宽度适中,要实现美感。方块下落时,可通过键盘方向键(上、下、左、右键)对该方块进行向上(变形),向下(加速)、向左、向右移动。
2.3 游戏的数据结构
相信大家都玩过俄罗斯方块,对这个游戏的玩法和方块形状都比较熟悉。我们这个游戏只选择了最基本的7中造型,包括长条型,正方型,正S型,反S型,正7型,反7型,T型。在旋转的过程中我们以逆时针旋转为基础。
为此,我们对于方块在某一瞬间的位置标识,采用一个4×2的小数组标识出来,即用4个存储单位空间存储当前下坠物的每一子块的位置,也就是说,用4个存储单位空间存储当前下坠物的每一子块的位置来对整个下坠物件的位置进行标识,而每个存储空间的大小就是一个点的坐标值(x,y),而每个方块按照从左到右的方式进行编号,并且在编号过程中对于同一列的方块实行从上到下进行编号。
1
2
3
ActiveStatus[0][0]和ActiveStatus[0][1]则是第0号方块的横坐标x和纵坐标y ;ActiveStatus[2][0]和ActiveStatus[2][1]则是第2号方块的横坐标x和纵坐标y。
(x0,y0)
(x1,y1)
(x2,y2)
(x3,y3)
2.3 创建主框架
首先建立一个项目工程,名为skyblue_Rect,并在AppWizard的架构选择过程中选择单文档方式,其他保持默认选项。其项目的架构类视图信息如图所示:
在构架类视图中是MFC基本架构组合:App(应用程序)类、Document(文档)类、View(视图)类、Frame(框架)类和用于提示关于作者的对话框CAboutDlg类,至于COptionDlg类是用作俄罗斯方块参数选择的对话框类对象。
2.4 游戏区域绘图
首先将外部位图文件rect.bmp中的位图动态导入(映射)到内存位图里面,根据游戏区域中的二维数组GameStatus[MAX_ROW][MAX_COL]中的内部数据将所有数据状态中为被占用状态MAP_STATE_NOT_EMPTY的小方块区域用指定的小方块图样类型来填充,然后将已经绘制好的游戏区域图像一次性的拷贝到与屏幕关联的设备环境中,从而达到屏幕的显示。
2.5 游戏设计分析
我先虚拟出俄罗斯方块游戏的类对象,并抽象出核心的数据属性和操作方法等,然后再作细化,最后将整个虚拟类的外壳脱掉,再移植到视图类中去,其实现如下:
CRectGameView : public CView
{//内部存取数据结构
int m_stateMap[MAX_ROW][MAX_COL];//初始化操作
GameInitnal(); //游戏的初始化 //用于判断数据相关状态的操作
IsLeftLimit(); //下坠物件是否可向左移动
IsRightLitmit(); //
IsBottom(); //是否已经到达了底部
IsGameEnd(); //是否游戏已经结束
//方块物件下坠过程中的操作
RectChange(); //下坠物件变形
RectDown(); //下坠物件正常下落
RectArrow(); //下坠物件方向移动(左,右,下加速) //状态控制操作
GameStart(); //游戏开始
GamePause(); //游戏暂停
GameEnd(); //游戏结束
2.6正常流程设计
(1)定时制机制
从分析游戏的特性可以知道,定时器的产生与生效应该在游戏开始的时候,而在游戏暂停或者游戏结束时则将已经设定的定时器失效/销亡(对于暂停的情况,使它销亡,当游戏从暂停状态又进入游戏状态时候,则重新创建一个定时器并激活它的运作),所以分别在游戏的开始函数、暂停函数已经结束函数中实现定时器的激活与去激活工作。这里,先在资源编辑器菜单资源里面添加三个菜单选项,分别是游戏的“开始”、“暂停”、和“结束”,然后利用ClassWizard直接在视图类对象Cskyblue_RectView中为它们添加空白的处理函数,
(2)定时处理
经过定时器的设置后,这里通过利用ClassWizard跳到定时器到时候的处理函数OnTimer()去实现,当固定时间片间隔到达后,先检测当前下坠物是否已经到达了底部,不是则进行RectDown()下坠物向下移动一个单位的操作,是则到底后产生一个新的“下一个下坠物”,并代替旧的,将原先旧的“下一个下坠物”用作当前激活状态下正在使用的下坠物,并对使用后的一些状态进行检测:是否马上到达底部,使则进行销行操作
;是否在到达底部的同时到达游戏区域的顶部,从而判定游戏是否因违规而结束。
产生的随机数函数:
int CSkyblue_RectView::Random(int MaxNumber)
{
//部下随机种子
srand( (unsigned)time( NULL ) );
//产生随机数
int random = rand() % MaxNumber;
//保证非0
if(random == 0 ) random++;
return random;
}
视图类创建了m_icurrentStatus和m_inextStatus两个成员变量来记录下坠物的类型,共有七种形状,并从7种方块中随机抽取图形。而m_currentRect除了记录下坠物的类型外,还需记录其当前的变形状态,最多用两位表示,第1位用作类型标识(1~7),第2位用作同种类型的不同表现方式,最多有4种状态(1~4)。
在产生新的下一个下坠物前,需要先将当前状态物的记录和旧的下一个下坠物保存下来,然后用随机函数Random()产生一个最大值不大于指定值的随机正整数,将这个新生成的正整数用作新的“下一个下坠物”的形状值。
(3)底部到达的判断与销行的实现
将新的下坠物放置到游戏区域中去,这时可能出现马上到达底部的情况,因此需要对它进行判断,如果是到达底部,则进行销行处理,并且修改相应的数据状态。而判断是否已经到达了底部,可以通过当前下坠物件所对应的接触面的方块位置为被占用状态(MAP_STATE_NOT_EMPTY=1)来确定,利用数组InterFace[74][4]记录1~7种下坠物的1~4种形态的接触面信息。
统计分数:在消行处理里面有一个专门用来统计消行数的变量,然后根据变量的值决定分数的多少,程序统计分数是:消一行得100分,同时消2行得400分,销掉x行,则分数为:x*(x*100)。如果总分数达到过关条件就过关,改变游戏速度,游戏初始化,开启新的一关,然后再加载方块。没有达到过关分数或者没有满行,则加载下一个方块继续游戏。
检测游戏区域中的所有行,,并对每行的所有纵列状态进行检测,如果其中有一列是空闲状态则不可以销行。如果可以销行的话,将增加单位分数,并且将该行清空,再将该行上面的所有物件都向下偏移一个单位,以填充该行的空缺。
例销行与积分
if (m_isBottom)
{//判断是否已得分
for (i=0;i
0;k--)
for (j=0;j0) && IsLeftLimit() && !m_isBottom)
{
//清原来的方块
GameStatus[x1][y1]=MAP_STATE_EMPTY;
GameStatus[x2][y2]=MAP_STATE_EMPTY;
GameStatus[x3][y3]=MAP_STATE_EMPTY;
GameStatus[x4][y4]=MAP_STATE_EMPTY;
// InvalidateCurrent();
//添加新的移动后数据状态
ActiveStatus[0][1] -= 1;
ActiveStatus[1][1] -= 1;
ActiveStatus[2][1] -= 1;
ActiveStatus[3][1] -= 1;
GameStatus[x1][y1-1]=MAP_STATE_NOT_EMPTY;
GameStatus[x2][y2-1]=MAP_STATE_NOT_EMPTY;
GameStatus[x3][y3-1]=MAP_STATE_NOT_EMPTY;
GameStatus[x4][y4-1]=MAP_STATE_NOT_EMPTY;
InvalidateCurrent();
}
Break;
case RIGHT:
if ( (ActiveStatus[3][1]< m_iCol-1) && IsRightLitmit() && !m_isBottom)
{
//清原来的方块
GameStatus[x1][y1]=MAP_STATE_EMPTY;
GameStatus[x2][y2]=MAP_STATE_EMPTY;
GameStatus[x3][y3]=MAP_STATE_EMPTY;
GameStatus[x4][y4]=MAP_STATE_EMPTY;
// InvalidateCurrent();
//添加新的移动后数据状态
ActiveStatus[0][1] += 1;
ActiveStatus[1][1] += 1;
ActiveStatus[2][1] += 1;
ActiveStatus[3][1] += 1;
GameStatus[x1][y1+1]=MAP_STATE_NOT_EMPTY;
GameStatus[x2][y2+1]=MAP_STATE_NOT_EMPTY;
GameStatus[x3][y3+1]=MAP_STATE_NOT_EMPTY;
GameStatus[x4][y4+1]=MAP_STATE_NOT_EMPTY;
InvalidateCurrent();
}
break;
case DOWN:
RectDown();
break;
}
}
2.9 游戏主界面
游戏设置界面
2.8
着手开始做的时候,发现很多东西并不容易实现。比如:如何显示每一个俄罗斯小方块,如何预测下一个俄罗斯小方块,如何控制每一个方块的下落速度,如何判断俄罗斯方块是否可以停止,如何通过键盘控制俄罗斯方块的位置和方向等。如果有一行或多行满行,如何删除这些行,并重新绘制游戏区界面,同时分数如何计算并显示等等,都要一一实现。感觉真是难上加难。
不过,是事情总会有解决办法,是问题总有突破口,于是我们找来相关书籍仔细研究算法,最后终于一切明了于心。
通过这次课程设计,我对游戏编程有了初步认识,对游戏法有了一定了解从游戏的需求分析,系统设计,再到游戏代码实现每一步都对自己是一个挑战,虽然参考了书上的系统设计和一定代码,但是真的感受到游戏编程的快乐,也学习到不少东西,主要是实现需求分析的能力以及算法地描述。