admin 管理员组

文章数量: 1184232


2024年3月1日发(作者:伤情鉴定程序和时间)

一、题目

利用C++编写俄罗斯方块游戏,是俄罗斯游戏能够在Microsoft Visual

C++ 6.0上运行该游戏。

二、实验目的

一方面通过对程序算法的设计和分析提高我们对问题进行文字论述和文字表达的能力并且培养我们进行知识综合,软件开发和软件的调试技术,开发较大程序的能力。

另一方面培养了我们相互合作的精神并培养了我们的创新意识。

三、需求分析

功能需求

随机给出不同的形状下落填充给定的区域,若填满一条便消掉,记分,设计不同的游戏难度,即方块下落的速度不同,若在游戏中各形状填满了给定区域,为输者。

方块及各种变换需求

本游戏需要有7种方块,而每种方块还可以进行旋转。每种方块每行每列最多只有4个小方块。可以将它们放在一个n*m的区域内,该区域可以看作是有许多个等面积小方块构成的区域,而这些区域的状态只有两种,被方块占据或空闲。因此,对于整个游戏区域的空间是占据或空闲,可以用一位数来标识。对于7种方块和它们旋转后的形态我们可以用不同的标识进行标记。对于旋转,游戏中所有方块都是按照逆时针旋转的规则进行的,而且在旋转过程中它们不会因为旋转而下降,总会保持在同一高度。任何方块经过一个旋转周期还会变回原型。

操作的需求

向上键

产生方块旋转操作,方块并非任何情况都能旋转,如果旋转后与小方格矩阵显示的方块有冲突或超出边界时,均不能发生旋转。因此首先要判断是否有足够的空间进行旋转。然后选择是否旋转。

向下键

产生方块加速下落操作,如果方块已经到达游戏区域底部或者已经有其他方块遮挡,则方块停止下降。

向左键

产生下落方块左移操作。首先要判断此方块是否能够发生左移,当越界或被其他显示方块阻挡时,则不能左移。

向右键

1

产生下落方块右移操作。首先要判断此方块是否能够发生右移,当越界或被其他显示方块阻挡时,则不能右移。

四、详细设计

主要功能设计

根据分析,俄罗斯方块这个程序一共要实现如下几个功能,开始游戏(F8)、游戏的暂停继续(S)和退出游戏(Q)。其中游戏控制最为主要和重要,它控制着整个游戏的画面和有关数据的操作,是游戏的核心部分。暂停和退出功能做成一体,在退出的提示下不做任何操作即可实现暂停的功能。

程序流程图

根据分析后的程序结构图设计出相应的流程图。俄罗斯方块的内容主要包括游戏开始,画背景和边框,显示分数等级和下一个方块的预览图;根据速度没隔一定时间方块自动下落,当有按键操作时,根据相应按键执行动作,每次动作前要判断是否动作可以执行。下落方块满一行时,消去该行,根据消去行数得到相应分数。分数达到一定程度,等级提升,速度加快。同时可以响应Esc按键,提示是否退出程序。

2

游戏界面

俄罗斯方块的游戏界面包括游戏区域边框、下落方块绘制、右部计分和预览图显示等。

游戏区域边框的绘制比较简单,循环中确定光标的位置输出特定字符,即可完成边框绘制。游戏区方块的绘制,循环从数据数组中依次读出数据,根据读

3

到的数据显示“□”,最后组成方块的形状,完成方块的绘制。计分和预览图部分先画出一个矩形区域,然后控制光标在其中显示分数、等级、预览图和提示信息。

生成方块

本程序中生成的方块有7种形状,每一种方块以shapeindex标记,在程序运行生成方块时,调用shapeindex=rand()%5+1;语句,确定当前要显示的是哪一个方块形状。而在实际运行中,第一次需要调用两次生成方块函数make(),将先产生的赋给游戏当前方块,第二个赋给预览图方块。以后每次产生一个方块,把预览方块赋给当前方块,把新产生的赋给预览方块。

方块变形

俄罗斯方块的特点就在于通过方块的变形拼满整行来消去该行方块从而使游戏可以继续下去,很难想象不能变形的俄罗斯方块是什么样子。而变形的过程就是根据当前方块形状改变方块的相对位置,这样就可以改变方块的形状了。在程序中每当按下“↑”键,程序判断可以变形后,根据当前方块的形状序号shapeindex和变化形状序号changeindex调用相应的方块数值赋给draw()函数,通过刷新重画就可以显示变化后的方块了。

