俄罗斯方块实验报告.docx

上传人:b****5 文档编号:5972267 上传时间:2023-01-02 格式:DOCX 页数:32 大小:101.68KB
下载 相关 举报
俄罗斯方块实验报告.docx_第1页
第1页 / 共32页
俄罗斯方块实验报告.docx_第2页
第2页 / 共32页
俄罗斯方块实验报告.docx_第3页
第3页 / 共32页
俄罗斯方块实验报告.docx_第4页
第4页 / 共32页
俄罗斯方块实验报告.docx_第5页
第5页 / 共32页
点击查看更多>>
下载资源
资源描述

俄罗斯方块实验报告.docx

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

俄罗斯方块实验报告.docx

俄罗斯方块实验报告

程序设计实践报告

(2012/2013学年第2学期)

 

题目:

俄罗斯方块游戏设计

 

专业

学生姓名

班级学号

指导教师

指导单位软件工程系

日期2013.03.27

成绩评定参考标准:

程序设计实践环节评分为五级制,即:

优秀、良好、中等、及格、不及格。

根据程序设计实践过程中学生以下表现评定:

学习态度是否端正、实验课前准备是否充分、是否实现课题要求的功能、算法设计是否合理、程序设计语言使用是否熟练、用户界面设计是否科学、程序设计实践报告完成情况(包括:

内容是否详实、文字表达是否流畅、格式是否符合规范、程序注释是否具体)、答辩表现、考勤等。

简短评语

 

教师签名:

2013年3月27日

评分等级

俄罗斯方块游戏设计

一、课题内容和要求

本程序的主要任务就是编写简单的俄罗斯方块游戏,要求设计比较美观和健全的游戏界面,可以实现方块预览、方块的控制、显示更新、分数更新以及帮助等基本功能,减少程序本身的错误,增强游戏的可操作性。

程序的设计将结合一些有关C语言图形界面设计的内容,该部分是之前没有接触过的,要求利用这次机会,比较简约地了解相关内容及其简单应用。

本程序的实现是选用WindowsXP/7操作系统以及MicrosoftVisualStudio2008C++为编译器,用C语言完成程序设计的实践。

使用Win32控制台应用程序,最终在DOS界面下形成程序的主界面。

二、概要设计

struct//此结构体数表是打印各个方块的依据

{//是该程序设计的灵魂

intvary_x[4];

intvary_y[4];

}vary[]={

{{0,2,4,6},{0,0,0,0}},{{0,0,0,0},{0,-1,-2,-3}},

{{0,2,2,0},{0,0,-1,-1}},{{0,-2,-2,-4},{0,0,-1,-1}},

{{0,0,2,2},{0,-1,-1,-2}},{{0,2,2,4},{0,0,-1,-1}},

{{0,0,-2,-2},{0,-1,-1,-2}},{{0,0,2,4},{0,-1,0,0}},

{{0,0,0,2},{0,-1,-2,-2}},{{0,0,-2,-4},{0,-1,-1,-1}},

{{0,2,2,2},{0,0,-1,-2}},{{0,2,4,4},{0,0,0,-1}},

{{0,-2,-2,-2},{0,0,-1,-2}},{{0,0,2,4},{0,-1,-1,-1}},

{{0,0,0,-2},{0,-1,-2,-2}},{{0,2,4,2},{0,0,0,-1}},

{{0,0,2,0},{0,-1,-1,-2}},{{0,-2,0,2},{0,-1,-1,-1}},

{{0,0,-2,0},{0,-1,-1,-2}},

};

本程序中关于方块的颜色、运动的速度都采用的数组常量的形式,数组的编号分别对应方块形状的编号,这样就使得“方块形状——方块颜色——运动速度”一一对应起来,更直观和方便地实现动态管理。

constintcol[]={11,15,12,12,10,10,9};//控制方块的颜色

constintspeed[]={0,12,9,6,3,1};//控制方块的下落速度

constintnumber[]={0,2,3,5,7,11,15};//对应方块的类型编号

本程序的主体部分是由多个函数的循环多层调用来实现的,主要由以下的九个函数构成:

①voidPrintNext(intpkind)

//该函数控制“下一个图形预览”中的方块样式,是整个程序的“引导者”、“开拓者”,将引导程序的动态实现,将决定着下面几个函数的调用

②voidStraightFall(intdepth)

//该函数控制方块的降落,是在整个程序中发挥着至关重要的作用,是整个动态过程的“纽带”将具体分功能的实现连串起来。

③voidChoiceDirection(int*prev_count)

