俄罗斯方块C语言.docx
- 文档编号:30587140
- 上传时间:2023-08-18
- 格式:DOCX
- 页数:45
- 大小:259.94KB
俄罗斯方块C语言.docx
《俄罗斯方块C语言.docx》由会员分享,可在线阅读,更多相关《俄罗斯方块C语言.docx(45页珍藏版)》请在冰豆网上搜索。
俄罗斯方块C语言
俄罗斯方块游戏
在本章内容中,将介绍使用C语言开发一个简单的俄罗斯方块游戏的方法,并详细介绍其具体的实现流程。
1.1第一个项目
1.1.1作业
2004年7月1日,晴,我的作业
在离校前的10分钟,我们最敬仰的程序老师TC给我们布置了一个暑期作业:
题目很简单——用C语言实现俄罗斯方块游戏(提示用graphics.h实现),并提醒务必做好前期的分析工作。
1.1.2准备
2004年7月3日,微风阵阵
老师的建议:
在做项目前一定要好好地构思和规划项目,根据需求规划开发流程。
于是,我在电脑上画了一个简单的项目开发流程图,如图1-1所示。
图1-1开发流程图
❑
功能分析:
分析整个系统所需要的功能;
❑模块结构规划:
规划系统中所需要的功能模块;
❑总体设计:
分析系统处理流程,探索系统核心模块的运作;
❑数据结构:
设计系统中需要的数据结构;
❑规划函数:
预先规划系统中需要的功能函数;
❑具体编码:
编写系统的具体实现代码。
1.2功能分析
2004年7月4日,阳光明媚
其基本结构如图1-2所示。
图1-2俄罗斯方块游戏的基本结构
这样,我总结出了俄罗斯方块游戏的基本功能模块,并做了一个简单的项目规划书,整个规划书分为两个部分:
❑系统需求分析;
❑结构规划。
1.2.1系统需求分析
1)游戏方块的预览功能
当游戏运行后并在底部出现一个游戏方块时,必须在预览界面中出现下一个方块,这样便于玩家提前进行控制处理。
因为在该游戏中共有19种方块,所以在方块预览区内要显示随机生成的游戏方块。
2)游戏方块的控制功能
游戏玩家可以对出现的方块进行移动处理,分别实现左移、右移、快速下移、自由下落和行满自动消除功能的效果。
3)更新游戏显示
当在游戏中移动方块时,需要先消除先前的游戏方块,然后在新坐标位置重新绘制新方块。
4)游戏速度设置和分数更新
通过游戏分数能够实现对行数的划分,例如,可以设置消除完整的一行为10分。
当达到一定数量后,需要给游戏者进行等级上的升级。
当玩家级别升高后,方块的下落速度将加快,从而游戏的难度就相应地提高了。
5)系统帮助
游戏玩家进入游戏系统后,通过帮助了解游戏的操作提示。
一个俄罗斯方块游戏的基本功能也就上述5条了,当然现实中的游戏产品更加复杂,但其基本的功能都是大同小异的。
1.2.2结构规划
现在开始步入结构规划阶段。
为了加深印象,我做了一个模块结构图,如图1-3所示。
图1-3游戏的模块结构
1.2.3选择工具
2004年7月5日,晴,工具的困惑
建议选TurboC。
因为在DEV-C++中使用graphics.h比较复杂!
历时两天,我确定好了整个项目的功能模块,做好了整体规划,也选好了开发工具。
接下来我将要步入总体设计阶段。
1.3总体设计
经过总体构成功能的分析后,接下来就可以根据各构成功能模块进行对应的总体设计处理。
主要包括两个方面:
❑运行流程分析;
❑核心处理模块分析。
1.3.1运行流程分析
2004年7月6日,上午,阳光明媚
了整个游戏的具体运作流程图。
游戏的具体运作流程如图1-4所示,用左移VK_LEFT、右移VK_RIGHT、下移VK_DOWN、旋转VK_UP和退出VK_Esc键判断键值。
上述几个按键移动处理的具体说明如下。
❑VK_LEFT:
调用MoveAble()函数,判断是否能左移,如果可以则调用EraseBox函数,清除当前的游戏方块。
并在下一步调用show_box()函数,在左移位置显示当前游戏的方块。
❑VK_RIGHT:
右移处理,及上面的VK_LEFT处理类似。
❑VK_DOWN:
下移处理,如果不能再移,必须将flag_newbox标志设置为1。
❑VK_UP:
旋转处理,首先判断旋转动作是否执行,在此需要满足多个条件,如果不合条件,则不予执行。
❑VK_Esc:
按Esc键后将退出游戏。
图1-4游戏运行流程
1.3.2核心处理模块分析
2004年7月6日,下午,多云,还有很长的路要走
1.方块预览
新游戏的方块将在4×4的正方形小方块中预览,使用随机函数rand()可以产生1-19之间的游戏方块编号,并作为预览的方块编号。
其中品尼高正方形小方块的大小由BSIZE×BSIZE来计算。
2.游戏方块控制处理
方块的移动控制是整个游戏的重点和难点,具体信息如下。
1)左移处理
处理过程如下。
(1)判断是否能够左移,判断条件有两个:
左移一位后方块不能超越游戏底板的左边线,否则将越界;并且在游戏方块有值(值为1)的位置,游戏底板不能是被占用的(占用时值为1)。
(2)清除左移前的游戏方块;
(3)在左移一位的位置处,重新显示此游戏的方块。
2)右移处理
处理过程如下。
(1)判断是否能够右移,判断条件有两个:
右移一位后方块不能超越游戏底板的右边线,否则将越界;游戏方块有值位置,游戏底板不能被占用;
(2)清除右移前的游戏方块;
(3)在右移一位的位置处,重新显示此游戏的方块。
3)下移处理
处理过程如下。
(1)判断是否能够下移,判断条件有两个:
下移一位后方块不能超越游戏底板的底边线,否则将越界;游戏方块有值位置,游戏底板不能被占用。
满足上述两个条件后,可以被下移处理。
否则将flag_newbox设置为1,在主循环中会判断此标志;
(2)清除下移前的游戏方块;
(3)在下移一位的位置处,重新显示此游戏的方块。
4)旋转处理
处理过程如下。
(1)判断是否能够旋转,判断条件有两个:
旋转后方块不能超越游戏底板的底边线、左边线和右边线,否则将越界;游戏方块有值位置,游戏底板不能被占用;
(2)清除旋转前的游戏方块;
(3)在游戏方块显示区域(4×4)的位置,使用当前游戏方块的数据结构中的next值作为旋转后形成的新游戏方块的编号,并重新显示这个编号的游戏方块。
3.更新显示
当游戏中的方块在进行移动处理时,要清除先前的游戏方块,用新坐标重绘游戏方块。
当消除满行后,要重绘游戏底板的当前状态。
清除游戏方块的方法是先画轮廓再填充,具体过程如下:
绘制一个轮廓,使用背景色填充小方块,然后使用前景色画一个游戏底板中的小方块。
循环此过程,变化当前坐标,填充并画出19个这样的小方块,从而在游戏底板中清除此游戏方块。
4.游戏速度和分数更新处理
当行满后,积分变量score会增加一个固定的值,然后将等级变量level和速度变量speed相关联,实现等级越高速度越快的效果。
2004年7月6日,晚上,总体设计的重要性
数据结构设计
1.4数据结构
2004年7月7日,上午,阳光充足
我就设计好了系统所需要的数据结构。
1.游戏底板结构体
此处的游戏底板结构体是BOARD,具体的代码如下。
structBOARD/*游戏底板结构,表示每个点所具有的属性*/
{
intvar;/*当前状态只有0和1,1表示此点已被占用*/
intcolor;/*颜色,游戏底板的每个点可以拥有不同的颜色,增强美观性*/
}Table_board[Vertical_boxs][Horizontal_boxs];
其中,BOARD结构体表示了游戏底板中每个小方块的属性,var表示了当前的状态,为0时表示未被占用,为1时表示已经被占用。
2.游戏方块结构体
此处的游戏方块结构体是SHAPE,具体的代码如下。
structSHAPE{
/*一个字节是8位,用每4位表示游戏方块中的一行,例如:
box[0]="0x88",box[1]="0xc0"表示的是:
1000
1000
1100
0000*/
charbox[2];
intcolor;/*每个方块的颜色*/
intnext;/*下个方块的编号*/
};
SHAPE结构体表示某个小方块的属性,charbox[2]表示用2个字节来表示这个块的形状,每4位来表示一个方块的一行。
color表示每个方块的颜色,颜色值可以根据需要设置。
3.SHAPE结构数组
此处的游戏方块结构体是SHAPE,具体的代码如下。
/*初始化方块内容,即定义MAX_BOX个SHAPE类型的结构数组,并初始化*/
structSHAPEshapes[MAX_BOX]=
{
/*
*口口口口口口口
*口口口口口口
*口口口
*/
{0x88,0xc0,CYAN,1},
{0xe8,0x0,CYAN,2},
{0xc4,0x40,CYAN,3},
{0x2e,0x0,CYAN,0},
/*
*口口口口口口
*口口口口
*口口口口口口
*/
{0x44,0xc0,MAGENTA,5},
{0x8e,0x0,MAGENTA,6},
{0xc8,0x80,MAGENTA,7},
{0xe2,0x0,MAGENTA,4},
/*
*口
*口口口口
*口口口
*/
{0x8c,0x40,YELLOW,9},
{0x6c,0x0,YELLOW,8},
/*
*口口口
*口口口口
*口
*/
{0x4c,0x80,BROWN,11},
{0xc6,0x0,BROWN,10},
/*
*口口口
*口口口口口口口口口口
*口口口
*/
{0x4e,0x0,WHITE,13},
{0x8c,0x80,WHITE,14},
{0xe4,0x0,WHITE,15},
{0x4c,0x40,WHITE,12},
/*口
*口
*口口口口口
*口
*/
{0x88,0x88,RED,17},
{0xf0,0x0,RED,16},
/*
*口口
*口口
*/
{0xcc,0x0,BLUE,18}
};
在上述代码中,定义了MAX_BOX个SHAPE类型的结构数组,并进行了初始化处理。
因为共有19种不同的方块类型,所以MAX_BOX为19。
2004年7月7日,晚上,数据结构的重要性
1.5一个神秘的箱子
2004年7月8日,晴空万里
武侠小说,是成年人的童话。
从少年开始我就迷恋上了武侠小说,一直伴我读到大学。
记得在某一段时间,我曾经特别痴迷一个神秘的箱子.
出自古龙的武侠名著《英雄无泪》的一段对白,没错最厉害的武器是一口箱子。
等我看完全文之后我才明白,这不是一口简单的箱子,箱子里有很多个零部件,能够根据不同的对手而迅速组成一个战胜对手的武器。
现在我发现这个箱子和程序中的函数是那么的相似!
我们要编程解决一个问题,要实现某个功能,我们可以编写一个函数来实现它。
如果有多个问题,则编写多个函数就可以实现,函数就是我们编程中的那个神秘的箱子。
书归正传,我预先设置好了整个项目中需要的函数,并做好了定义。
1.函数NewTimer
函数NewTimer用于实现新的时钟,具体结构如下:
voidinterruptnewtimer(void)
2.函数SetTimer
函数SetTimer用于设置新时钟的处理过程,具体结构如下:
voidSetTimer(voidinterrupt(*IntProc)(void))
3.函数KillTimer
函数KillTimer用于恢复原有的时钟处理过程,具体结构如下:
voidKillTimer()
4.函数initialize
函数initialize用于初始化界面,具体结构如下:
voidinitialize(intx,inty,intm,intn)
5.函数DelFullRow
函数DelFullRow用于删除满行,y设置删除的行数,具体结构如下:
intDelFullRow(inty)
6.函数setFullRow
函数setFullRow用于查询满行,并调用DelFullRow函数进行处理,具体结构如下:
voidsetFullRow(intt_boardy)
7.函数MkNextBox
函数MkNextBox用于生成下一个游戏方块,并返回方块号,具体结构如下:
intMkNextBox(intbox_numb)
8.函数EraseBox
函数EraseBox用于清除以(x,y)位置开始的编号为box_numb的游戏方块,具体结构如下:
voidEraseBox(intx,inty,intbox_numb)
9.函数show_box
函数show_box用于显示以(x,y)位置开始的编号为box_numb,颜色值为color的游戏方块,具体结构如下:
voidshow_box(intx,inty,intbox_numb,intcolor)
10.函数MoveAble
函数MoveAble首先判断方块是否可以移动,其中(x,y)是当前的位置,box_numb是方块号,direction是方向标志。
具体结构如下:
intMoveAble(intx,inty,intbox_numb,intdirection)
2004年7月8日,晚上,函数的重要性
我历经8天的忙碌,终于完成了前期的所有工作。
整个过程很顺利,虽然项目很简单,但是我很细心,可以说这是我第一次这么细心地做一个项目。
到此为止,我更加认识到了函数在C语言中的作用。
几乎项目中的所有功能都是通过函数来实现的,函数构成了整个项目的主体。
1.6具体实现
2004年7月9日,晴空万里
开发征程正式拉开序幕!
1.6.1预处理
如果说前面的准备工作是开发项目的第一步,那么预处理就打响了程序开发的第一枪。
我们先看一下预处理的定义:
预处理是在程序源代码被编译之前,由预处理器(Preprocessor)对程序源代码进行的处理。
这个过程并不对程序的源代码进行解析,但它把源代码分割或处理成为特定的符号用来支持宏调用。
看来预处理也是一个准备工作,不搬到台面上。
这就像我们辛苦学习知识,为将来的工作而做准备一样。
在以后工作中,所学的知识只会在后台默默无闻地支持着我们。
在开始之前画了一个简单的实现流程图,如图1-5所示。
图1-5预处理流程图
(1)先引用图形函数库等公用文件,具体代码如下所示。
#include
#include
#include
#include
(2)定义按键码,即操控游戏的按键:
左移、右移、下移、上移等。
具体代码如下所示。
/*定义按键码*/
#defineVK_LEFT0x4b00
#defineVK_RIGHT0x4d00
#defineVK_DOWN0x5000
#defineVK_UP0x4800
#defineVK_ESC0x011b
#defineTIMER0x1c/*设置中断号*/
(3)定义系统中需要的常量,例如方块种类、方块大小、方块颜色等。
具体实现代码如下所示。
/*定义常量*/
#defineMAX_BOX19/*总共有19种形态的方块*/
#defineBSIZE20/*方块的边长是20个像素*/
#defineSys_x160/*显示方块界面的左上角x坐标*/
#defineSys_y25/*显示方块界面的左上角y坐标*/
#defineHorizontal_boxs10/*水平的方向以方块为单位的长度*/
#defineVertical_boxs15/*垂直的方向以方块为单位的长度,也就说长是15个方块*/
#defineBegin_boxs_xHorizontal_boxes2/*产生第一个方块时出现的起始位置*/
#defineFgColor3/*前景颜色,如文字.2-green*/
#defineBgColor0/*背景颜色.0-blac*/
#defineLeftWin_xSys_x+Horizontal_boxs*BSIZE+46/*右边状态栏的x坐标*/
#definefalse0
#definetrue1
/*移动的方向*/
#defineMoveLeft1
#defineMoveRight2
#defineMoveDown3
#defineMoveRoll4
/*以后坐标的每个方块可以看作像素点是BSIZE*BSIZE的正方形*/
(4)定义系统中需要的全局变量,例如,方块的下落速度、玩家的分数、当前的方块编号等。
具体实现代码如下所示。
/*定义全局变量*/
intcurrent_box_numb;/*保存当前方块编号*/
/*x,y是保存方块的当前坐标的*/
intCurbox_x=Sys_x+Begin_boxs_x*BSIZE,Curbox_y=Sys_y;
intflag_newbox=false;/*是否要产生新方块的标记0*/
intspeed=0;/*下落速度*/
intscore=0;/*总分*/
intspeed_step=30;/*每等级所需要分数*/
/*指向原来时钟中断处理过程入口的中断处理函数指针*/
voidinterrupt(*oldtimer)(void);
(5)定义底板结构和方块结构。
每一个新出现的方块结构是不同的,当方块下落到游戏底板后,结构也是不同的,所以必须编写两个结构来存储即时结构。
具体实现代码如下所示。
structBOARD/*游戏底板结构,表示每个点所具有的属性*/
{
intvar;/*当前状态只有0和1,1表示此点已被占用*/
intcolor;/*颜色,游戏底板的每个点可以拥有不同的颜色,增强美观性*/
}Table_board[Vertical_boxs][Horizontal_boxs];
/*方块结构*/
structSHAPE{
charbox[2];/*一个字节等于8位,每4位表示一个方块的一行
如:
box[0]="0x88",box[1]="0xc0"表示的是:
1000
000
1100
0000*/
intcolor;/*每个方块的颜色*/
intnext;/*下个方块的编号*/
};
(6)开始初始化方块内容,即定义允许MAX_BOX个预定义类型的数组,其中MAX_BOX代表允许的最多箱子个数,并初始化。
初始化就是把变量赋为默认值,把控件设为默认状态,把没准备的准备好。
具体实现代码如下所示。
/*初始化方块内容*/
structSHAPEshapes[MAX_BOX]=
{
/*
*口口口口口口口
*口口口口口口
*口口口
*/
{0x88,0xc0,CYAN,1},
{0xe8,0x0,CYAN,2},
{0xc4,0x40,CYAN,3},
{0x2e,0x0,CYAN,0},
/*
*口口口口口口
*口口口口
*口口口口口口
*/
{0x44,0xc0,MAGENTA,5},
{0x8e,0x0,MAGENTA,6},
{0xc8,0x80,MAGENTA,7},
{0xe2,0x0,MAGENTA,4},
/*
*口
*口口口口
*口口口
*/
{0x8c,0x40,YELLOW,9},
{0x6c,0x0,YELLOW,8},
/*
*口口口
*口口口口
*口
*/
{0x4c,0x80,BROWN,11},
{0xc6,0x0,BROWN,10},
/*
*口口口
*口口口口口口口口口口
*口口口
*/
{0x4e,0x0,WHITE,13},
{0x8c,0x80,WHITE,14},
{0xe4,0x0,WHITE,15},
{0x4c,0x40,WHITE,12},
/*口
*口
*口口口口口
*口
*/
{0x88,0x88,RED,17},
{0xf0,0x0,RED,16},
/*
*口口
*口口
*/
{0xcc,0x0,BLUE,18}
};
2004年7月9日,晚上,时刻要学习
在具体编码之前,在我脑海中关于预处理的知识毫无印象。
因此在具体编码时,我发现一行代码也写不出来。
这种情况我相信在很多初学者身上也发生过,而且不止发生一次。
我没有办法,只能自己搜集资料学习。
看来无论是一个学生,还是以后步入职场,都要随时提高自己,来应对新技术的发展。
1.6.2主函数
我深知主函数的重要性,所以在设计之前,特意请教了师兄A:
我:
“开始主函数设计了,哈哈!
”
A:
“呵呵,看来准备工作都已经做完了。
我不知你前面的工作流程,但是我还是提醒你要注意:
前期准备工作的重要性!
整体分析和规划都要仔细考虑,并且尽可能的书面化!
”
我:
“嗯,明白了!
作为主函数,您有什么建议?
”
A:
“五个字:
尽量的简单!
因为主函数肩负着入口和出口的重任,所以尽量不要把太多细节方面的逻辑直接放在主函数内,这样不利于维护和扩展。
主函数应该尽量简洁,具体的实现细节应该封装到被调用的子函数中。
”
简单是编写函数的第一要务,我编写的主函数如下。
voidmain(){
intGameOver=0;
intkey,nextbox;
intCurrentaction=0;/*标记当前动作状态*/
intgd=VGA,gm=VGAHI,errorcode;
initgraph(&gd,&gm,"");
errorcode=graphresult();
if(errorcode!
=grOk)
{
printf("\nNotice:
Graphicserror:
%s\n",grapherrormsg(errorcode));
printf("Pressanykeytoquit!
");
getch();
exit
(1);
}
setbkcolor(BgColor);
setcolor(FgColor);
randomize();
SetTimer(newtimer);
initialize(Sys_x,Sys_y,Horizontal_boxs,Vertical_boxs);/*初始化*/
nextbox=MkNextBox(-1);
show_box(Curbox_x,Curbox_y,current_
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 俄罗斯方块 语言