GitHub地址:
https://github.com/weijifen/AndroidTetris
行满判定与处理
如果方块的移动是下移,则handler.sendEmptyMessage(0)
,
这是先判断可否下移.
如何判断可否向下移动一位?
先向下移动一位,如果没有出界或者与已经固定的方块重复,则渲染移动之后的结果,如果出界或者重复,则移回原来的位置,停止移动,将移动方块加入固定的方块的数组,进行行满判定,如果行满则消除,重新产生新的方块,并下落。
/*
* 如果处于等时间间隔下落状态
* 检测是否可以按要求移动
*/
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实现俄罗斯方块(附源码)(一)
Android使用GridView实现俄罗斯方块(附源码)(二)
Android使用GridView实现俄罗斯方块(附源码)(三)
Android使用GridView实现俄罗斯方块(附源码)(四)