俄罗斯方块设计详解(上)
俄罗斯方块要解决这些问题:
1.如何旋转
2.如何生成不同的方块
3.如何控制方块的移动
4.如何检测行满
5.如何消除某行
6.如何检测方块已经下落到底了
7.如何判断游戏死亡
8.数据如何存储
9.特殊情况的处理,比如如何检测方块在边上不能翻转的情况
所谓“兵马未动,粮草先行”,首先要想好数据结构。其中关键部分只有两个,一个是要操作的方块数据,一个是工作区数据。首先来说,数据与显示应该分离开来,即方块以何种颜色显示在工作区,如何检测碰撞等等与数据有关,不要与显示有任何关系,方块的移动产生的坐标变化也不要用实际的像素,而要用数组的x,y偏移量。那么,用两个伪二维数组来分别存储正在操作的方块和整个工作区的方块(工作区指面板已经放入的、未放入的),用下图表示:

第一个问题:存储
方块用4×4的数组表示,每一个元素的值有三个,分别表示:是否在位,颜色,种类。种类是为了以后扩充准备,比如可以随机在某个小方块上产生一个道具,而这个道具有特殊作用,就需要在方块里面做标志。
工作区数组用10×25的二位数组标志,每个元素的值与方块的数组一致,也是有三位,即是否在位,颜色以及方块种类。
对于用4个小方块组成的方块组合,不考虑变身的话,总共有7种,分别为L型,T型,一型,N型,田型,J型以及倒N型。我本来考虑在程序运行时自动生成所有变种,但是没研究出算法来,所以仍然采用提前固定好,存储在数组里面。比如L型的以如下数据存储:

上面的存储也可以稍微优化一下,比如一个方块里面的小方块基本上颜色都是一样的,所以red数据可以一个大数组存储一个就行,另外为了以后的旋转以及检测碰撞的方便,还要为这个数组存储一个最大旋转范围,比如L型的最多只会占三行三列,则存size=3,田字型的存为2。
而工作区的数组则与方块一致,只不过维度要更大一些,比如10×25维度的。
上面就是这个游戏里的两个大数组。另外可能还需要两个变量存储当前操作的方块所在的行列数:curPosX, curPosY
第二个问题:移动
移动时要做的是将curPosX和curPosY在移动方向上加1,并且擦除刚才的方块,然后在新位置重新绘制。每种语言的绘制方式都不一样,这里就不贴代码了。
移动时要碰到的问题是,如何判断不能移动的情况。不能移动有两种情况:
1.方块的数组超出工作区边界,即左边或者右边
2.方块的数组碰到已经下落的那些方块
超出边界的判断比较简单,在真正移动之前,先用临时变量存储一下加值后的curPosX,curPosY, 然后用一个循环,判断4×4方块里的在位的方块是否有curPosX与方块相对于4×4方块左上角的坐标是否小于0或者大于工作区的宽度,如果有则认为越界,不能进行移动。
碰到已经下落的方块也不难,只要从curPosY向下遍历最多4行工作区数组,如果发现4×4数组的在位的方块位置上工作区数组也在位,则说明不能再下落了。
第三个问题:判断下落到底了
在程序开始时,会启动一个定时器每隔1秒(或更短,表示下落速度)发一个向下移动的指令,如果命令是下落(而不是向左和向右),而且不能移动了,则要进入冷冻程序,即固定正在下落的方块,产生新的方块。这块儿有些逻辑,还是贴下代码:
请带这这两个特殊请款查看代码:
1.方块在下落到底后并不是马上固定,在一小段时间内还可以移动
2.虽然已经不能往下移动了,但由于还可以左右移动,如果未固定前左右移动后又可以下落了,这种情况则要取消固定程序
set tmpX 0
set tmpY 0
if {$act == “d”} {
#向下
set tmpY 1
} elseif {$act == “l”} {
#向左
set tmpX -1
} elseif {$act == “r”} {
#向右
set tmpX 1
} else {
;
}
if {[canMove [array get CurBlock] $tmpX $tmpY]} {
if {$freezeTimerID != -1 && [canMove [array get CurBlock] 0 1] == 1} {
#删除冻住的Tmier
after cancel $freezeTimerID
after cancel FreezeBlock
set freezeTimerID -1
}
incr curPosX $tmpX
incr curPosY $tmpY
$C delete curblock
draw_blocks $C [array get CurBlock] $curPosX $curPosY red “curblock”
} else {
#如果是往下不能动,则一秒种后冷冻,并产生下一个方块
if {$tmpY == 1 && $freezeTimerID == -1 &&[canMove [array get CurBlock] 0 1] == 0} {
set freezeTimerID [after $freezetime FreezeBlock ]
}
}
其中canMove函数是判断当前方块是否可以移动的,当方块不可以向下移动时,需要重点关注,个人觉得这块儿是最难处理的。
第四个问题:如何检测行满
当走入固定方块的流程后,则需要检测是否有可消除的行了。检测比较简单,由于受影响的行不会超过4行,所以只需要从curPosY开始检测工作区数组四行,如果这一行全部在位,则将这一行删除,计分。
消行后,需要讲上面的行移动下来,移动的算法下回与旋转一块儿讲,休息一会儿,休息一会儿。。。。
魏晋遗疯原创,转载请注明出处






还没有任何评论。