Android使用GridView实现俄罗斯方块(附源码)(四)

GitHub地址:
https://github.com/weijifen/AndroidTetris

行满判定与处理

如果方块的移动是下移,则handler.sendEmptyMessage(0),
这是先判断可否下移.
如何判断可否向下移动一位?
先向下移动一位,如果没有出界或者与已经固定的方块重复,则渲染移动之后的结果,如果出界或者重复,则移回原来的位置,停止移动,将移动方块加入固定的方块的数组,进行行满判定,如果行满则消除,重新产生新的方块,并下落。
《Android使用GridView实现俄罗斯方块(附源码)(四)》

/* * 如果处于等时间间隔下落状态 * 检测是否可以按要求移动 */
position[0]++;
for ( int i = 3; i >= 0; i-- )
{
    int line = i + position[0];
    if ( line >= 0 && StateFang.shape[rand][i] != 0 )
    {
        /* 如果到底了,或者下面有方块 */
        if ( line >= ySize ||
             ( (allBlock[line] & (leftMath( StateFang.shape[rand][i], position[1] ) ) ) != 0)
             )
        {
            canMove = false;
            break;
        }
    }
}
if ( !canMove )
{
    position[0]--;
    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 );
                }
            }
        }
    }
    stopDown();
} else {
    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 );
                }
            }
        }
    }
}

如何判断一行是否已经满了?
因为已经固定的方块是用二进制表示的,所以只要10个二进制位全部为1则说明该行已经满了,即:

if (allBlock[i] == 0x3ff) {
    // 该行已经满了
}

因为行满消除之后上面的方块会往下掉,所以我们从最后一行往上遍历,如果有行满,则按照从下往上的顺序,一行一行往下掉,这一次掉落完成之后,最上面的行赋值0,然后进一步检测该行是否已满,如果没有满,则继续检测下一行,如果满了,继续掉落。

for ( int i = ySize - 1; i >= 0; )
{
    if ( allBlock[i] == 0x3ff )
    {
        score++;
        scoreTextView.setText( "分数:" + score );
        for ( int j = i - 1; j >= 0; j-- )
        {
            allBlock[j + 1] = allBlock[j];
            for ( int k = 0; k < xSize; k++ )
            {
                blockColor[j + 1][k] = blockColor[j][k];
            }
        }
        allBlock[0] = 0;
        for ( int j = 0; j < xSize; j++ )
        {
            blockColor[0][j] = 0;
        }
    } else {
        i--;
    }
}

检测到不能继续往下移动之后:

void stopDown()
{
    /* 写入、消除、重置 */
    for ( int i = 3; i >= 0; i-- )
    {
        int line = i + position[0];
        if ( line >= 0 && StateFang.shape[rand][i] != 0 )
        {
            allBlock[line] += (leftMath( StateFang.shape[rand][i], position[1] ) );
            for ( int j = 0; j < xSize; j++ )
            {
                if ( ( (1 << j) & (leftMath( StateFang.shape[rand][i], position[1] ) ) ) != 0 )
                {
                    blockColor[line][j] = randColor;
                }
            }
        }
    }
    for ( int i = ySize - 1; i >= 0; )
    {
        if ( allBlock[i] == 0x3ff )
        {
            score++;
            scoreTextView.setText( "分数:" + score );
            for ( int j = i - 1; j >= 0; j-- )
            {
                allBlock[j + 1] = allBlock[j];
                for ( int k = 0; k < xSize; k++ )
                {
                    blockColor[j + 1][k] = blockColor[j][k];
                }
            }
            allBlock[0] = 0;
            for ( int j = 0; j < xSize; j++ )
            {
                blockColor[0][j] = 0;
            }
        } else {
            i--;
        }
    }
    if ( allBlock[0] != 0 )
    {
        if ( score > highestScore )
        {
            cacheUtils.getValue( "highestScore" + grade, score + "" );
            highestScore = score;
            maxScoreTextView.setText( "最高分:" + highestScore );
            scoreTextView.setText( "分数:" + score );
        }

        gameOver();
    }
    rand        = nextRand;
    position[0] = StateFang.initPosition[rand][1];
    position[1] = StateFang.initPosition[rand][0];
    randColor   = nextRandColor;

    nextRand    = random.nextInt( 19 );
    nextRandColor   = random.nextInt( 5 ) + 1;
    nextTetrisShow();
    Log.i( TAG, rand + "" );
}