方块显示

以上方块的操作都是数据层面的操作,而真正要在游戏窗口中看到数据的变化,还必须把方块不断的绘制出来。这就是draw()函数的作用。把当前运动的方块对应节点存储在一个4*4数组里,变形和生成方块的过程就是更新该数组数据的过程。然后在draw()函数里检测数组的各个值,并控制光标跳到一定位置,画出“□”组成方块。

4

障碍判断

障碍判断,就是在方块运动中或者变形中判断周围是否有障碍阻碍下落、移位、变形。当方块下落遇到下面有方块或者到达下边界则应停止下落并记录数据,存入背景数据数组。变形时应判断这个变形是否可以进行,如果有障碍则不能变形。例如当方块达到右边界,而若变形则会越过边界,那么这个变形的命令是不应执行的。所有这些判断都由meet()函数进行,根据是否有障碍返回1或0,再由其他函数接收执行相应操作。

消行计分

游戏玩家拼满一行后,程序消去满行,并计分。中当一个方块下落停止后,程序检查方块是否充满了游戏区域,如果是结束游戏。不是,则判断是否构成消行条件,从下落方块的最低点依次向上检查是否可以消行,根据消去行数分数增加。分数达到一定程度,等级提升,分数暂定为每1000升一级。

暂停退出

游戏的友好性在于能考虑用户的需要,随时可以暂停/继续游戏,在不愿继续游戏时退出游戏。本程序可以在用户需要的时候响应Esc按键,提示是否退出游戏,如果不做选择即可暂停游戏,等待选择。

程序调试

经过调试和修改,程序完全实现设计要求,成功模拟了俄罗斯方块的运行过程和游戏效果,只是界面略微简陋,但已从程序层面上实现了游戏,达到了这次实训的要求和目的。程序正常生成方块,根据速度值每隔一定时间自动下落,如有操作按键按下,根据按键实现位移和变形。当方块满一行后,可以消除该行,同时记录分数和等级。按下Esc键红色提示信息正常显示,可以响应Y,N键决定是否退出游戏

软件使用说明

打开俄罗斯方块游戏后,通过控制方向区域的“↑”、“↓”、“←”、“→”来控制,“↑”键代表变形转换,“↓”、“←”、“右”均代表方向键,“Q”代表退出游戏键。若想暂停游戏,可通过“S”键,当初来询问框是,可不做选择,则可达到暂停的效果

5

五、实验结果

六、经验和教训

通过这次课程设计,我收获了很多。首先把所学知识加以利用和巩固,其

6

次在实践中遇到问题去探索和学习,更增加了新知识。在程序设计编写过程中两个类的数据交换是个比较麻烦的过程,这个类的定义过程中要用到另一个类做参数类型,而在后一个类中亦需要第一个类做参数类型,出现了互相调用的情况。

编译提示未定义,只好在两个类外定义函数负责两个类函数的数据交换。实践证明达到了预期的目的,积累了经验。由于程序是用文本窗口模拟的图形,界面比较简陋,如果使用MFC用C++来实现,那么界面将会非常好,只是由于所学知识有限,只有下一步去探索了。

通过这次的学习设计,我发现我还有许许多多的不足的地方,比如c++的程序设计,源代码的书写等等,刚开始我发现我的问题后,十分紧张,感觉很绝望,没有别人的帮助,自己动手设计曾经自己想都没想过的东西,虽然很兴奋,但想想自己无从下手,原来的兴奋劲一下子都没了,很忙然。但是,通过与王老师的交流,经过老师的耐心讲解,我慢慢感到希望又重生了,于是我通过上网查找资料,进图书馆查找书籍等,终于知道了俄罗斯方块游戏的设计概念,终于知道了设计的方法,于是,渐渐地我的游戏设计理念诞生了。

通过几个礼拜的设计,我的游戏渐渐初见成效了,当我把回车键摁下的一刹那,我十分激动,眼前的既熟悉又陌生的游戏出现在了我的面前,看着自己设计的童年时代经常玩的游戏,我感慨万千。通过这次设计,我学会了很多东西,例如通过网络资料来寻求帮助,自己改正错误,加强了我自己的自己动手能力,对今后的学习和生活有很大的帮助,有助于以后的课程设计顺利完成。

七、工作日志

周 月

次 日

1

1

1

9.3

9.4

9.5

实验、实习项目、课程设计名称

确定课题,把题目都看遍