//该函数实现键盘对小方块的控制,在小方块降落的过程中要时刻“监视”,因此,在②函数中,每当一些变量发生变化时,即要调用该函数

④voidRevolve(int**prev_count)

//该函数控制方块的旋转。

该函数必须依附于③,即每次有键盘的按动时,都要检验是否按了“↑”如果按了,立即执行该函数,实行方块的翻转

⑤voidLevelMove()

//该函数控制方块的移动方向。

该函数类似于第四个函数,也是依附于③

⑥voidGetDepth(int*pdepth)

//该函数可以得到小方块已累积的高度,从而判断小方块是否已经落下。

该函数是一个在多处都需要调用的函数

⑦voidCheckBoundary()

//该函数可以控制、防止方块越过边界。

一旦可能越过边界,则之前的方向控制无效,在方块左右移动、变化形状时都需要调用该函数

⑧voidCheckFull(int*pscore)

//检验是否有一行完全被覆盖,若有一行全部被排满的,即刻整体下移。

在②函数的最后,立刻调用该函数

⑨voidGameOver()

//即当depth为0时,游戏结束,显示动态结束画面。

在②函数的最开始,立刻调用该函数

 

程序流程图大致如下:

 

说明:

该程序的流程图略显复杂,但其实质,依旧是顺序、选择、循环的使用,唯一复杂的就是循环的主体不再是简单的程序语句而是比较复杂的函数,这样就会使得程序的结构比较冗杂,但如果把流程图画出来,就会清晰地看到问题依旧是很简单。

 

四、关键源码

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

{

gotoxy(x+vary[pkind].vary_x[i],y+vary[pkind].vary_y[i]);

puts("▆");

}//该循环是用来依据上述数表打印各种不同的小方块,是本程序中最基本的语句块

voidGameOver()//该函数是用来实现游戏结束后的动画效果

{

color(391);

for(inti=31;i>=0;i--)//用两层循环来实现主界面方块的打印

for(intj=0;j<17;j++)

{

gotoxy(36-j*2,3+i);

puts("▆");

Sleep(5);//延时函数的调用,使得最终的程序界面更有动感

}

color(back);

for(intj=0;j<31;j++)

for(inti=0;i<17;i++)

{

board[i][j].having=0;

gotoxy(4+2*i,j+3);

puts("▆");

Sleep(5);

}

}

voidRevolve(int**prev_count)//控制方块的旋转,该函数是整个程序中最难的一部分代码

{

inti;

before.x=current.x;

before.y=current.y;

(**prev_count)++;

(**prev_count)%=connection[pri_kind].sum;

current.x=current.x+connection[pri_kind].connection_x[**prev_count];

current.y=current.y+connection[pri_kind].connection_y[**prev_count];

CheckBoundary();//调用检验边界函数,防止方块在旋转过程中越出程序界面

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

{

intm=(current.x-4+vary[number[pri_kind]+**prev_count].vary_x[i])/2;

intn=current.y+vary[number[pri_kind]+**prev_count].vary_y[i]-3;

if(board[m][n].having||m>16||m<0||n>31)

{

current.x=current.x-connection[pri_kind].connection_x[**prev_count];

current.y=current.y-connection[pri_kind].connection_y[**prev_count];

revolve=0;

(**prev_count)--;

before.x=current.x;

before.y=current.y;

return;

}

}

color(back);//用背景色把旋转前的方块覆盖

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

{

if(before.y+vary[kind].vary_y[i]>2)

{

gotoxy(before.x+vary[kind].vary_x[i],before.y+vary[kind].vary_y[i]);

puts("");

}

}

kind=number[pri_kind]+**prev_count;

color(col[pri_kind]);//打印旋转后的新的方块

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

{

if(current.y+vary[kind].vary_y[i]>2)

{

gotoxy(current.x+vary[kind].vary_x[i],current.y+vary[kind].vary_y[i]);

puts("▆");

}

}

Sleep(speed[rank]);//根据等级来决定旋转、下落的速度

before.x=current.x;

before.y=current.y;

}

intmain()//主函数是将一切子函数连串在一起的工具

