俄罗斯方块实现下降速度使用编程技术:JAVA,C语言,C#。
俄罗斯方块
俄罗斯方块是款非常流行的小游戏,通过七个方块的旋转和位移,构成各种变幻莫测的图案。而游戏者也在不断的叠加和消除中找到乐趣。
从编程的角度来说,这个游戏混合了键盘事件,定时器,随机数,鼠标事件(如果没有菜单,这个也不是必需的),数据结构比较清晰,算法比较灵活,图形是比较简单的矩形,所以很适合刚学习完某种语言的语法又想做点什么的初学者。
本文使用的语言包括:c(Turboc2.0,VC6.0),JAVA(Applet),c#(.NET2003)
编写这个游戏的基本过程是:
首先,把这七个方块用一种数据结构存储起来
其次,在游戏中将这七个方块随机挑选出来并显示在屏幕上,根据键盘事件进行旋转
最后,判断到达底部的方块是简单叠加还是引发消除事件
另外,对这个游戏来说,还有一些记分和过关加速的规则,这些会穿插在上面的内容中讲述。
1存储和旋转七个方块
对于OOP语言(JAVA,C#)来讲,可以写一个数据结构类,处理本游戏中的全部数据。在界面中调用这个类,以实现界面操作与数据结构分离。在后面展示这两个数据结构类的时候,会发现除了语法外,这两个类是基本相同的(把C#的类改了改,再加上键盘操作就成Applet了)。
对于过程化语言(c)来说,界面显示函数与数据结构函数地位相当,所以在形式上稍微分分就行了。
1.1存储七个方块
普通俄罗斯方块游戏中,只有七个基本方块:|,Z,N,L,7,|-,O,如果加上旋转,一共是19种方块(要注意哦,最后一个正方形的方块不能旋转)。这19种方块都可以画在一个4*4的方格中。
于是就有两个方法:一个是只存储七个方块,在游戏运行的时候计算旋转后的方块形状;另一个是将19种方块全部存储起来,在游戏运行的时候计算取其中的哪个方块。
另外,考虑到4*4是16,而一个int正好是16位(TC2是16位,其他的是32位),所以可以把一个方块存储在一个int数据中,取出的时候,判断它的每个bit位,为1处有小方块,为0处是空白。
分别用二维数组和bit的方法来表示这19个方块:
int blocks_shape[19][16]={
0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,/*11*/
0,0,0,0,0,1,0,0,1,1,0,0,1,0,0,0,/*12*/
0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,0,/*21*/
0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,/*22*/
0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,/*31*/
0,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0,/*32*/
0,0,0,0,0,0,1,0,1,1,1,0,0,0,0,0,/*33*/
0,0,0,0,1,1,0,0,0,1,0,0,0,1,0,0,/*34*/
0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,/*41*/
0,0,0,0,0,1,0,0,0,1,0,0,1,1,0,0,/*42*/
0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,0,/*43*/
0,0,0,0,1,1,0,0,1,0,0,0,1,0,0,0,/*44*/
0,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,/*51*/
0,0,0,0,0,1,0,0,1,1,0,0,0,1,0,0,/*52*/
0,0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,/*53*/
0,0,0,0,1,0,0,0,1,1,0,0,1,0,0,0,/*54*/
0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,/*61*/
0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,/*62*/
0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,/*71*/
};
注释里第一个数字表示方块编号,第二个数字表示该方块的第几个变形。
private int[] blocks_shape = {3168,1224,1728,2244,3712,2188,736,3140,2272,1100,3616,3208,1248,1220,228,2248,3840,17476,3264};
显然,第二种方法节约了一些内存。
1.2旋转七个方块
在游戏中计算旋转后的方块形状:
这个其实就是考考二维坐标知识。还是不贴图。先在脑袋里想像一个4*4的方格,上面有个|-。|占三个方格的长度,-占一个。以左下角为原点,从左向右画x轴;从下向上画y轴。如果在游戏中想逆时针旋转方块,就把这个坐标轴顺时针旋转一次。多想几遍得到结论:原来的x坐标与新的y坐标相同,原来的y坐标与新的x坐标方向正好相反。这里的“相反”意思是,原来是4,现在是1,原来是2,现在是3。语言不好表达啦,不过想通的话就很容易理解。
在游戏中计算调用19个方块中的哪个方块:
将19个方块存入数组后,再设一数组告诉程序哪七个下标是七个基本方块。比如:private int[] start_bricks = {0,2,4,8,12,16,18};每次随机产生的方块下标都为start_bricks里的元素。当游戏者旋转方块时,将当前方块下标加1,再判断加1后的下标是否在start_bricks里。如果在的话,说明已经是另一个形状了,舍掉,使用start_bricks里原来的下标。最后根据此下标在blocks_shape数组中取出形状,显示在屏幕上。
看看代码。
private int[] bricks = {3168,1224,1728,2244,3712,2188,736,3140,2272,1100,3616,3208,1248,1220,228,2248,3840,17476,3264};
private int[] start_bricks = {0,2,4,8,12,16,18};
/*mid是当前方块在bricks中的下标*/
for(i=0;i<7;i++)
{
if(mid+1==start_bricks[i])
/*如果mid加1后是start_bricks中的元素*/
/*则还是取start_bricks中原来的元素,即七个基本方块之一,不让它变形*/
mid=start_bricks[i-1];
}
2方块落下的处理
2.1判断方块已经到达底部或是否碰到墙壁
不论是用二维数组还是用bit的方法存储的方块,最终都需要遍历方块的16个小方块中为1的地方是否将会碰到障碍物。“将会”指的是方块如果向左,向右,向下移动后就会碰到障碍物;“障碍物”指的是左右下墙壁和底部已经落下未被消除的小方块。
比较困难的地方,就是把为1的地方转换为相应的坐标,用此坐标判断是否碰到了障碍物。用旋转的办法比较简单,根据旋转的方向转换一下坐标就可以了。如果采用存储19个方块的方法的话,直接看下程序:
用二维数组:
/*遍历16个小方块*/
for(j=0;j<16;j++)
{
/*此处有小方块,判断它是否碰到障碍物*/
if(blocks_shape[i][j]==1)
{
/*a,b是方块左上角坐标,aaa,bbb是新得到的小方块的坐标*/
aaa=a-1+j/4;
bbb=b+j%4;
if( ( aaa==h )||( aa[aaa][bbb]>0 )||( bbb<0 )||( bbb>9 ) )
/*aaa==h 表示该小块已经到达底部;aa[aaa][bbb]>0 表示底部该位置已经有了小方块*/
{
/*返回false,表示会碰到障碍物*/
return false;
}
}
}
用bit与此类似,只是多了一个移位处理:
for(int i=0;i<16;i++)
{
/*num是当前方块的int值,a,b是当前方块的左上角坐标*/
if( (num>>(15-i)&1)!=0 )/*此位为1*/
{
if( (a+i/4>22)||(b+i%4<0)||(b+i%4>11) )/*取得新坐标并加以判断*/
{
flag=false;
break;
}
if( (arr[a+i/4][b+i%4]>0) )/*底部该位置是否有已经落下的小方块*/
{
flag=false;
break;
}
}
}
2.2消除一行
判断到方块落下后,就开始检查是否有满足消除条件的行了。
规则很简单:只要某行被小方块全部填充,该行就被消除,上面的小方块全部下移一行。所以,只要把原来的小方块全部擦去,计算完新方块位置后,再全部画出就可以了,计算的时候,可以遍历整个游戏区域,看有没有可以消除的行。但是在实际编程中就会发现,这样的设计虽然简单,但是计算量大,而且会引起闪烁(因为是整个擦除和重画),效果不是很好。仔细观察这个游戏,就会发现,其实每次消除的时候,只要判断当前方块所在的四行就足够了。在编程的时候,还可以为方块设一个最高高度h,每次刷新的时候,只刷新从h到当前方块底部的行。虽然这样有点烦琐,但是节约了计算时间,减少了重绘面积,效果还是挺明显的。
另外,一些关于这个游戏算法的介绍,有人建议把随机产生下一个方块的计算放在当前方块正在下落的时候,因为那时比较空闲。总之,只要用心去想,一定会写出代码紧凑,资源利用合理,界面舒适的游戏。
温馨提示:内容为网友见解,仅供参考