确定课题,把题目都看遍

确定课题,把题目都看遍,最终确定题目

1

1

2

2

2

2

2

9.6

9.7

查找资料,确定方法

查找资料,确定方法

机房

机房

机房

机房

机房

机房

机房

3 需求分析报告

实验、实习、课程设计具体地点

机房

机房

机房

时数

3

3

3

作业或实验、实习报告

初步计划报告

3 程序流程图、代码

3 程序流程图、代码

3 程序流程图、代码

3 程序流程图、代码

9.10 程序修改、改进

9.11 程序修改、改进

9.12 程序编写、调试

9.13 程序联调

9.14

答辩,提交报告

测试程序报告

心得总结

7

八、参考书籍、网址

[1]杨永国.Visual C++ 6.0实用教程.北京:清华大学出版社.2004年

[2]唐俊明.Visual C++ 6.0 编程实例与技巧.北京:高等教育出版,2002

[3]曹向东 张国海 C++语言程序设计 机械工业出版社 2003

[4]浦滨 C++游戏编程从入门到精通 科学出版社 2006

[5]段钢 编著 加密与解密(第三版).电子工业出版社.2009年8月

[6] 潘锦平.软件系统开发技术.西安:西安电子科技大学出版社, 1997九、源程序代码(详细的注释)

#include

#include

#include

#include

#include

class Console

{

public:

Console()

{

hStdOutput = INVALID_HANDLE_VALUE;

hStdError = INVALID_HANDLE_VALUE;

}

bool Open( void )

{

hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE );

hStdError = GetStdHandle( STD_ERROR_HANDLE );

return INVALID_HANDLE_VALUE!=hStdOutput

INVALID_HANDLE_VALUE!=hStdError;

}

inline bool SetTitle( char* title ) // 设置标题

{

return TRUE==SetConsoleTitle(title);

}

bool RemoveCursor( void ) // 去处光标

{

CONSOLE_CURSOR_INFO cci;

if( !GetConsoleCursorInfo( hStdOutput, &cci ) ) return false;

le = false;

if( !SetConsoleCursorInfo( hStdOutput, &cci ) ) return false;

if( !GetConsoleCursorInfo( hStdError, &cci ) ) return false;

le = false;

&&

8

if( !SetConsoleCursorInfo( hStdError, &cci ) ) return false;

return true;

}

bool SetWindowRect( short x, short y ) // 设置窗体尺寸

{

SMALL_RECT wrt = { 0, 0, x, y };

if( !SetConsoleWindowInfo( hStdOutput, TRUE, &wrt ) ) return false;

if( !SetConsoleWindowInfo( hStdError, TRUE, &wrt ) ) return false;

return true;

}

bool SetBufSize( short x, short y ) // 设置缓冲尺寸

{

COORD coord = { x, y };

if( !SetConsoleScreenBufferSize( hStdOutput, coord ) ) return false;

if( !SetConsoleScreenBufferSize( hStdError, coord ) ) return false;

return true;

}

bool GotoXY( short x, short y ) // 移动光标

{

COORD coord = { x, y };

if( !SetConsoleCursorPosition( hStdOutput, coord ) ) return false;

if( !SetConsoleCursorPosition( hStdError, coord ) ) return false;

return true;

}

bool SetColor( WORD color ) // 设置前景色/背景色

{

if( !SetConsoleTextAttribute( hStdOutput, color ) ) return false;

if( !SetConsoleTextAttribute( hStdError, color ) ) return false;

return true;

}

bool OutputString( const char* pstr, size_t len=0 ) // 输出字符串

{

DWORD n = 0;

return TRUE==WriteConsole( hStdOutput, pstr, len?len:strlen(pstr), &n,

NULL );

}

bool OutputStringNoMove( short x, short y, const char* pstr, size_t len=0 ) // 输出字符串

{

COORD coord = { x, y };

DWORD n = 0;

return TRUE==WriteConsoleOutputCharacter( hStdOutput, pstr,

len?len:strlen(pstr), coord, &n );

}

9

private:

HANDLE hStdOutput;

HANDLE hStdError;

};

const char bg[] =

"┏━━━━━━━━━━━┓ "

"┃■■■■■■■■■■■┃ ←↓→↑ "

"┃■■■■■■■■■■■┃ Begin: F8 "

"┃■■■■■■■■■■■┃ "

"┃■■■■■■■■■■■┃ Sleep "