{

srand((unsignedlong)time(0));

gotoxy(5,5);

printf("一切准备就绪,游戏是否开始?

\n");

gotoxy(4,6);

printf("开始游戏请按S键,否则按任意其他键退出。

\n");

charc;

c=getchar();

if(c!

='S')exit(0);

system("CLS");

system("color24");//设置背景颜色

intscore=0;//检验文件是否能够成功打开

FILE*fp=fopen("C:

\\els.txt","r");

if(fp==NULL)

{

fp=fopen("C:

\\els.txt","w");

fprintf(fp,"%d",score);

fclose(fp);

}

PrintGarphy();//打印程序的边界和基本框架以及其中的文字

kind=rand()%7;//随机函数来随机确定方块的形状

intdepth;

while

(1)//是一个无限循环,不断地重复着游戏的过程

{

intpkind=rand()%7;

PrintNext(pkind);

current.x=20;//方块初始出现的位置

current.y=2;

GetDepth(&depth);

if(depth==0)

{

GameOver();//当剩余的高度为0时,游戏结束

score=0;

rank=1;

}

pri_kind=kind;

if(kind==6)kind=15,current.x-=2;

elseif(kind==5)kind=11;

elseif(kind==4)kind=7;

elseif(kind==3)kind=5;

elseif(kind==2)kind=3,current.x+=2;

elseif(kind==1)kind=2;

elsekind=0,current.x-=2;

StraightFall(depth);//调用方块下落函数

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

{

intm=(current.x-4+vary[kind].vary_x[i])/2;

intn=current.y-3+vary[kind].vary_y[i];

board[m][n].having=1;

board[m][n].color=col[pri_kind];

}

CheckFull(&score);//检验是否有满行的出现,若有,整体下呀,并加分

kind=pkind;

}

return0;

}

五、测试数据及其结果分析

 

程序正式运行前所给的提示信息

 

这是程序主界面,由PrintGarphy()打印生成

 

程序主界面

 

 

运行过程中的程序界面

 

 

这是游戏结束时,动态界面的一瞬间截图

 

六、程序设计实践中的关键技术难题以及解决办法

【问题1】在左右方向改变方块的位置时,若一直按住左键或右键,会出现方块部分或整体越过程序界面的现象。

【分析】在程序中层已经定义和调用了函数voidGetdepth()用来计算已经累计的高度,并由depth的值是否为0来判断是否游戏结束,是否方块已经降落。

因此,在程序中还应当定义另一个函数,用来判断是否方块越界,并在其变换和左右移动时调用,以防止方块的越界。

【解决方法】在主程序中定义一个新的函数voidCheckBoundary(),其定义如下:

voidCheckBoundary()

{

inti,n,m;

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

{

m=current.x+vary[kind].vary_x[i];

if(m<4||m>36)

{

current.x+=(m<4?

2:

-2);

i=0;

}

}

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

{

m=(current.x+vary[kind].vary_x[i]-4)/2;

n=current.y+vary[kind].vary_y[i]-3;

if(n<0||n>31||board[m][n].having)

{

if(level==1)

current.x+=2;

elseif(level==2)

current.x-=2;

level=0;

break;

}

}

【问题2】在程序的调用中,发现程序每一次运行,“最高分”都会从0开始记。

即使我把HighScore设为全局变量,依旧是出现该问题。

【分析】程序的每一次运行,都会把每一个变量初始化。

尽管全局变量的作用域是从变量定一开始一直到程序结束,然而,依旧不是“永久”,随着程序的结束而覆灭。

因而,解决的办法就是,及时的把最高分写到计算机的文件中,来求得“永久”的保存。

【解决方法】

在程序中添加以下代码,一旦有新的最高分生成,则立刻写入文件C:

\\els.txt.

FILE*fp=fopen("C:

\\els.txt","r")

fscanf(fp,"%d",&t);

fclose(fp);

if(*pscore>t)

{

fp=fopen("C:

\\els.txt","w");

fprintf(fp,"%d",*pscore);

fclose(fp);

color(43);

gotoxy(51,16);

printf("%d",*pscore);

}

fclose(fp);

七、总结与展望

两周的编程实践,对于自身编程水平是个不小的提高。

实践是认识的来源、实践是认识发展的动力,第一次接触实践课,让我深刻感受到了实践出真知的道理。

因此,在以后的学习中,要树立实践的意识,提高把理论知识上升为实践的能力。

做成事情的前提是有一个合理可行的规划。

在本次程序设计实践的开始阶段,由于编译器的选择问题(即VisualC和TurboC),项目几乎停滞不前,导致许多时间白白浪费。

因此在以后的实践中,要在任务正式开展之前,就要做好充分的规划,避免手忙脚乱。

适当的借鉴和对案例的充分分析加之对书本、互联网等工具的充分使用,才能把一项比较庞大的工程做好。

在这次的程序设计中,我在互联网上找到了至少30个有关俄罗斯方块的例子,其中能够无差错运行的少之又少。

本人花费了很大的功夫仔细地研究了那六七个案例,体悟它们程序实现的方法,找到之间的区别和相同之处。

在阅读每一个程序时,多多少少都会有不完全理解甚至是根本看不懂的地方,这个时候,书本网络就是一个极好的工具。

在两周的时间里,除了思考和面对电脑,剩余的时间几乎就在图书馆里找资料,在追求知识的过程中,也有了许多新的发现。

独立的思考是一切成功的基础。

把一切的感知和灵感都要归咎与自己的思考。

每一个案例的分析都是我独立思考的结果,以后的学习生活中都要善于思考、勤于思考。

分部编写各个函数,并对其逐一进行测试,化大为小,是本次实践的一大收获,是编写较复杂程序的有效方法。

这样有利于条理的清晰,避免错误。

合作与交流是成功的捷径。

与同课题的同学合作甚至是与不同课题同学的交流,都会让我产生思想的火花,在困惑自己许久的问题上带来突破。

在程序中,本人很想再编写一个计时器,用来计量游戏已经运行的时间,几经尝试都没有成功。

初步猜想,可能需要多线程的控制,下一步在这个问题上力求有所突破。

 

源代码:

/*

*Copyright(C)2013,ZhangXu,NanjingUniversityofPostsandTelecommunications

*Allrightsreserved.

*当前版本:

1.2.1

*作者:

ZhangXu

*完成日期:

2013年3月29日

*/

#include

#include

#include

#include

constintback=34;

constintcol[]={11,15,12,12,10,10,9};//用数组记录不同方块的颜色

constintspeed[]={0,12,9,6,3,1};//用数组记录不同的降落速度

constintnumber[]={0,2,3,5,7,11,15};

intkind;

intpri_kind;

intrank=1;//等级状态标记,由此确定下落的速度

intrevolve=0;

intlevel=0;

struct

{

intx;

inty;

}current,before;

struct

{

intx;

inty;

intcolor;

boolhaving;

}board[17][32];

struct

{

intvary_x[4];

intvary_y[4];

}vary[]={

{{0,2,4,6},{0,0,0,0}},{{0,0,0,0},{0,-1,-2,-3}},

{{0,2,2,0},{0,0,-1,-1}},{{0,-2,-2,-4},{0,0,-1,-1}},

{{0,0,2,2},{0,-1,-1,-2}},{{0,2,2,4},{0,0,-1,-1}},

{{0,0,-2,-2},{0,-1,-1,-2}},{{0,0,2,4},{0,-1,0,0}},

{{0,0,0,2},{0,-1,-2,-2}},{{0,0,-2,-4},{0,-1,-1,-1}},

{{0,2,2,2},{0,0,-1,-2}},{{0,2,4,4},{0,0,0,-1}},

{{0,-2,-2,-2},{0,0,-1,-2}},{{0,0,2,4},{0,-1,-1,-1}},

{{0,0,0,-2},{0,-1,-2,-2}},{{0,2,4,2},{0,0,0,-1}},

{{0,0,2,0},{0,-1,-1,-2}},{{0,-2,0,2},{0,-1,-1,-1}},

{{0,0,-2,0},{0,-1,-1,-2}},

};

struct

{

intsum;

intconnection_x[5];

intconnection_y[5];

}connection[]={

{2,{-2,2},{0,1}},

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

{2,{2,-2},{0,0}},

{2,{-2,2},{0,0}},

{4,{-2,0,4,-2},{0,0,-1,1}},

{4,{-4,2,-2,4},{0,0,-1,1}},

{4,{-2,2,0,0},{0,0,0,0}},

};

voidgotoxy(intx,inty)//光标位置确定函数

{

COORDpos;

pos.X=x;

pos.Y=y;

SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);

}

voidcolor(intb)//该函数的调用可以决定输出内容的颜色

{

HANDLEhConsole=GetStdHandle((STD_OUTPUT_HANDLE));

SetConsoleTextAttribute(hConsole,b);

}

voidPrintGarphy()

{

color(1593);//初始化控制台窗口,获取窗口句柄,设置边框颜色

inti,j;

gotoxy(2,2);//确定光标从第二行第二列开始,以下开始打印边框

for(i=0;i<28;i++)

printf("▓");

gotoxy(2,35);

for(i=0;i<28;i++)

printf("▓");

for(i=0;i<33;i++)

{

gotoxy(2,3+i);

printf("▓");

}

for(i=0;i<33;i++)

{

gotoxy(38,3+i);

if(i==7||i==18)

printf("▓▓▓▓▓▓▓▓▓▓");

else

printf("▓");

}

for(i=0;i<33;i++)

{

gotoxy(56,3+i);

printf("▓");

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

当前位置:首页 > 求职职场 > 简历

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

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