GitHub地址: https://github.com/weijifen/AndroidTetris
移动方向
移动方向包括左移,右移和下移。
移动方块实际是对position向量做改变,改变之后使用handler进行渲染:
/* 将正在下落的方块与停止下落的方块区别对待 */
for ( int i = 0; i < ySize; i++ )
{
if ( allBlock[i] == 0 )
{
for ( int j = 0; j < xSize; j++ )
{
blockList.set( i * xSize + j, 0 );
}
} else {
for ( int j = 0; j < xSize; j++ )
{
blockList.set( i * xSize + j, blockColor[i][j] );
}
}
}
for ( int i = 3; i >= 0; i-- )
{
int line = i + position[0];
if ( line >= 0 && StateFang.shape[rand][i] != 0 )
{
for ( int j = 0; j < xSize; j++ )
{
if ( ( (1 << j) & (leftMath( StateFang.shape[rand][i], position[1] ) ) ) != 0 )
{
blockList.set( line * xSize + j, randColor );
}
}
}
}
这里将能否进行这些动作的判断放在改变posiotion之前判断。
左移
在判断该位置符合左移的条件就改变position数组,然后通过handler.sendEmptyMessage(1)
进行渲染。
如何判断是否可以左移?
- 判断是否越界
- 判断是否重复
判断是否越界
本游戏方块的左移实际是二进制的右移,我们可以根据二进制右移的特性来做判断。
右移的时候低位移出,高位补0,左移的时候高位移出,低位补0.
所以我们可以通过先右移再左移,如果结果和原数字相同,则说明可以右移,如果和原数字不相同,则说明不可以右移。
如
(
1010
)
2
(1010)_2
(1010)2右移一位为
(
101
)
2
(101)_2
(101)2,再左移一位为
(
1010
)
2
(1010)_2
(1010)2.
(
1011
)
2
(1011)_2
(1011)2右移一位为
(
101
)
2
(101)_2
(101)2,再左移一位为
(
1010
)
2
(1010)_2
(1010)2.
判断是否与其他方块重复
如果没有越界,则通过将可以运动的方块与已经固定的方块进行按位与操作,如果结果为0,则说明没有重复,如果结果不为0,则说明有重复。
leftMove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//左移实际是数字的右移
//左移需要判断是否能够左移
for (int i=3;i>=0;i--) {
if ((((leftMath(StateFang.shape[rand][i] ,position[1])) >> 1) << 1)
!= (leftMath(StateFang.shape[rand][i] ,position[1]))) {
// 如果越界了
return;
}
}
for (int i=3;i>=0;i--) {
int line = i + position[0];
if (line >= 0 && StateFang.shape[rand][i]!=0) {
if ((allBlock[line] & (leftMath(StateFang.shape[rand][i], position[1]) >> 1)) != 0) {
return;
}
}
}
position[1]--;
handler.sendEmptyMessage(1);
}
});
右移
判断是否越界
通过判断右移之后的二进制数组是否大于10位二进制可以表示的最大数字,如果大于,则说明右移之后越界,不可以右移,如果不大于,说明可以右移。
判断是否与其他方块重复
与左移一样。
rightMove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for (int i=3;i>=0;i--) {
if (((leftMath(StateFang.shape[rand][i], position[1])) << 1) > 0x3ff
) {
// 如果越界了
return;
}
}
for (int i=3;i>=0;i--) {
int line = i + position[0];
if (line >= 0 && StateFang.shape[rand][i]!=0) {
if ((allBlock[line] & (leftMath(StateFang.shape[rand][i], position[1]) << 1)) != 0) {
return;
}
}
}
position[1]++;
handler.sendEmptyMessage(1);
}
});
旋转设计
反L形方块表示方法如下:
{0x8, 0x8, 0xc, 0x0}
表示为二进制为:
1 0 0 0
1 0 0 0
1 1 0 0
0 0 0 0
但是方格以左边为低位更方便,所以变为:
0 0 0 1
0 0 0 1
0 0 1 1
0 0 0 0
所以这是一个形状为反L的方块。
那么方块的旋转应该如何表示?
例如反L形的逆时针旋转之后为shape[1]({0xe, 0x8, 0, 0}
):
0 1 1 1
0 0 0 1
0 0 0 0
0 0 0 0
这样我们就可以定义一个数组nextShape,其下标i表示第i个方块形状,值表示旋转之后是第几个方块形状。
public static int[] nextShape = new int[] {
1, 2, 3, 0, 5, 6, 7, 4, 9, 8, 11, 10, 13, 14, 15, 12, 17, 16, 18
};
这样需要旋转的时候我们就可以根据数组的下标的下标(shape[nextShape[i]]
)找到旋转之后的形状。
另外需要判断旋转之后是否越界或者与已经固定的方块重复,如果出现这些情况,那就不能旋转。
方法是先旋转,再检查。
rotateMove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int nextRotate = StateFang.nextShape[rand];
for (int i=3;i>=0;i--) {
int line = i + position[0];
// 检查是否越界
if (leftMath(StateFang.shape[nextRotate][i] ,position[1]) > 0x3ff){
//右边界
return;
}else if(StateFang.shape[nextRotate][i]>0 && line>=ySize){
//下边界
return;
} else if (leftMath(leftMath(StateFang.shape[nextRotate][i], position[1]),
-position[1]) != StateFang.shape[nextRotate][i]) {
return;
}
//检查是否与其他方块重合
else if (line>0 && line<ySize &&
(leftMath(StateFang.shape[nextRotate][i], position[1]) & allBlock[line]) != 0) {
return;
}
}
rand = nextRotate;
handler.sendEmptyMessage(1);
}
});
值得一提的是,检查是否超越左边界的方法和检查左移的方法类似,先将方块移到指定位置,再反向移动,检查是否和原来数字相同。
下移
目标:一次下移到不能再下移的为位置。
计算运动的方块的每一行可以下移的最大距离,再找出这些最大距离里面的最小值。
如何找出运动的方块的某一行可以下移的最大距离
这一行的每一列检测是否有方块,如果有方块,则检测该列下方最近的方块(检查下一行是否有方块,再下一行是否有方块,直到边界处)。记录最大的移动量。
通过该移动量改变position数组,使用handler.sendEmptyMessage(0)
渲染游戏界面。
downMove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int down=1<<10;
for(int i=3;i>=0;i--) {
int line = i + position[0];
if (line >= 0 && StateFang.shape[rand][i] != 0) {
down = Math.min(down, ySize - line - 1);
for (int j=0;j<xSize;j++) {
if (((1 << j)& (leftMath(StateFang.shape[rand][i] ,position[1])))!=0) {
for(int k=0;k+line<ySize;k++) {
if (blockColor[k + line][j] > 0) {
down = Math.min(down, k-1);
break;
}
}
}
}
}
}
if (down <= 0 || down==(1<<10)) {
return;
} else {
position[0] += down;
handler.sendEmptyMessage(0);
}
}
});
下落速度设计
该游戏可以选择下落速度。
在selectActivity中:
@Override
public void onClick(View v) {
Intent intent = new Intent(SelectActivity.this, MainActivity.class);
switch (v.getId()) {
case R.id.grade1:
intent.putExtra("grade", 1);
break;
case R.id.grade2:
intent.putExtra("grade", 2);
break;
case R.id.grade3:
intent.putExtra("grade", 3);
break;
case R.id.grade4:
intent.putExtra("grade", 4);
break;
case R.id.grade5:
intent.putExtra("grade", 5);
break;
default:
break;
}
startActivity(intent);
}
使用intent传输:
Intent intent = getIntent();
grade = intent.getIntExtra( "grade", 3 );
switch ( grade )
{
case 1:
timeInterval = 1200;
break;
case 2:
timeInterval = 1000;
break;
case 3:
timeInterval = 800;
break;
case 4:
timeInterval = 600;
break;
case 5:
timeInterval = 400;
break;
default:
break;
}
该游戏使用定时器,每过一个 timeInterval 时间,自动下落一格,所以通过改变timeInterval控制下落速度。
感兴趣的朋友请继续阅读 Android使用GridView实现俄罗斯方块(附源码)(四)