基于Android的俄羅斯方塊開發(fā)(附源碼)

最近在學Android開發(fā),一直想找個項目來練練手洗显,前段時間在考試也沒有時間外潜,不過那時候就有點想法,就是想做個俄羅斯方塊或者貪吃蛇什么的墙懂。然后一直到這幾天才有時間來寫這個項目橡卤。
完成這個項目主要有幾個問題要處理:
① 邊界問題,即如何判斷俄羅斯方塊是否已經到達邊界损搬,主要是在左右移動和下降過程中碧库,判斷俄羅斯方塊是否已經抵達邊界,使其不超越邊界巧勤。
② 接觸問題嵌灰,即如何判斷俄羅斯方塊已經與其他俄羅斯方塊接觸,此時應該停止方塊的下落颅悉,或者避免方塊間重合沽瞭。
③ 旋轉問題,俄羅斯方塊要旋轉很簡單剩瓶,只要用轉換公式即可驹溃,但問題是如何判斷旋轉后的位置是否不合法,即有沒有可能觸及邊界延曙,或者與其他俄羅斯方塊重合豌鹤。
④ 消去問題,當網格中有一行填滿了方塊枝缔,需要消去此行布疙,并將在其上的所有方塊均向下移動一行,更新分數等相關信息。
⑤ 界面顯示問題灵临,如何顯示下落的俄羅斯方塊和靜止的俄羅斯方塊組截型,以及下一個即將下落的俄羅斯方塊。
⑥ 還有一些比較細節(jié)的問題儒溉,只要有耐心還是很容易可以解決的宦焦,這里就不再贅述了。
** 下面給出我相應的解決方案:**
** ** 首先睁搭,看下我的項目文件框架圖:

然后赶诊,我在解釋下每個類的作用:


第① 和第②個問題類似,每個俄羅斯方塊(TetrisBlock)對象包含四個更小的塊單元(BlockUnit), 在處理這兩個問題的時候只要在塊單元類當中添加判斷塊單元對象是否接觸邊界或者其他俄羅斯方塊的塊單元的方法园骆,然后俄羅斯方塊類的判斷接觸邊界或其他俄羅斯方塊的方法舔痪,只需依次調用該俄羅斯方塊對象的所有塊單元對象的判斷方法,若其中一個塊單元接觸邊界锌唾,則該俄羅斯方塊接觸邊界锄码。而邊界和方塊接觸問題都可以大致分為兩類,a. 接觸兩側邊界或在兩側接觸其他俄羅斯方塊晌涕,b. 接觸網格底部邊界或者方塊接觸其下的其他俄羅斯方塊滋捶。這兩類問題需要分開處理,因為方塊的下落和左右移動是分開進行的余黎,當方塊左右移動時不能穿過兩側邊界或與其他俄羅斯方塊重合重窟,當方塊下落時,則不能穿過底部邊界或穿過其下的俄羅斯方塊惧财。
第③個問題巡扇,先克隆一個下落的俄羅斯方塊,對其進行旋轉操作垮衷,然后判斷其是否超越邊界或者與其他俄羅斯方塊重合厅翔,若其狀態(tài)合法則將下落的俄羅斯方塊進行旋轉操作,否則結束操作并返回搀突。
第④個問題刀闷,用一個數組標記每行塊單元個數,并計算每行最多可填入多少個塊單元仰迁,若某一行已經填滿則甸昏,刪除每個俄羅斯方塊中在該行上的塊單元,并且將在該行之上的塊單元全部向下移動一行徐许。

最后的效果大概這樣

Paste_Image.png

能想到這個問題的解決方案就可以很輕松的開始敲代碼了施蜜。

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>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末艇挨,一起剝皮案震驚了整個濱河市残炮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缩滨,老刑警劉巖势就,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泉瞻,死亡現(xiàn)場離奇詭異,居然都是意外死亡苞冯,警方通過查閱死者的電腦和手機袖牙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舅锄,“玉大人鞭达,你說我怎么就攤上這事』史蓿” “怎么了畴蹭?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鳍烁。 經常有香客問我叨襟,道長,這世上最難降的妖魔是什么老翘? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任芹啥,我火速辦了婚禮,結果婚禮上铺峭,老公的妹妹穿的比我還像新娘墓怀。我一直安慰自己,他們只是感情好卫键,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布傀履。 她就那樣靜靜地躺著,像睡著了一般莉炉。 火紅的嫁衣襯著肌膚如雪钓账。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天絮宁,我揣著相機與錄音梆暮,去河邊找鬼。 笑死绍昂,一個胖子當著我的面吹牛啦粹,可吹牛的內容都是我干的。 我是一名探鬼主播窘游,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼唠椭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了忍饰?” 一聲冷哼從身側響起贪嫂,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤旬陡,失蹤者是張志新(化名)和其女友劉穎孕蝉,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體贱除,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡溉痢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年萌朱,在試婚紗的時候發(fā)現(xiàn)自己被綠了摆屯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片湿滓。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖台猴,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情俱两,我是刑警寧澤饱狂,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站宪彩,受9級特大地震影響休讳,放射性物質發(fā)生泄漏。R本人自食惡果不足惜尿孔,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一俊柔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧活合,春花似錦雏婶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至告嘲,卻和暖如春错维,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背橄唬。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工赋焕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仰楚。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓隆判,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缸血。 傳聞我的和親對象是個殘疾皇子蜜氨,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容