分数、等级显示与存储

每一个等级的最高分都需要存储并显示。
我们使用SharedPreferences实现分数的存储和显示,将它封装成为CacheUtils:

public class CacheUtils {
    String fileName;
    SharedPreferences preferences;
    SharedPreferences.Editor editor;

    public CacheUtils(Context context, String fileName)//fileName是缓存的唯一标识
    {
        this.fileName=fileName;
// 数据只能被本应用程序读写
        preferences=context.getSharedPreferences(this.fileName,Context.MODE_PRIVATE);
        editor=preferences.edit();
    }

    /** * 向Cache存入指定key对应的数据 * 其中value可以是String、boolean、float、int、long等各种基本类型的值 * @param key * @param value */
    public void putValue(String key,String value)
    {
        editor.putString(key,value);
// 提交所做的修改
        editor.commit();
    }
    public void putValue(String key,int value)
    {
        editor.putInt(key,value);
// 提交所做的修改
        editor.commit();
    }
    public void putValue(String key,List<String> value)
    {
        editor.putStringSet(key,(Set<String>) value);
// 提交所做的修改
        editor.commit();
    }
    /** * 向Cache存入指定key对应的数据 * 其中value可以是String、boolean、float、int、long等各种基本类型的值 * @param key * @param value */
    public void putValue(String key,boolean value)
    {
        editor.putBoolean(key,value);
// 提交所做的修改
        editor.commit();
    }

    /** * 获取Cache数据里指定key对应的value。如果key不存在,则返回默认值def。 * @param key * @param def * @return */
    public String getValue(String key,String def)
    {
        return preferences.getString(key,def);
    }

    /** * 清空Cache里所有数据 */
    public void clearCache()
    {
        editor.clear();
// 保存修改
        editor.commit();
    }
}

在Activity的onCreate方法里面取出并显示:

cacheUtils = new CacheUtils( MainActivity.this, "UserInfo" );
String maxString = "";
try {
    maxString = cacheUtils.getValue( "highestScore" + grade, String.valueOf( 0 ) );
} catch ( Exception e ) {
    Log.e( TAG, e.toString() );
}


try {
    highestScore = Integer.parseInt( maxString.toString() );
} catch ( NumberFormatException e ) {
    highestScore = 0;
}
maxScoreTextView.setText("最高分:" + highestScore);

本局的分数累加并且显示:
每行满消除一行便累加1,在本局游戏结束的时候,判断当前分数和历史最高分比较,如果大于历史最高分,就写入到SharedPreferences中。

if ( score > highestScore )
{
    cacheUtils.getValue( "highestScore" + grade, score + "" );
    highestScore = score;
    maxScoreTextView.setText( "最高分:" + highestScore );
    scoreTextView.setText( "分数:" + score );
}

暂停

暂停的时候“左移”,“右移”,“旋转”按钮都不能使用,而且还要能够点击继续。
我们在点击暂停的时候取消定时器,设置动作按钮不可用,点击“继续”时候重置定时器,设置动作按钮可用。

private void pause()
{
    isPause = !isPause;
    if ( isPause )
    {
        stopTimer();
        pausebtn.setText( "继续" );
        leftMove.setEnabled( false );
        rightMove.setEnabled( false );
        rotateMove.setEnabled( false );
        downMove.setEnabled( false );
    } else {
        startTimer();
        pausebtn.setText( "暂停" );
        leftMove.setEnabled( true );
        rightMove.setEnabled( true );
        rotateMove.setEnabled( true );
        downMove.setEnabled( true );
    }
}
// 停止定时器
    private void stopTimer(){
        if(timer != null){
            timer.cancel();
            // 一定设置为null,否则定时器不会被回收
            timer = null;
        }
    }
    private void startTimer() {
        if (timer == null) {
            timer = new Timer();
        }
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                handler.sendEmptyMessage(0);
            }
        },0,timeInterval);
    }

附:
感兴趣的朋友可以回顾Android使用GridView实现俄罗斯方块(附源码)(一)

点赞