俄罗斯方块王安.docx
《俄罗斯方块王安.docx》由会员分享,可在线阅读,更多相关《俄罗斯方块王安.docx(17页珍藏版)》请在冰豆网上搜索。
俄罗斯方块王安
软件应用技术论文
题目:
俄罗斯方块
院系名称:
信息科学与工程学院
专业班级:
计算机科学与技术0702班
学生姓名:
王安学号:
20074140212
2010年12月10
目录
1功能描述2
2系统功能模块2
2.1外部功能图2
2.2主函数功能图3
3模块划分3
3.1整个游戏的流程图3
4算法思想及代码3
4.1俄罗斯方块的构造和储存3
4.2方块的移动3
4.3方块的变形3
4.4方块移动和变形中所做的一些判断3
4.5显示方块3
4.6图形的更新3
4.7其他3
5测试情况3
5.1游戏中的界面3
5.2消行前的界面3
5.3消行后的界面3
5.4游戏结束时的界面3
1功能描述
(1)游戏区域上方不断地出现预定义形状的方块,下坠方块可以通过旋转改变其显示形状,并且不断地往下坠,直到它接触到游戏区域底部或者其它之前已经累叠起的下坠方块。
当一个下坠方块到达游戏区域底部或者接触到其他之前已经累叠起的下坠方块后,其位置则确定下来并占有该空间的位置区域。
(2)当游戏区域的某一行被下坠方块完全填充,则消除该行的所有下坠方块,垒在其上的方块将按照一定的算法掉下代替改行空间。
(3)游戏的结束是以下坠方块的顶部到达游戏区域顶部作为判断依据。
(4)在游戏区域产生一个新的方块的一瞬间,立即产生下一个下坠方块的形状并在提示区域的左上方有预先提示。
同时,在游戏界面的右下方,将显示玩家所得的分数,已经消除的行数以及游戏的级别。
每当游戏玩家成功消除一行方块时,将按照一定的算法计算出所得分数,并修改游戏状态区域的分数和行数的数值。
(5)另外,游戏可提供不同的游戏级别,用户可根据自己的兴趣选择相应的级别。
游戏级别的信息也将显示在游戏状态区域。
(6)本游戏通过键盘方向键操作。
在游戏过程中,当游戏玩家按up键时,则改变方块的形状;按right键时,则方块向右移一个单元;按left键时,则方向块左移一个单元;当按down键时,加速下落。
当方块堆满方框顶是游戏结束,游戏提示玩家是否继续。
(7)从键盘输入的各按键的代码分别为:
ESC0x011b、UP0x4800、DOWN0x5000、LEFT0x4b00、RIGHT0x4d00、SPACE0x3920、Y0x1579、N0x316e。
本游戏通过键盘方向键操作。
在游戏过程中,当游戏玩家按up键时,则改变方块的形状;按right键时,则方块向右移一个单元;按left键时,则方向块左移一个单元;当按down键时,加速下落。
2系统功能模块
2.1外部功能图
图2.1外部功能图
2.2主函数功能图
图2.2主函数功能图
3模块划分
3.1整个游戏的流程图
的等5][ps[t1].co[3]+p.j-1]==0________________________________________________________________________________________________
图3.1整个游戏流程图
游戏开始进入游戏,玩家根据自己的情况选择游戏速度,速度选择过后就开始游戏,由游戏生成两个随机产生的方块,一个是当前方块,一个是下一个将要落下的方块,方块下落的同时玩家按键移动,移动时判断是否越界或到底,再判断消行,到底消行后就回到生成方块的地方继续,还要判断是否到顶,到顶就结束游戏。
4算法思想及代码
4.1俄罗斯方块的构造和储存
定义了一个结构表示形状:
typedefstructshape
{
intco[8];
}shape;
用一个结构体里面包含了一个数组来储存方块的坐标,所有的各种形状都可以放在4x4的格子里。
假定第二列,第四行的格子坐标为(0,0)(如上图中黑块所示),则每个形状的四个方块都可以用4个数对来表示。
坐标x从左向右依次增加,y从上到下依次增加。
表示的时候,组成该形状的四个方块从左到右,从上到下(不一定非要按这个顺序)。
如上面七种形状的第一个用数对来表示就是(-2,0)、(-1,0)、(0,0)、(1,0)。
结构shape中的co就是用来表示这4个数对的。
为了简化程序,用一维数组xy[8]来表示。
co[0]、co[1表示第一个数对,co[2]、co[3]表示第二个数对,依次类推。
七种形状及它们旋转后的变形体一共有19种形状,用一个全局数组表示。
假定旋转的方向是逆时针方向(顺时针方向道理一样)。
4.2方块的移动
表示方块形状的点Co不变,改变动点i,j来实现方块的移动,i为纵坐标,j为横坐标,所以当方块向左移动的时候就是p.j--,当方块向右移动就是p.j++,方块向下移动的时候就是p.i++,在程序中还可以按空格键使方块直接落到底端,用一个循环来做,判断是否接触到底端,如果没有就执行p.i++。
4.3方块的变形
在该程序中,把7中方块的总共19不同变形都用结构体储存下来了,前面四种为一种形状的不同变形,第5种到第六种为一种形状的不同变形,第七种为正方形方块,没有变形,第八到第九为一种形状的不同变形,第十到十一也为一种形状的不同变形,十二到十五和十六到十九分别为两种方块的不同变形。
程序中把方块定义为参数t,当按上键时执行t++,即变化为下一种情况,但是会遇到一个问题,就是假如t为四的时候再做t++就会变成五,但是五不是前面四种形状中的一种变形,不符合要求,所以我用一个判断语句做个判断,即if(t1==3)t1=0;,该语句的意思是,假如t1等于3的时候就把t1赋值为0,不让它继续加导致变成另外一种类型的方块;下面的依次类推,如果t1不等于3、5、6、8、10、14、18等就可以直接执行t++了。
方块变形的程序代码如下:
if(t1==3&&check_change(p,ps[0]))t1=0;
elseif(t1==5&&check_change(p,ps[4]))t1=4;
elseif(t1==6)t1=6
elseif(t1==8&&check_change(p,ps[7]))t1=7;
elseif(t1==10&&check_change(p,ps[9]))t1=9;
elseif(t1==14&&check_change(p,ps[11]))t1=11;
elseif(t1==18&&check_change(p,ps[15]))t1=15;
elseif(check_change(p,ps[t1+1]))t1++;break;
上面代码中有函数check_change(p,ps[t1+1]),该函数是做一个判断,在下面讲会讲到。
4.4方块移动和变形中所做的一些判断
把移动、变形放到一个死循环中,用_kbhit()判断是否有键盘消息,用_getch()接收键盘消息,其中0x50、0x4B、0x4d、0x48分别代表下键、左键、右键和上键,32代表按空格。
接受到从键盘输入的这些消息后,就做该消息所对应的操作。
每做一次就会清屏一次,并显示出来它做过该操后的效果。
以下是代码:
while
(1)
{
for(inti=1;i<=30;i++)
{
if(_kbhit())//_kbhit()判断是否有键盘消息
{
switch(_getch())//接收数据
{
case0x50:
//↓向下移动
if(check_buttom(p,ps[t1]))
{
p.i++;
system("cls");
show(p,ps[t1],ps[t2]);
}
break;
case0x4B:
//←向左移动
if(back[ps[t1].co[0]+p.i][ps[t1].co[1]+p.j-1]==0&&back[ps[t1].co[2]+.i][ps[t1].co[3]+p.j-1]==0&&back[ps[t1].co[4]+p.i][ps[t1].co[5]+p.j-1]==0&&back[ps[t1].co[6]+p.i][ps[t1].co[7]+p.j-1]==0)
{
p.j--;
system("cls");
show(p,ps[t1],ps[t2]);
}
break;
case0x4d:
//→向右移动
if(back[ps[t1].co[0]+p.i][ps[t1].co[1]+p.j+1]==0&&back[ps[t1].co[2]+p.i][ps[t1].co[3]+p.j+1]==0&&back[ps[t1].co[4]+p.i][ps[t1].co[5]+p.j+1]==0&&back[ps[t1].co[6]+p.i][ps[t1].co[7]+p.j+1]==0)
{
p.j++;
system("cls");
show(p,ps[t1],ps[t2]);
}
break;
case0x48:
//↑变换形状
if(t1==3&&check_change(p,ps[0]))t1=0;
elseif(t1==5&&check_change(p,ps[4]))t1=4;
elseif(t1==6)t1=6;
elseif(t1==8&&check_change(p,ps[7]))t1=7;
elseif(t1==10&&check_change(p,ps[9]))t1=9;
elseif(t1==14&&check_change(p,ps[11]))t1=11;
elseif(t1==18&&check_change(p,ps[15]))t1=15;
elseif(check_change(p,ps[t1+1]))t1++;
break;
case32:
//空格直接降到底端
while(check_buttom(p,ps[t1]))
{
p.i++;
}
default:
break;
}
}
}
其中还需要注意的就是移动的时候要判断是否到达边缘,到达边缘之后就不能继续移动了,上面代码中判断到达边缘的语句有:
back[ps[t1].co[0]+p.i][ps[t1].co[1]+p.j-1]==0&&back[ps[t1].co[2]+p.i][ps[t1].co[3]+p.j-1]==0&&back[ps[t1].co[4]+p.i][ps[t1].co[5]+p.j-1]==0&&back[ps[t1].co[6]+p.i][ps[t1].co[7]+p.j–1等;其他大概都差不多。
还有一个问题不容易被察觉,就是方块贴到方框边缘后变形的问题,在这儿也要做一个判断才行,要判断该形状变形后的形状是否超越过了边缘,如果没有做这个判断的话,方块变形后就会挂在方框边缘上,因为方框的边缘是赋值为1的,变形后移动的时候它会判断背景是否为1,如果为1的话便不会再移动了,所以图形块就挂在上面移动不了了。
关于判断变形后的图形是否越界的函数是:
intcheck_change(pointp,shapes){
if(back[s.co[0]+p.i][s.co[1]+p.j]==1||back[s.co[2]+p.i][s.co[3]+p.j]==1||back[s.co[4]+p.i][s.co[5]+p.j]==1||back[s.co[6]+p.i][s.co[7]+p.j]==1)
{
return0;
}
elsereturn1;}
其中传递进来的一个参数是t1+1,即当前图形的下一个形状,if中的条件就是判断当前图形的下一个形状是否挨到边缘,是的话就返回0,没有就返回1。
接下来便是判断是否到底,然后再进行输出或刷新。
4.5显示方块
用一个双循环来输出■,用s1.co[0]+p.i==i&&s1.co[1]+p.j==j来判断,即用当前方块的坐标加上动点的坐标的值是否与当前的行或列相等,相等的时候就输出一个■,不相等的时候输出空格。
在外循环内写两个内循环,一个用于现实当前正在下落的方块,另一个用来显示下一个将要显示的方块。
第二的内循环把jj初始化为-1是为了不让其与边框贴太近导致看不清,而用s2.co[0]+4==i和s2.co[1]+1==jj中间加了4和1是为了让上下左右都有一定的空间让游戏界面更加美观,在这个循环里做一个判断然后显示分数和游戏级别。
程序源代码如下:
voidshow(pointp,shapes1,shapes2)//显示方块
{
for(inti=0;i<=20;cout<{
for(intj=0;j<=14;j++)
{
if(s1.co[0]+p.i==i&&s1.co[1]+p.j==j)
cout<<"■";
elseif(s1.co[2]+p.i==i&&s1.co[3]+p.j==j)
cout<<"■";
elseif(s1.co[4]+p.i==i&&s1.co[5]+p.j==j)
cout<<"■";
elseif(s1.co[6]+p.i==i&&s1.co[7]+p.j==j)
cout<<"■";
elseif(back[i][j]==1)
cout<<"■";
else
cout<<"";
}
if(i==2)cout<<"下一个图形";
for(intjj=-1;jj<=4;jj++)
{
if(s2.co[0]+4==i&&s2.co[1]+1==jj)
cout<<"■";
elseif(s2.co[2]+4==i&&s2.co[3]+1==jj)
cout<<"■";
elseif(s2.co[4]+4==i&&s2.co[5]+1==jj)
cout<<"■";
elseif(s2.co[6]+4==i&&s2.co[7]+1==jj)
cout<<"■";
elseif(i==8&&jj==1)cout<<"分数:
"<elseif(i==10&&jj==1)cout<<"level"<elsecout<<"";
}
}
}
4.6图形的更新
方块每移动一个坐标的位置,图形都会更新一次,其中的循环和显示方块中的循环一样,只是执行的内容改变了,在判断方块的坐标加上动点的坐标是否等于背景的坐标后,就把该坐标的值赋值为一,当下一个图形移动到该图形处由于该图形的背景坐标为一,所以图形便会停止移动了。
该工作做完后便判断是否要消行,判断消行也要用一个双循环,在外循环中定义一个标志flag并赋值为1,在内循环中判断,看是否有一个坐标处为0,如果有一个为0的话,就把flag的值改为0;该循环结束后就进行下一个内循环,先判断flag的值,值为1的话就进行消行操作,即把上面一行的值赋给下面一行。
在更新这个函数的开始定义一个变量,用来记消去的行数,如果一次消去一行,分数就加上10,一次消去两行,分数加上30,一次消去三行,分数加上50,消去四行分数就加上100。
在该函数中还要判断游戏是否结束,即是检查第一列的没行的开始一个坐标是否等于1,如果等于1方块累积到了顶端,游戏便结束了,在方框底端输出“Gameover!
!
!
”字样。
该部分代码如下:
voidupdate(pointp,shapes)//更新
{
inti,j,count=0;
for(i=0;i<=19;i++)
for(j=1;j<=13;j++)
{
if(s.co[0]+p.i==i&&s.co[1]+p.j==j)
back[i][j]=1;
elseif(s.co[2]+p.i==i&&s.co[3]+p.j==j)
back[i][j]=1;
elseif(s.co[4]+p.i==i&&s.co[5]+p.j==j)
back[i][j]=1;
elseif(s.co[6]+p.i==i&&s.co[7]+p.j==j)
back[i][j]=1;
}
elseif(count==4)
score+=100;
for(i=0;i<=19;i++)//消行
{
intflag=1;
for(j=1;j<=13;j++)
{
if(back[i][j]!
=1)
{
flag=0;
break;
}
}
if(flag==1)
{
intk=i;
for(k=i;k>=1;k--)
{
for(j=1;j<=13;j++)
back[k][j]=back[k-1][j];//把上面一行赋值给下面一行
}
count++;
}
}
if(count==1)
score+=10;
elseif(count==2)
score+=30;
elseif(count==3)
score+=50;
elseif(count==4)
score+=100;
for(j=1;j<=13;j++)
if(back[0][j]==1)
{
cout<<"Gameover!
\n";
exit(0);
}
}
4.7其他
在程序中的方块是随机产生的,用srand(time(NULL))做随机种子,然后用rand%19随机选取十九中图形的一种。
程序中的system(“cls”)是用于清屏。
在开始还有一个chioce函数,是用来游戏玩家根据自己的需要选择游戏级别的,在选择中要按照游戏中提示的来选择,否则将按默认的速度来处理。
5测试情况
5.1游戏中的界面
图5.1游戏开始时的界面
5.2消行前的界面
图5.2消行前的界面
5.3消行后的界面
5.3消行后的界面
5.4游戏结束时的界面
图5.4结束时的界面
7、总结
在这次试验中,课设题目要求不仅要求对课本知识有较深刻的了解,同时要求程序设计者有较强的思维和动手能力。
在写程序的过程中遇到很多麻烦,最开始都不知道怎么下笔,理清思路后才开始慢慢能自己动手了,当方块能自己移动的时候又出现问题了,就是把方块移动到右边的时候再变形的话下个方块就容易挂在边上,最后写了一个函数,判断当前方块变形后的下一个方块是否越界,在变形的时候便调用这个函数就可以了。
做课程设计不仅让我修补了以前学习的漏洞,也让我知道一个道理:
编程需要兴趣和实际动手。
这应该可以借鉴在老师的教学工作上。
创新思维至关重要,这不仅能让我们写出精简的代码,也有助于开发出高效的程序。
参考文献
[1]陆宗骐.C/C++图像处理编程.北京:
清华大学出版社,2005
[2]钱能.C++程序设计教程.北京:
清华大学出版社,2005
[3]吕凤翥.C++语言基础教程题解与上机指导.北京:
清华大学出版社,2006
[4]杨祥金等.Windows程序设计教程.北京:
清华大学出版社,2007
[5]PeterNorton,RobMcGregor著.MFC开发Windows95/NT4应用程序.孙凤英,魏军,叙京等译.北京:
清华大学出版社,1988