最近在學Android開發(fā),一直想找個項目來練練手洗显,前段時間在考試也沒有時間外潜,不過那時候就有點想法,就是想做個俄羅斯方塊或者貪吃蛇什么的墙懂。然后一直到這幾天才有時間來寫這個項目橡卤。
完成這個項目主要有幾個問題要處理:
① 邊界問題,即如何判斷俄羅斯方塊是否已經到達邊界损搬,主要是在左右移動和下降過程中碧库,判斷俄羅斯方塊是否已經抵達邊界,使其不超越邊界巧勤。
② 接觸問題嵌灰,即如何判斷俄羅斯方塊已經與其他俄羅斯方塊接觸,此時應該停止方塊的下落颅悉,或者避免方塊間重合沽瞭。
③ 旋轉問題,俄羅斯方塊要旋轉很簡單剩瓶,只要用轉換公式即可驹溃,但問題是如何判斷旋轉后的位置是否不合法,即有沒有可能觸及邊界延曙,或者與其他俄羅斯方塊重合豌鹤。
④ 消去問題,當網格中有一行填滿了方塊枝缔,需要消去此行布疙,并將在其上的所有方塊均向下移動一行,更新分數等相關信息。
⑤ 界面顯示問題灵临,如何顯示下落的俄羅斯方塊和靜止的俄羅斯方塊組截型,以及下一個即將下落的俄羅斯方塊。
⑥ 還有一些比較細節(jié)的問題儒溉,只要有耐心還是很容易可以解決的宦焦,這里就不再贅述了。
** 下面給出我相應的解決方案:**
** ** 首先睁搭,看下我的項目文件框架圖:
然后赶诊,我在解釋下每個類的作用:
第① 和第②個問題類似,每個俄羅斯方塊(TetrisBlock)對象包含四個更小的塊單元(BlockUnit), 在處理這兩個問題的時候只要在塊單元類當中添加判斷塊單元對象是否接觸邊界或者其他俄羅斯方塊的塊單元的方法园骆,然后俄羅斯方塊類的判斷接觸邊界或其他俄羅斯方塊的方法舔痪,只需依次調用該俄羅斯方塊對象的所有塊單元對象的判斷方法,若其中一個塊單元接觸邊界锌唾,則該俄羅斯方塊接觸邊界锄码。而邊界和方塊接觸問題都可以大致分為兩類,a. 接觸兩側邊界或在兩側接觸其他俄羅斯方塊晌涕,b. 接觸網格底部邊界或者方塊接觸其下的其他俄羅斯方塊滋捶。這兩類問題需要分開處理,因為方塊的下落和左右移動是分開進行的余黎,當方塊左右移動時不能穿過兩側邊界或與其他俄羅斯方塊重合重窟,當方塊下落時,則不能穿過底部邊界或穿過其下的俄羅斯方塊惧财。
第③個問題巡扇,先克隆一個下落的俄羅斯方塊,對其進行旋轉操作垮衷,然后判斷其是否超越邊界或者與其他俄羅斯方塊重合厅翔,若其狀態(tài)合法則將下落的俄羅斯方塊進行旋轉操作,否則結束操作并返回搀突。
第④個問題刀闷,用一個數組標記每行塊單元個數,并計算每行最多可填入多少個塊單元仰迁,若某一行已經填滿則甸昏,刪除每個俄羅斯方塊中在該行上的塊單元,并且將在該行之上的塊單元全部向下移動一行徐许。
最后的效果大概這樣
能想到這個問題的解決方案就可以很輕松的開始敲代碼了施蜜。
package cn.jczhuang.tetris2;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import cn.jczhuang.tetris2.model.BlockUnit;
import cn.jczhuang.tetris2.model.TetrisBlock;
import cn.jczhuang.tetris2.view.TetrisView;
import cn.jczhuang.tetris2.view.ShowNextBlockView;
public class Main extends Activity {
public Button left, right, rotate, start, speedUp; //按鈕
public TextView score, maxScore, level, speed; //標簽
public int scoreValue,maxScoreValue,levelValue,speedValue; //標簽值
public String scoreString = "分數:",maxScoreString = "最高分:",levelString = "等級:",speedString = "速度:";
public TetrisView view;
public ShowNextBlockView nextBlockView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 獲取各組件和標簽值
view = (TetrisView)findViewById(R.id.tetrisView);
left = (Button)findViewById(R.id.left);
right = (Button)findViewById(R.id.right);
rotate = (Button)findViewById(R.id.rotate);
start = (Button)findViewById(R.id.start);
speedUp = (Button)findViewById(R.id.speedUp);
nextBlockView = (ShowNextBlockView)findViewById(R.id.nextBlockView);
nextBlockView.invalidate();
score = (TextView)findViewById(R.id.score);
maxScore = (TextView)findViewById(R.id.maxScore);
level = (TextView)findViewById(R.id.level);
speed = (TextView)findViewById(R.id.speed);
scoreValue = maxScoreValue =0;
levelValue = speedValue = 1;
score.setText(scoreString + scoreValue);
level.setText(levelString + levelValue);
speed.setText(speedString + speedValue);
maxScore.setText(maxScoreString + maxScoreValue);
//設置各按鈕的監(jiān)聽器
left.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (view.canMove)
view.getFallingBlock().move(-1);
runOnUiThread(new Runnable() {
@Override
public void run() {
view.invalidate();
}
});
}
});
right.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (view.canMove)
view.getFallingBlock().move(1);
runOnUiThread(new Runnable() {
@Override
public void run() {
view.invalidate();
}
});
}
});
rotate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (view.canMove == false)
return;
TetrisBlock copyOfFallingBlock = view.getFallingBlock().clone();
copyOfFallingBlock.rotate();
if (copyOfFallingBlock.canRotate()) {
TetrisBlock fallinBlock = view.getFallingBlock();
fallinBlock.rotate();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
view.invalidate();
}
});
}
});
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.init();
}
});
speedUp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (view.canMove) {
view.getFallingBlock().setY(view.getFallingBlock().getY() + BlockUnit.UNITSIZE);
runOnUiThread(new Runnable() {
@Override
public void run() {
view.invalidate();
}
});
}
}
});
view.setFather(this);
view.invalidate();
}
}
package cn.jczhuang.tetris2.model;
import java.util.ArrayList;
import cn.jczhuang.tetris2.view.TetrisView;
/**
* Created by Terence on 2016/2/3.
*/
public class TetrisBlock implements Cloneable{
/*
* 俄羅斯方塊
*/
public final static int TYPESUM = 7; //方塊種類總數
public final static int DIRECTIONSUM = 4; //每個方塊有四個方向
private int blockType,blockDirection; //方塊種類,方塊朝向
private int color; //方塊顏色
private float x, y; //方塊坐標
private ArrayList<BlockUnit> units = new ArrayList<>(); //方塊組成部分
private ArrayList<TetrisBlock> blocks = new ArrayList<>(); //所有俄羅斯方塊
public void remove(int j){
/*
* 刪除在第j行上的方塊單元
* @param 需刪除行標
*/
for(int i=units.size()-1;i>=0;i--){
/*
* ①逆向遍歷
* ②根據y坐標計算單元所在行绊寻,若為j行則從units中刪除
*/
if((int)((units.get(i).getY()- TetrisView.beginPoint)/50) == j)
units.remove(i);
}
}
public boolean canRotate(){
/*
* 判斷方塊是否能夠翻轉
* @return 若能翻轉返回true
*/
for(TetrisBlock b:blocks){
//遍歷俄羅斯方塊所有單元,是否均能翻轉,若其中一個單元不能澄步,則俄羅斯方塊也不能翻轉
if(canRotate(b)==false){
return false;
}
}
return true;
}
public boolean canRotate(TetrisBlock other){
/*
* 判斷方塊是否能夠翻轉
* @return 若能翻轉返回true
* @param 另一俄羅斯方塊
*/
for(BlockUnit i:units){
//遍歷俄羅斯方塊所有單元冰蘑,是否均能翻轉,若其中一個單元不能村缸,則俄羅斯方塊也不能翻轉
for(BlockUnit j:other.getUnits() ){
if(i.canRotate(j) == false){
return false;
}
}
}
return true;
}
public void move(int x){
/*
* 俄羅斯方塊左右移動
*/
// 檢查是否接觸邊界祠肥,若接觸邊界,并且往接觸邊界移動會超界梯皿,故返回
if(checkCollision_X() <0 && x<0||checkCollision_X()>0&&x>0)
return;
//更新移動后的坐標
if(x > 0)
setX(getX() + BlockUnit.UNITSIZE);
else
setX(getX() - BlockUnit.UNITSIZE);
}
public boolean checkCollision_Y() {
/*
* 判斷俄羅斯方塊是否與其他俄羅斯方塊或者邊界(底部)接觸
* @return 若接觸返回true
*/
for(BlockUnit u:units){
//遍歷所有單元塊判斷是否接觸底部
if(u.checkOutOfBoundary_Y())
return true;
}
for(TetrisBlock block:blocks){
//判斷是否與其他俄羅斯方塊接觸
if(this == block) {
continue;
}
//判斷俄羅斯方塊底部是否接觸其他俄羅斯方塊
if(checkCollision_Y(block))
return true;
}
return false;
}
public int checkCollision_X() {
/*
* 判斷俄羅斯方塊是否與其他俄羅斯方塊或者邊界(兩側)接觸
* @return 若接觸返回true
*/
for(BlockUnit u:units){
//遍歷所有單元塊判斷是否接觸兩側
if(u.checkOutOfBoundary_X() != 0)
return u.checkOutOfBoundary_X();
}
for(TetrisBlock block:blocks){
if(this == block)
continue;
//判斷俄羅斯方塊兩側是否接觸其他俄羅斯方塊
if(checkCollision_X(block) != 0)
return checkCollision_X(block);
}
return 0;
}
public boolean checkCollision_Y(TetrisBlock other){
/*
* 判斷俄羅斯方塊是否與其他俄羅斯方塊或者邊界(底部)接觸
* @return 若接觸返回true
*/
for(BlockUnit i: units){
//遍歷所有單元塊判斷是否接觸底部
for(BlockUnit j:other.units){
if(i == j) {
continue;
}
//判斷俄羅斯方塊底部是否接觸其他俄羅斯方塊
if(i.checkVerticalCollision(j))
return true;
}
}
return false;
}
public int checkCollision_X(TetrisBlock other){
/*
* 判斷俄羅斯方塊是否與其他俄羅斯方塊或者邊界(兩側)接觸
* @return 若接觸返回true
*/
for(BlockUnit i: units){
//遍歷所有單元塊判斷是否接觸兩側
for(BlockUnit j:other.units){
if(i == j)
continue;
//判斷俄羅斯方塊兩側是否接觸其他俄羅斯方塊
if(i.checkHorizontalCollision(j)!=0)
return i.checkHorizontalCollision(j);
}
}
return 0;
}
public TetrisBlock(float x,float y){
/*
* 構造函數
*/
this.x = x;
this.y = y;
blockType = (int)(Math.random() * TYPESUM) + 1; //隨機生成一個種類
blockDirection = 1; //默認初始方向
color = (int)(Math.random() * 5) + 1; //隨機生成一個顏色
switch(blockType){
case 1:
for(int i=0;i<4;i++){
units.add(new BlockUnit(x + (-2 + i ) * BlockUnit.UNITSIZE , y));
}
break;
case 2:
units.add(new BlockUnit(x + (-1 + 1 ) * BlockUnit.UNITSIZE , y - BlockUnit.UNITSIZE));
for(int i=0;i<3;i++){
units.add(new BlockUnit(x + (-1 + i ) * BlockUnit.UNITSIZE , y ));
}
break;
case 3:
for(int i=0;i<2;i++){
units.add(new BlockUnit(x + (i-1) * BlockUnit.UNITSIZE,y - BlockUnit.UNITSIZE ));
units.add(new BlockUnit(x + (i-1) * BlockUnit.UNITSIZE,y ));
}
break;
case 4:
units.add(new BlockUnit(x + (-1 + 0 ) * BlockUnit.UNITSIZE , y - BlockUnit.UNITSIZE));
for(int i=0;i<3;i++){
units.add(new BlockUnit(x + (-1 + i ) * BlockUnit.UNITSIZE , y ));
}
break;
case 5:
units.add(new BlockUnit(x + (-1 + 2 ) * BlockUnit.UNITSIZE , y - BlockUnit.UNITSIZE));
for(int i=0;i<3;i++){
units.add(new BlockUnit(x + (-1 + i ) * BlockUnit.UNITSIZE , y ));
}
break;
case 6:
for(int i=0;i<2;i++){
units.add(new BlockUnit(x + (-1+i) * BlockUnit.UNITSIZE,y - BlockUnit.UNITSIZE ));
units.add(new BlockUnit(x + i * BlockUnit.UNITSIZE,y ));
}
break;
case 7:
for(int i=0;i<2;i++){
units.add(new BlockUnit(x + i * BlockUnit.UNITSIZE,y - BlockUnit.UNITSIZE ));
units.add(new BlockUnit(x + ( -1 + i )* BlockUnit.UNITSIZE,y ));
}
break;
}
}
public void setX(float x) {
/*
* 設置俄羅斯方塊坐標
* @param 新坐標值
*/
float dif_x = x - this.x; //x增量
for (BlockUnit u:units){
//根據增量更新方塊單元坐標
u.setX(u.getX() + dif_x);
}
this.x = x;
}
public void setY(float y) {
/*
* 設置俄羅斯方塊坐標
* @param 新坐標值
*/
//若縱坐標超界則返回
if(checkCollision_Y())
return;
float dif_y = y - this.y;//y增量
for (BlockUnit u:units){
//根據增量更新方塊單元坐標
u.setY(u.getY() + dif_y);
}
this.y = y;
}
public TetrisBlock(TetrisBlock other){
x = other.x;
y = other.y;
color = other.color;
blockDirection = other.blockDirection;
blockType = other.blockType;
blocks = other.blocks;
}
@Override
public TetrisBlock clone(){
TetrisBlock block = new TetrisBlock(this);
for(BlockUnit u:getUnits()){
block.units.add(u.clone());
}
return block;
}
public ArrayList<TetrisBlock> getBlocks() {
return blocks;
}
public void setBlocks(ArrayList<TetrisBlock> blocks) {
this.blocks = blocks;
}
public ArrayList<BlockUnit> getUnits() {
return units;
}
public void setUnits(ArrayList<BlockUnit> units) {
this.units = units;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public int getBlockDirection() {
return blockDirection;
}
public void setBlockDirection(int blockDirection) {
this.blockDirection = blockDirection;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public void rotate(){
if(checkCollision_X()!=0 && checkCollision_Y() || blockType == 3)
return;
//按順時針旋轉
for(BlockUnit u:units){
float tx = u.getX();
float ty = u.getY();
u.setX(-(ty - y) + x);
u.setY( tx - x + y) ;
}
}
}
package cn.jczhuang.tetris2.model;
import cn.jczhuang.tetris2.view.TetrisView;
/**
* Created by Terence on 2016/2/3.
*/
public class BlockUnit implements Cloneable{
/*
* @class 俄羅斯方塊單元塊仇箱,每個俄羅斯方塊包含四個單元塊
*
*/
public final static float UNITSIZE = 50; //單元塊的大小
public static float max_x, max_y;//單元塊的最大橫縱坐標
private float x,y; //單元塊的橫縱坐標
public BlockUnit(float x,float y){
/*
* @param 單元塊橫縱坐標
* 構造函數
*/
this.x = x;
this.y = y;
}
public boolean canRotate(BlockUnit other){
/*
* 判斷是否適合進行旋轉
* @param 另一單元塊對象
* @return 若能旋轉返回true,否則false
*/
//超出邊界
if(x<TetrisView.beginPoint/2 || x>= TetrisView.max_x - UNITSIZE||y >= TetrisView.max_y - UNITSIZE)
return false;
//與其他單元塊重合
if(Math.abs(x-other.x)<=UNITSIZE/2 && Math.abs(y-other.y)<=UNITSIZE/2)
return false;
return true;
}
public boolean checkOutOfBoundary_Y(){
/*
* 判斷單元塊是否在縱向剛好踩到邊界东羹,即y坐標是否剛好踩界剂桥,
* 用以判斷俄羅斯方塊是否能向下移動
* @return 若超界返回true,否則false
*/
if(y >= TetrisView.max_y - UNITSIZE * 2 )
return true;
else if( y - TetrisView.max_y - UNITSIZE * 2 <= 1e-5 && y - TetrisView.max_y - UNITSIZE * 2 >= -1e-5)
return true;
else
return false;
}
public int checkOutOfBoundary_X(){
/*
* 判斷單元塊是否在橫向剛好踩到邊界属提,即x坐標是否剛好踩界权逗,
* 用以判斷俄羅斯方塊是否能左右移動
* @return 若超界返回true,否則false
*/
if(x<=50 )
return -1;
else if(x >= TetrisView.max_x - UNITSIZE * 2)
return 1;
else
return 0;
}
public boolean checkVerticalCollision(BlockUnit other){
/*
* 判斷單元塊是否在橫向與邊界或其他單元塊接觸
* @param 另一單元塊
* @return 若接觸返回true
*/
if(y >= TetrisView.max_y - UNITSIZE )
return true;
else if( y - TetrisView.max_y - UNITSIZE >= 1e-5 && y - TetrisView.max_y - UNITSIZE <= -1e-5)
return true;
if(Math.abs(x - other.x) >= UNITSIZE)
return false;
else{
if(Math.abs(y - other.y) > UNITSIZE)
return false;
else if( y- other.y - UNITSIZE < 1e-5 && y- other.y - UNITSIZE > -1e-5)
return true;
return true;
}
}
public int checkHorizontalCollision(BlockUnit other){
/*
* 判斷單元塊是否在縱向與邊界或其他單元塊接觸
* @param 另一單元塊
* @return 若接觸返回true
*/
if(x <= 50 || x > TetrisView.max_x - UNITSIZE * 2)
return checkOutOfBoundary_X();
if(Math.abs(y - other.y )>= UNITSIZE)
return 0;
else{
if(Math.abs(x - other.x) > UNITSIZE)
return 0;
else if(x - other.x - UNITSIZE <= 1e-5 && x - other.x - UNITSIZE >= -1e-5)
return -1;
else if(other.x - x - UNITSIZE <= 1e-5 && other.x - x - UNITSIZE >= -1e-5)
return 1;
}
return 0;
}
@Override
public BlockUnit clone(){
return new BlockUnit(getX(),getY());
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
}
package cn.jczhuang.tetris2.view;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import cn.jczhuang.tetris2.Main;
import cn.jczhuang.tetris2.model.BlockUnit;
import cn.jczhuang.tetris2.model.TetrisBlock;
/**
* Created by Terence on 2016/2/3.
*/
public class TetrisView extends View {
/*
* 主畫板冤议,用于顯示游戲主要部分斟薇,即活動部分,俄羅斯方塊下降恕酸,消去等
*
*/
boolean flag;
public static int beginPoint = 10; //網格開始坐標值堪滨,橫縱坐標的開始值都是此值
public static int max_x, max_y; //保存俄羅斯方塊單元的最大橫縱坐標
public static float beginX; //俄羅斯方塊初始x坐標
public int dropSpeed = 300; //俄羅斯方塊下落線程默認休眠時間
public int currentSpeed = 300; //俄羅斯方塊下落線程當前休眠時間
public boolean isRun = true; //標識游戲是否正在進行
public boolean canMove = false; //標識此時俄羅斯方塊是否能左右移動
public Thread dropThread ; //游戲主線程
private int[] map = new int[100]; //保存每行網格中包含俄羅斯方塊單元的個數
private Main father; //調用此對象的Activity對象
private TetrisBlock fallingBlock; //正在下落的俄羅斯方塊
private Thread thread = new Thread();//俄羅斯方塊下落線程
private float x1, y1, x2, y2; //保存onTouchEvent中的起始坐標和結束坐標
private ArrayList<TetrisBlock> blocks = new ArrayList<>();
private float h,w;//保存TetrisView的寬和高
public void clear(){
//清空游戲狀態(tài)
isRun = false;
blocks.clear();
thread = new Thread();
fallingBlock = null;
}
@Override
public boolean onTouchEvent(MotionEvent event){
/*
* 觸摸事件,根據滑動方向左右移動俄羅斯方塊蕊温,向上滑動則旋轉俄羅斯方塊
*/
if(event.getAction() == MotionEvent.ACTION_DOWN){
//記錄觸摸滑動事件起始位置坐標
x1 = event.getX();
y1 = event.getY();
}
if(event.getAction() == MotionEvent.ACTION_UP ){
//記錄觸摸庝事件結束位置坐標
//若此時俄羅斯方塊不能左右移動袱箱,即不在下落過程,則返回
if(canMove == false)
return false;
x2 = event.getX();
y2 = event.getY();
float tx = fallingBlock.getX();
float ty = fallingBlock.getY();
if(x1 - x2 > 50){
//向左滑動
fallingBlock.move(-1);
//更新界面
father.runOnUiThread(new Runnable() {
@Override
public void run() {
TetrisView.this.invalidate();
}
});
}else if(x2 - x1 > 50){
//向右滑動
fallingBlock.move(1);
//更新界面
father.runOnUiThread(new Runnable() {
@Override
public void run() {
TetrisView.this.invalidate();
}
});
}else if(y1 - y2 > 50){
//向上滑動寿弱,旋轉俄羅斯方塊
fallingBlock.rotate();
//更新界面
father.runOnUiThread(new Runnable() {
@Override
public void run() {
TetrisView.this.invalidate();
}
});
}
}
return true;
}
public void init(){
/*
* 游戲設置初始化
*/
dropSpeed = 300; //俄羅斯方塊下落線程默認休眠時間
currentSpeed = 300; //俄羅斯方塊下落線程當前休眠時間
Arrays.fill(map, 0); //每行網格中包含俄羅斯方塊單元的個數全部初始化為0
flag = true; //第一次進入線程循環(huán)
isRun = true; //游戲正在運行
dropThread=new Thread(new Runnable() {
/*
* 游戲主線程
*/
@Override
public void run() {
while(isRun) {
try {
//初始化各參數
Thread.sleep(3000);
h = getHeight();
w = getWidth();
beginX = (int)((w - beginPoint)/100) * 50 + beginPoint;
} catch (Exception e) {
e.printStackTrace();
}
if(flag) {
//第一次進入線程循環(huán)犯眠,創(chuàng)建第一個俄羅斯方塊
father.nextBlockView.createNextBlock();
father.runOnUiThread(new Runnable() {
//更新ui
@Override
public void run() {
father.nextBlockView.invalidate();
}
});
flag = false; //下次循環(huán)不在執(zhí)行此塊操作
}
if(thread.getState() == Thread.State.TERMINATED || thread.getState() == Thread.State.NEW) {
//如果線程新創(chuàng)建或者已經結束,則重新創(chuàng)建新線程
thread = new Thread(new Runnable() {
@Override
public void run() {
h = getHeight();
w = getWidth();
for(TetrisBlock b:blocks){
//判斷游戲是否應該結束
if(b.getY()<=BlockUnit.UNITSIZE)
isRun = false;
}
fallingBlock = father.nextBlockView.nextBlock; //跟新當前線程
father.nextBlockView.createNextBlock(); //創(chuàng)建新的后備線程
father.runOnUiThread(new Runnable() {
//更新ui
@Override
public void run() {
father.nextBlockView.invalidate();
}
});
fallingBlock.setBlocks(blocks); //設置全局俄羅斯方塊
float ty;
int end = (int) ((h - 50 - beginPoint) / BlockUnit.UNITSIZE);
float dropCount = fallingBlock.getY(); //用以記錄正常下落情況下y坐標的值症革,即不考慮碰撞情況
canMove = true; //俄羅斯方塊開始下落
while (dropCount-fallingBlock.getY()<=2 * BlockUnit.UNITSIZE) {
//若dropCount即y坐標的理想值與y坐標的準確值相差不到兩個方塊的大小筐咧,
// 說明俄羅斯方塊仍在下落,否則說明發(fā)生觸碰事件噪矛,停止下落量蕊,跳出循環(huán)
try {
Thread.sleep(currentSpeed);
//更新相應坐標值
ty = fallingBlock.getY();
ty = ty + BlockUnit.UNITSIZE;
dropCount += BlockUnit.UNITSIZE;
fallingBlock.setY(ty);
father.runOnUiThread(new Runnable() {
//更新ui
@Override
public void run() {
TetrisView.this.invalidate();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
canMove = false;//俄羅斯方塊結束下落
blocks.add(fallingBlock); //講俄羅斯方塊加入靜止的俄羅斯方塊數組中
TetrisBlock temp = fallingBlock;
for (BlockUnit u : temp.getUnits()) {
//更新map,即更新每行網格中靜止俄羅斯方塊單元的個數
int index = (int) ((u.getY() - beginPoint) / 50); //計算所在行數
map[index]++;
}
//每行最大個數
int full = (int) ((w - 50 - beginPoint) / BlockUnit.UNITSIZE) + 1;
for (int i = 0; i <= end; i++) {
if (map[i] >= full) {
//若某行達到最大個數則消去此行并更新分數等級等信息
father.scoreValue += 100;
if(father.scoreValue > 1000) {
father.speedValue += 1;
father.levelValue += 1;
}
if(father.scoreValue>father.maxScoreValue){
father.maxScoreValue = father.scoreValue;
}
//將被消去行上的所有俄羅斯方塊向下移動一行
map[i] = 0;
for (int j = i; j > 0; j--)
map[j] = map[j - 1];
map[0] = 0;
//消去此行
for (TetrisBlock b : blocks)
b.remove(i);
for (int j = blocks.size()-1; j>=0; j--) {
if (blocks.get(j).getUnits().isEmpty()) {
blocks.remove(j);
continue;
}
for (BlockUnit u : blocks.get(j).getUnits()) {
if ((int) ((u.getY() - beginPoint) / 50) < i)
u.setY(u.getY() + BlockUnit.UNITSIZE);
}
}
father.runOnUiThread(new Runnable() {
@Override
public void run() {
//更新ui
father.score.setText(father.scoreString + father.scoreValue);
father.maxScore.setText(father.maxScoreString + father.maxScoreValue);
father.speed.setText(father.speedString + father.speedValue);
father.level.setText(father.levelString + father.levelValue);
TetrisView.this.invalidate();
}
});
}
}
}
});
thread.start();
}
}
if(isRun == false){
//游戲結束
father.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(father,"game over",Toast.LENGTH_SHORT).show();
}
});
}
}
});
dropThread.start();
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
max_x = getWidth();
max_y = getHeight();
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
RectF rel;
float size = BlockUnit.UNITSIZE;
//俄羅斯方塊顏色數組
int color[] = {0,Color.BLUE,Color.RED,Color.YELLOW,Color.GREEN,Color.GRAY};
if(!blocks.isEmpty()){
//繪出所有靜止的俄羅斯方塊
for(TetrisBlock block:blocks){
paint.setColor(color[block.getColor()]); //設置畫筆為俄羅斯方塊的顏色
for(BlockUnit u:block.getUnits()){
float tx = u.getX();
float ty = u.getY();
rel = new RectF(tx, ty, tx + size, ty + size); //將方塊畫成圓角矩形的形式
canvas.drawRoundRect(rel, 8, 8, paint);
}
}
}
if(fallingBlock!=null) {
//繪制正在下落的俄羅斯方塊
paint.setColor(color[fallingBlock.getColor()]);
for (BlockUnit u : fallingBlock.getUnits()) {
float tx = u.getX();
float ty = u.getY();
rel = new RectF(tx, ty, tx + size, ty + size);
canvas.drawRoundRect(rel, 8, 8, paint);
}
}
paint.setColor(Color.LTGRAY);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
//繪制網格
for(int i=beginPoint; i<max_x-50; i+= 50){
for(int j=beginPoint; j<max_y-50; j+= 50) {
rel = new RectF(i, j, i + 50, j + 50);
canvas.drawRoundRect(rel, 8, 8, paint);
}
}
}
public TetrisBlock getFallingBlock() {
return fallingBlock;
}
public float getH() {
return h;
}
public void setH(float h) {
this.h = h;
}
public void setFallingBlock(TetrisBlock fallingBlock) {
this.fallingBlock = fallingBlock;
}
public Activity getFather() {
return father;
}
public void setFather(Main father) {
this.father = father;
}
public TetrisView(Context context) {
super(context);
}
public TetrisView(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
package cn.jczhuang.tetris2.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import cn.jczhuang.tetris2.model.BlockUnit;
import cn.jczhuang.tetris2.model.TetrisBlock;
/**
* Created by Terence on 2016/2/5.
*/
public class ShowNextBlockView extends View {
/*
* 用以顯示下一個俄羅斯方塊
*/
public TetrisBlock nextBlock = null; //保存下一個俄羅斯方塊
public TetrisBlock createNextBlock(){
/*
* 創(chuàng)建下一個俄羅斯方塊
*/
nextBlock = new TetrisBlock(TetrisView.beginX,TetrisView.beginPoint);
return nextBlock;
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
//畫布畫筆初始化
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
RectF rel;
float size = BlockUnit.UNITSIZE;
//方塊顏色
int color[] = {0,Color.BLUE,Color.RED,Color.YELLOW,Color.GREEN,Color.GRAY};
if(nextBlock!=null) {
//畫出下一個俄羅斯方塊
paint.setColor(color[nextBlock.getColor()]);
for (BlockUnit u : nextBlock.getUnits()) {
//設置填充風格
paint.setStyle(Paint.Style.FILL);
paint.setColor(color[nextBlock.getColor()]);
//獲取每個方塊的橫縱坐標
float tx = (float)(u.getX() - TetrisView.beginX + BlockUnit.UNITSIZE * 1.5);
float ty = u.getY() + BlockUnit.UNITSIZE ;
//創(chuàng)建圓角矩形
rel = new RectF(tx, ty, tx + size, ty + size);
canvas.drawRoundRect(rel, 8, 8, paint);
//畫出邊界
paint.setColor(Color.LTGRAY);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
canvas.drawRoundRect(rel, 8, 8, paint);
}
}
}
public ShowNextBlockView(Context context) {
/*
* 構造函數
*/
super(context);
}
public ShowNextBlockView(Context context, AttributeSet attrs) {
/*
* 構造函數
*/
super(context, attrs);
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#1a98df"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="right"
android:layout_margin="3dp"
android:background="@drawable/ic_reply_white_48dp"
android:gravity="right" />
<TextView
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_gravity="center"
android:layout_weight="1"
android:background="#1a98df"
android:gravity="center"
android:text="俄羅斯方塊"
android:textColor="#ffffff"
android:textSize="20dp" />
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="right"
android:layout_margin="3dp"
android:background="@drawable/ic_send_white_48dp"
android:gravity="right" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#ffffff"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<cn.jczhuang.tetris2.view.TetrisView
android:id="@+id/tetrisView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:orientation="vertical"
android:layout_height="wrap_content">
<cn.jczhuang.tetris2.view.ShowNextBlockView
android:id="@+id/nextBlockView"
android:layout_width="250px"
android:layout_height="250px"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/score"
android:textSize="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="分數:" />
<TextView
android:id="@+id/level"
android:textSize="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="等級:level " />
<TextView
android:id="@+id/speed"
android:textSize="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="速度:" />
<TextView
android:id="@+id/maxScore"
android:textSize="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="最高分:" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<Button
android:id="@+id/left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="left" />
<Button
android:id="@+id/right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="right" />
<Button
android:id="@+id/rotate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="rotate" />
<Button
android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="start" />
</LinearLayout>
<Button
android:id="@+id/speedUp"
android:text="speedUp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</LinearLayout>