"┃■■■■■■■■■■■┃ Quit "

"┃■■■■■■■■■■■┃ "

"┃■■■■■■■■■■■┃ "

"┃■■■■■■■■■■■┃ NEXT "

"┃■■■■■■■■■■■┃┏━━━━┓"

"┃■■■■■■■■■■■┃┃ ┃"

"┃■■■■■■■■■■■┃┃ ┃"

"┃■■■■■■■■■■■┃┗━━━━┛"

"┃■■■■■■■■■■■┃ LEVEL "

"┃■■■■■■■■■■■┃┏━━━━┓"

"┃■■■■■■■■■■■┃┃ 0┃"

"┃■■■■■■■■■■■┃┗━━━━┛"

"┃■■■■■■■■■■■┃ SCORE "

"┃■■■■■■■■■■■┃┏━━━━┓"

"┃■■■■■■■■■■■┃┃ 00000┃"

"┗━━━━━━━━━━━┛┗━━━━┛";

const char bk[7][4][4][4] =

{

{

{ { 0,1,1,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 1,0,0,0 },{ 1,1,0,0 },{ 0,1,0,0 },{ 0,0,0,0 } },

{ { 0,1,1,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 1,0,0,0 },{ 1,1,0,0 },{ 0,1,0,0 },{ 0,0,0,0 } }

}

,

{

{ { 1,1,0,0 },{ 0,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 0,1,0,0 },{ 1,1,0,0 },{ 1,0,0,0 },{ 0,0,0,0 } },

{ { 1,1,0,0 },{ 0,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 0,1,0,0 },{ 1,1,0,0 },{ 1,0,0,0 },{ 0,0,0,0 } }

}

,

10

{

{ { 1,1,1,0 },{ 1,0,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 1,0,0,0 },{ 1,0,0,0 },{ 1,1,0,0 },{ 0,0,0,0 } },

{ { 0,0,1,0 },{ 1,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 1,1,0,0 },{ 0,1,0,0 },{ 0,1,0,0 },{ 0,0,0,0 } }

}

,

{

{ { 1,1,1,0 },{ 0,0,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 1,1,0,0 },{ 1,0,0,0 },{ 1,0,0,0 },{ 0,0,0,0 } },

{ { 1,0,0,0 },{ 1,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 0,1,0,0 },{ 0,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 } }

}

,

{

{ { 1,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 1,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 1,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 1,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } }

}

,

{

{ { 0,1,0,0 },{ 1,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 0,1,0,0 },{ 1,1,0,0 },{ 0,1,0,0 },{ 0,0,0,0 } },

{ { 1,1,1,0 },{ 0,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 1,0,0,0 },{ 1,1,0,0 },{ 1,0,0,0 },{ 0,0,0,0 } }

}

,

{

{ { 1,1,1,1 },{ 0,0,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 1,0,0,0 },{ 1,0,0,0 },{ 1,0,0,0 },{ 1,0,0,0 } },

{ { 1,1,1,1 },{ 0,0,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },

{ { 1,0,0,0 },{ 1,0,0,0 },{ 1,0,0,0 },{ 1,0,0,0 } }

}

};

const WORD COLOR_A =

FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY; //

运动中的颜色

const WORD COLOR_B = FOREGROUND_RED;

// 固定不动的颜色

const WORD COLOR_C =

FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_BLUE; //

空白处的颜色

int score = 0, level = 0;

11

char data[19][11] = { 0 };

int next = -1;

int x=4, y=-2, c=-1, z=0; // x坐标,坐标,当前方块,方向

Console csl; // 定义控制台对象

void DrawScoreLevel( void ) // 绘制得分

{

char tmp[6];

sprintf( tmp, "%05d", score );

StringNoMove( 31, 19, tmp, 5 );

sprintf( tmp, "%1d", level );

StringNoMove( 35, 15, tmp, 1 );

}

void DrawNext( void ) // 绘制 "next框" 中的图形

{

for( int i=0; i<2; ++i )

{

for( int j=0; j<4; ++j )

{

StringNoMove( 28+j*2, 10+i, bk[next][0][i][j]==0?"2 );

}

}

}

void DrawOver( void ) // 游戏结束

{

StringNoMove( 28, 10, "GAME" );

StringNoMove( 28, 11, "OVER" );

}

void Draw( WORD color )

{

for( int i=0; i<4; ++i )

{

if( y+i<0 || y+i>= 19 ) continue;

for( int j=0; j<4; ++j )

{

if( bk[c][z][i][j] == 1 )

{

or( color );

( 2+x*2+j*2, 1+y+i );

String( "■", 2 );

}

}

}

":"■",

12

}

bool IsFit( int x, int y, int c, int z ) // 给定的x,y,c,z是否可行

{

for( int i=0; i<4; ++i )

{

for( int j=0; j<4; ++j )

{

if( bk[c][z][i][j]==1 )

{

if( y+i < 0 ) continue;

if( y+i>=19 || x+j<0 || x+j>=11 || data[y+i][x+j]==1 ) return false;

}

}

}

return true;

}

void RemoveRow( void ) // 消行

{

const char FULLLINE[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };

int linecount = 0;

for( int i=0; i<19; ++i )

{

if( 0 == memcmp( data[i], FULLLINE, 11 ) )

{

++linecount;

for( int m=0; m<11; ++m )

{

for( int n=i; n>1; --n )

{

data[n][m] = data[n-1][m];

or( data[n][m]==1?COLOR_B:COLOR_C );

( 2+m*2, 1+n );

String( "■", 2 );

}

data[0][m] = 0;

StringNoMove( 2+m*2, 1, "■", 2 );

}

}

}

char data[19][11] = { 0 };

if( linecount == 0 ) return;

int _score = 0;

switch( linecount )

13

{

case 1: _score = 100; break;

case 2: _score = 300; break;

case 3: _score = 700; break;

case 4: _score = 1500;break;

}

score += _score;

if( score > 99999 ) score = 99999;

level = score/10000;

DrawScoreLevel();

}

void MoveTrans( void ) // 逆时针翻转{

if( IsFit( x, y, c, (z+1)%4 ) )

{

Draw( COLOR_C );

z=(z+1)%4;

Draw( COLOR_A );

}

}

void MoveLeft( void ) // 向左移

{

if( IsFit( x-1, y, c, z ) )

{

Draw( COLOR_C );

--x;

Draw( COLOR_A );

}

}

void MoveRight( void ) // 向右移

{

if( IsFit( x+1, y, c, z ) )

{

Draw( COLOR_C );

++x;

Draw( COLOR_A );

}

}

void MoveDown( void ) // 向下移

{

if( IsFit( x, y+1, c, z ) )

14

{

Draw( COLOR_C );

++y;

Draw( COLOR_A );

}

else if( y != -2 ) // 触底

{

Draw( COLOR_B );

for( int i=0; i<4; ++i )

{

if( y+i<0 ) continue;

for( int j=0; j<4; ++j )

{

if( bk[c][z][i][j] == 1 )

{

data[y+i][x+j] = 1;

}

}

}

RemoveRow();

x=4, y=-2, c=next, z=0;

next = rand()%7;

DrawNext();

}

else // 游戏结束

{

DrawOver();

}

}

void MessageDeal( void )

{

int cycle = 9 - level;

for( ; ; )

{

for( int i=0; i

{

if( _kbhit() )

{

switch( _getch() )

{

15

case 'Q':

case 'q': // 退出

return;

break;

case 'S': // 暂停

case 's':

for( ; ; )

{

switch( _getch() )

{

case 'Q':

case 'q': // 退出

return;

case 'S':

case 's':

goto LABLE_CONTINUE;

break;

}

}

LABLE_CONTINUE:

break;

case 0xe0: // ←↓→ ↑

switch( _getch() )

{

case 0x4B: // ←

MoveLeft();

break;

case 0x50: // ↓

MoveDown();

break;

case 0x4d: // →

MoveRight();

break; // ↑ 变形

case 0x48:

MoveTrans();

default:

break;

}

break;

default:

break;

}

16

}

Sleep( 55 );

}

MoveDown();

}

}

int main()

{

();

le( "俄罗斯方块 " ); // 设置标题

Cursor(); // 去处光标

dowRect( 38-1, 21-1 ); // 设置窗体尺寸

Size( 38, 21 ); // 设置缓冲尺寸

StringNoMove( 0,0,bg ); // 输出背景字符

srand( time(0) ); // 设置随机种子

next = rand()%7;

DrawNext();

{

for( char c = (char)_getch(); c!='B'&&c!='b'; c=(char)_getch() ) // 开始 Begin

;}

x=4, y=-2, c=next, z=0;

next = rand()%7;

DrawNext();

MessageDeal();

return 0;

}

17


本文标签: 方块 游戏 程序 是否 下落