正在加载...
2009-1
2

  俄罗斯方块设计详解(上)
  
  俄罗斯方块要解决这些问题:
  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开始检测工作区数组四行,如果这一行全部在位,则将这一行删除,计分。
  消行后,需要讲上面的行移动下来,移动的算法下回与旋转一块儿讲,休息一会儿,休息一会儿。。。。
  
  魏晋遗疯原创,转载请注明出处
  

: http://www.daaaxiang.com/index.php/archives/11

本文相关评论 - 1条评论都没有呢

还没有任何评论。