俄罗斯方块C语言.docx

上传人:b****5 文档编号:30587140 上传时间:2023-08-18 格式:DOCX 页数:45 大小:259.94KB
下载 相关 举报
俄罗斯方块C语言.docx_第1页
第1页 / 共45页
俄罗斯方块C语言.docx_第2页
第2页 / 共45页
俄罗斯方块C语言.docx_第3页
第3页 / 共45页
俄罗斯方块C语言.docx_第4页
第4页 / 共45页
俄罗斯方块C语言.docx_第5页
第5页 / 共45页
点击查看更多>>
下载资源
资源描述

俄罗斯方块C语言.docx

《俄罗斯方块C语言.docx》由会员分享,可在线阅读,更多相关《俄罗斯方块C语言.docx(45页珍藏版)》请在冰豆网上搜索。

俄罗斯方块C语言.docx

俄罗斯方块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_

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 初中教育 > 政史地

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1