1.方块的设计
俄罗斯方块游戏主要包含如下几种类型的方块,如图9-2所示。每种方块都可以变换形状,按照顺时针的方向旋转90°就可以生成一种新的方块。
那么该如何在程序中表示方块呢?显然,即便是包含了变形后的方块,俄罗斯方块中的方块种类也不过十几种。因此,使用穷举的方式,在程序中将各个方块列举出来并不复杂。另外,方块的最大长和宽为4个单位,因此使用一个4×4的数组可以表示整个方块,其中数组中为1的位置表示有方块,其他为0的位置代表为空白。例如,图9-2中的方块图用数组表示,如图9-3所示。
图9-2 俄罗斯方块的方块
图9-3 用数组表示方块
一旦某个方块用数组的形式表示之后,这个方块与数组四周的边距也就确定了,例如,如图9-3所示的方块的4个边距分别是1,1,1,1。边距在方块的移动过程中,用来检测方块与背景和其他方块的碰撞。除了边距之外,还应该给方块定义一个索引属性,这样就可以方便地标识每个方块。有了索引属性之后,就可以定义一个变换数组,数组中可以标识某个索引的方块变形一次后方块的索引。
2.Shape类
有了上面的分析作为基础,我们就可以实现Shape类来表示方块的数据结构。Shape类的成员变量和构造器如下所示。
private int index;
//4×4的二维数组
private int[] data;
//上、下、左、右的margin
private int marginTop;
private int marginRight;
private int marginBottom;
private int marginLeft;
private Shape(final int index, final int[] data, int mt, int mr, int mb,int ml) {
this.index = index;
this.data = data;
this.marginTop = mt;
this.marginRight = mr;
this.marginBottom = mb;
this.marginLeft = ml;
}
Shape类中最重要的数据是由若干Shape对象组成的数组,这个数组代表了所有可能出现的俄罗斯方块。数组中Shape对象的索引值为0~18。Shape[]数组定义如下所示,为了节省篇幅,省略了部分Shape对象。
//Shape数组
public static final Shape[] SHAPES = {
// 0号方块,下一个是0号
//○○
//○○
new Shape(0, new int[] { 0, 0, 0, 0,
0, 1, 1, 0,
0, 1, 1, 0,
0, 0, 0, 0 }, 1, 1, 1, 1),
// 1号方块,下一个是2号
//○○○○
new Shape(1, new int[] { 0, 0, 0, 0,
1, 1, 1, 1,
0, 0, 0, 0,
0, 0, 0, 0 }, 1, 0, 2, 0),
// 2号方块,下一个是1号 www.2cto.com
//○
//○
//○
//○
new Shape(2, new int[] { 0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0 }, 0, 2, 0, 1),
// 3号方块,下一个是4号
//○
//○
//○○
new Shape(3, new int[] { 0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 1, 0,
0, 0, 0, 0 }, 0, 1, 1, 1),
//省略部分数组定义
};
有了Shape[]数组之后,可以很容易地实现方块变形功能。对于每个方块,经过一次变形后所对应的方块是固定的。例如,对于索引号为0的方块,变形后仍然是本身;对于索引号为2的方块,变形后对应的索引号为1。可以定义一个一维数组NEXT[],用来存放Shape[]数组中每个方块变形后的索引数。想获得某个方块变形后的方块索引,直接查询NEXT[]数组即可。索引数组与next()方法如下所示。
//当变换方块形状时,查询此表
private static final int[] NEXT = { 0, 2, 1, 4, 5, 6, 3, 8, 9, 10, 7, 12,13, 14, 11, 16, 15, 18, 17 };
//下一个方块
public Shape next() {
return SHAPES[NEXT[index]];
}