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

最近在學(xué)Android開發(fā),一直想找個(gè)項(xiàng)目來練練手,前段時(shí)間在考試也沒有時(shí)間,不過那時(shí)候就有點(diǎn)想法,就是想做個(gè)俄羅斯方塊或者貪吃蛇什么的。然后一直到這幾天才有時(shí)間來寫這個(gè)項(xiàng)目。
完成這個(gè)項(xiàng)目主要有幾個(gè)問題要處理:
① 邊界問題,即如何判斷俄羅斯方塊是否已經(jīng)到達(dá)邊界,主要是在左右移動(dòng)和下降過程中,判斷俄羅斯方塊是否已經(jīng)抵達(dá)邊界,使其不超越邊界。
② 接觸問題,即如何判斷俄羅斯方塊已經(jīng)與其他俄羅斯方塊接觸,此時(shí)應(yīng)該停止方塊的下落,或者避免方塊間重合。
③ 旋轉(zhuǎn)問題,俄羅斯方塊要旋轉(zhuǎn)很簡單,只要用轉(zhuǎn)換公式即可,但問題是如何判斷旋轉(zhuǎn)后的位置是否不合法,即有沒有可能觸及邊界,或者與其他俄羅斯方塊重合。
④ 消去問題,當(dāng)網(wǎng)格中有一行填滿了方塊,需要消去此行,并將在其上的所有方塊均向下移動(dòng)一行,更新分?jǐn)?shù)等相關(guān)信息。
⑤ 界面顯示問題,如何顯示下落的俄羅斯方塊和靜止的俄羅斯方塊組,以及下一個(gè)即將下落的俄羅斯方塊。
⑥ 還有一些比較細(xì)節(jié)的問題,只要有耐心還是很容易可以解決的,這里就不再贅述了。
** 下面給出我相應(yīng)的解決方案:**
** ** 首先,看下我的項(xiàng)目文件框架圖:

然后,我在解釋下每個(gè)類的作用:


第① 和第②個(gè)問題類似,每個(gè)俄羅斯方塊(TetrisBlock)對象包含四個(gè)更小的塊單元(BlockUnit), 在處理這兩個(gè)問題的時(shí)候只要在塊單元類當(dāng)中添加判斷塊單元對象是否接觸邊界或者其他俄羅斯方塊的塊單元的方法,然后俄羅斯方塊類的判斷接觸邊界或其他俄羅斯方塊的方法,只需依次調(diào)用該俄羅斯方塊對象的所有塊單元對象的判斷方法,若其中一個(gè)塊單元接觸邊界,則該俄羅斯方塊接觸邊界。而邊界和方塊接觸問題都可以大致分為兩類,a. 接觸兩側(cè)邊界或在兩側(cè)接觸其他俄羅斯方塊,b. 接觸網(wǎng)格底部邊界或者方塊接觸其下的其他俄羅斯方塊。這兩類問題需要分開處理,因?yàn)榉綁K的下落和左右移動(dòng)是分開進(jìn)行的,當(dāng)方塊左右移動(dòng)時(shí)不能穿過兩側(cè)邊界或與其他俄羅斯方塊重合,當(dāng)方塊下落時(shí),則不能穿過底部邊界或穿過其下的俄羅斯方塊。
第③個(gè)問題,先克隆一個(gè)下落的俄羅斯方塊,對其進(jìn)行旋轉(zhuǎn)操作,然后判斷其是否超越邊界或者與其他俄羅斯方塊重合,若其狀態(tài)合法則將下落的俄羅斯方塊進(jìn)行旋轉(zhuǎn)操作,否則結(jié)束操作并返回。
第④個(gè)問題,用一個(gè)數(shù)組標(biāo)記每行塊單元個(gè)數(shù),并計(jì)算每行最多可填入多少個(gè)塊單元,若某一行已經(jīng)填滿則,刪除每個(gè)俄羅斯方塊中在該行上的塊單元,并且將在該行之上的塊單元全部向下移動(dòng)一行。

最后的效果大概這樣

Paste_Image.png

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

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;       //標(biāo)簽

    public int scoreValue,maxScoreValue,levelValue,speedValue;     //標(biāo)簽值

    public String scoreString = "分?jǐn)?shù):",maxScoreString = "最高分:",levelString = "等級:",speedString = "速度:";

    public TetrisView view;

    public ShowNextBlockView nextBlockView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // 獲取各組件和標(biāo)簽值
        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);

        //設(shè)置各按鈕的監(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;        //方塊種類總數(shù)

    public final static int DIRECTIONSUM = 4;   //每個(gè)方塊有四個(gè)方向

    private  int blockType,blockDirection;      //方塊種類,方塊朝向

    private int color;                          //方塊顏色

    private float x, y;                         //方塊坐標(biāo)

    private ArrayList<BlockUnit> units = new ArrayList<>();     //方塊組成部分

    private ArrayList<TetrisBlock> blocks = new ArrayList<>();  //所有俄羅斯方塊

    public void remove(int j){
        /*
         * 刪除在第j行上的方塊單元
         * @param 需刪除行標(biāo)
         */

        for(int i=units.size()-1;i>=0;i--){
            /*
             * ①逆向遍歷
             * ②根據(jù)y坐標(biāo)計(jì)算單元所在行,若為j行則從units中刪除
             */
            if((int)((units.get(i).getY()- TetrisView.beginPoint)/50) == j)
                units.remove(i);
        }
    }
    public boolean canRotate(){
        /*
         * 判斷方塊是否能夠翻轉(zhuǎn)
         * @return 若能翻轉(zhuǎn)返回true
         */
        for(TetrisBlock b:blocks){
            //遍歷俄羅斯方塊所有單元,是否均能翻轉(zhuǎn),若其中一個(gè)單元不能,則俄羅斯方塊也不能翻轉(zhuǎn)
            if(canRotate(b)==false){
                return false;
            }
        }
        return true;
    }
    public boolean canRotate(TetrisBlock other){
        /*
         * 判斷方塊是否能夠翻轉(zhuǎn)
         * @return 若能翻轉(zhuǎn)返回true
         * @param 另一俄羅斯方塊
         */
        for(BlockUnit i:units){
            //遍歷俄羅斯方塊所有單元,是否均能翻轉(zhuǎn),若其中一個(gè)單元不能,則俄羅斯方塊也不能翻轉(zhuǎn)
            for(BlockUnit j:other.getUnits() ){
                if(i.canRotate(j) == false){
                    return false;
                }
            }
        }
        return true;
    }

    public void move(int x){
        /*
         * 俄羅斯方塊左右移動(dòng)
         */

        // 檢查是否接觸邊界,若接觸邊界,并且往接觸邊界移動(dòng)會(huì)超界,故返回
        if(checkCollision_X() <0 && x<0||checkCollision_X()>0&&x>0)
            return;

        //更新移動(dòng)后的坐標(biāo)
        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() {
        /*
         * 判斷俄羅斯方塊是否與其他俄羅斯方塊或者邊界(兩側(cè))接觸
         * @return 若接觸返回true
         */
        for(BlockUnit u:units){
            //遍歷所有單元塊判斷是否接觸兩側(cè)
            if(u.checkOutOfBoundary_X() != 0)
                return u.checkOutOfBoundary_X();
        }
        for(TetrisBlock block:blocks){
            if(this == block)
                continue;
            //判斷俄羅斯方塊兩側(cè)是否接觸其他俄羅斯方塊
            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){
        /*
         * 判斷俄羅斯方塊是否與其他俄羅斯方塊或者邊界(兩側(cè))接觸
         * @return 若接觸返回true
         */
        for(BlockUnit i: units){
            //遍歷所有單元塊判斷是否接觸兩側(cè)
            for(BlockUnit j:other.units){
                if(i == j)
                    continue;

                //判斷俄羅斯方塊兩側(cè)是否接觸其他俄羅斯方塊
                if(i.checkHorizontalCollision(j)!=0)
                    return i.checkHorizontalCollision(j);
            }
        }
        return 0;
    }
    public TetrisBlock(float x,float y){
        /*
         * 構(gòu)造函數(shù)
         */
        this.x = x;
        this.y = y;

        blockType = (int)(Math.random() * TYPESUM) + 1;   //隨機(jī)生成一個(gè)種類
        blockDirection = 1;                               //默認(rèn)初始方向
        color = (int)(Math.random() * 5) + 1;             //隨機(jī)生成一個(gè)顏色

        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) {
        /*
         * 設(shè)置俄羅斯方塊坐標(biāo)
         * @param 新坐標(biāo)值
         */
        float dif_x = x - this.x; //x增量

        for (BlockUnit u:units){
            //根據(jù)增量更新方塊單元坐標(biāo)
            u.setX(u.getX() + dif_x);
        }
        this.x = x;
    }

    public void setY(float y) {
        /*
         * 設(shè)置俄羅斯方塊坐標(biāo)
         * @param 新坐標(biāo)值
         */

        //若縱坐標(biāo)超界則返回
        if(checkCollision_Y())
            return;
        float dif_y = y - this.y;//y增量
        for (BlockUnit u:units){
            //根據(jù)增量更新方塊單元坐標(biāo)
            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;

        //按順時(shí)針旋轉(zhuǎn)
        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 俄羅斯方塊單元塊,每個(gè)俄羅斯方塊包含四個(gè)單元塊
     *
     */
    public final static float UNITSIZE = 50; //單元塊的大小

    public static float max_x, max_y;//單元塊的最大橫縱坐標(biāo)

    private float x,y; //單元塊的橫縱坐標(biāo)

    public BlockUnit(float x,float y){
        /*
         * @param 單元塊橫縱坐標(biāo)
         * 構(gòu)造函數(shù)
         */
        this.x = x;
        this.y = y;
    }

    public boolean canRotate(BlockUnit other){
        /*
         * 判斷是否適合進(jìn)行旋轉(zhuǎn)
         * @param 另一單元塊對象
         * @return 若能旋轉(zhuǎn)返回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坐標(biāo)是否剛好踩界,
         * 用以判斷俄羅斯方塊是否能向下移動(dòng)
         * @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坐標(biāo)是否剛好踩界,
         * 用以判斷俄羅斯方塊是否能左右移動(dòng)
         * @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 {
    /*
     * 主畫板,用于顯示游戲主要部分,即活動(dòng)部分,俄羅斯方塊下降,消去等
     *
     */

    boolean flag;

    public static int beginPoint = 10;  //網(wǎng)格開始坐標(biāo)值,橫縱坐標(biāo)的開始值都是此值

    public static int max_x, max_y;     //保存俄羅斯方塊單元的最大橫縱坐標(biāo)

    public static float beginX;         //俄羅斯方塊初始x坐標(biāo)

    public int dropSpeed = 300;         //俄羅斯方塊下落線程默認(rèn)休眠時(shí)間

    public int currentSpeed = 300;      //俄羅斯方塊下落線程當(dāng)前休眠時(shí)間

    public boolean isRun = true;        //標(biāo)識(shí)游戲是否正在進(jìn)行

    public boolean canMove = false;     //標(biāo)識(shí)此時(shí)俄羅斯方塊是否能左右移動(dòng)

    public Thread dropThread ;          //游戲主線程

    private int[] map = new int[100];   //保存每行網(wǎng)格中包含俄羅斯方塊單元的個(gè)數(shù)

    private Main father;                //調(diào)用此對象的Activity對象

    private TetrisBlock fallingBlock;   //正在下落的俄羅斯方塊

    private Thread thread = new Thread();//俄羅斯方塊下落線程

    private float x1, y1, x2, y2;       //保存onTouchEvent中的起始坐標(biāo)和結(jié)束坐標(biāo)

    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){
        /*
         * 觸摸事件,根據(jù)滑動(dòng)方向左右移動(dòng)俄羅斯方塊,向上滑動(dòng)則旋轉(zhuǎn)俄羅斯方塊
         */

        if(event.getAction() == MotionEvent.ACTION_DOWN){
            //記錄觸摸滑動(dòng)事件起始位置坐標(biāo)
            x1 = event.getX();
            y1 = event.getY();
        }
        if(event.getAction() == MotionEvent.ACTION_UP ){
            //記錄觸摸庝事件結(jié)束位置坐標(biāo)

            //若此時(shí)俄羅斯方塊不能左右移動(dòng),即不在下落過程,則返回
            if(canMove == false)
                return false;

            x2 = event.getX();
            y2 = event.getY();

            float tx = fallingBlock.getX();
            float ty = fallingBlock.getY();

            if(x1 - x2 > 50){
                //向左滑動(dòng)
                fallingBlock.move(-1);

                //更新界面
                father.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        TetrisView.this.invalidate();
                    }
                });
            }else if(x2 - x1 > 50){
                //向右滑動(dòng)
                fallingBlock.move(1);

                //更新界面
                father.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        TetrisView.this.invalidate();
                    }
                });
            }else if(y1 - y2 > 50){
                //向上滑動(dòng),旋轉(zhuǎn)俄羅斯方塊
                fallingBlock.rotate();

                //更新界面
                father.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        TetrisView.this.invalidate();
                    }
                });
            }
        }
        return true;
    }
    public void init(){
        /*
         * 游戲設(shè)置初始化
         */
        dropSpeed = 300;     //俄羅斯方塊下落線程默認(rèn)休眠時(shí)間

        currentSpeed = 300;  //俄羅斯方塊下落線程當(dāng)前休眠時(shí)間

        Arrays.fill(map, 0); //每行網(wǎng)格中包含俄羅斯方塊單元的個(gè)數(shù)全部初始化為0

        flag = true;         //第一次進(jìn)入線程循環(huán)

        isRun = true;        //游戲正在運(yùn)行

        dropThread=new Thread(new Runnable() {
            /*
             * 游戲主線程
             */
            @Override
            public void run() {
                while(isRun) {
                    try {
                        //初始化各參數(shù)
                        Thread.sleep(3000);
                        h = getHeight();
                        w = getWidth();
                        beginX  = (int)((w -  beginPoint)/100) * 50 + beginPoint;
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if(flag) {
                        //第一次進(jìn)入線程循環(huán),創(chuàng)建第一個(gè)俄羅斯方塊
                        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)建或者已經(jīng)結(jié)束,則重新創(chuàng)建新線程
                        thread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                h = getHeight();
                                w = getWidth();

                                for(TetrisBlock b:blocks){
                                    //判斷游戲是否應(yīng)該結(jié)束
                                    if(b.getY()<=BlockUnit.UNITSIZE)
                                        isRun = false;
                                }

                                fallingBlock = father.nextBlockView.nextBlock; //跟新當(dāng)前線程

                                father.nextBlockView.createNextBlock();        //創(chuàng)建新的后備線程

                                father.runOnUiThread(new Runnable() {
                                    //更新ui
                                    @Override
                                    public void run() {
                                        father.nextBlockView.invalidate();
                                    }
                                });

                                fallingBlock.setBlocks(blocks);      //設(shè)置全局俄羅斯方塊

                                float ty;

                                int end = (int) ((h - 50 - beginPoint) / BlockUnit.UNITSIZE);

                                float dropCount = fallingBlock.getY(); //用以記錄正常下落情況下y坐標(biāo)的值,即不考慮碰撞情況

                                canMove = true; //俄羅斯方塊開始下落

                                while (dropCount-fallingBlock.getY()<=2 * BlockUnit.UNITSIZE) {
                                    //若dropCount即y坐標(biāo)的理想值與y坐標(biāo)的準(zhǔn)確值相差不到兩個(gè)方塊的大小,
                                    // 說明俄羅斯方塊仍在下落,否則說明發(fā)生觸碰事件,停止下落,跳出循環(huán)
                                    try {
                                        Thread.sleep(currentSpeed);

                                        //更新相應(yīng)坐標(biāo)值
                                        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;//俄羅斯方塊結(jié)束下落

                                blocks.add(fallingBlock); //講俄羅斯方塊加入靜止的俄羅斯方塊數(shù)組中

                                TetrisBlock temp = fallingBlock;

                                for (BlockUnit u : temp.getUnits()) {
                                    //更新map,即更新每行網(wǎng)格中靜止俄羅斯方塊單元的個(gè)數(shù)
                                    int index = (int) ((u.getY() - beginPoint) / 50); //計(jì)算所在行數(shù)
                                    map[index]++;
                                }

                                //每行最大個(gè)數(shù)
                                int full = (int) ((w - 50 - beginPoint) / BlockUnit.UNITSIZE) + 1;

                                for (int i = 0; i <= end; i++) {
                                    if (map[i] >= full) {
                                        //若某行達(dá)到最大個(gè)數(shù)則消去此行并更新分?jǐn)?shù)等級等信息

                                        father.scoreValue += 100;
                                        if(father.scoreValue > 1000) {
                                            father.speedValue += 1;
                                            father.levelValue += 1;
                                        }
                                        if(father.scoreValue>father.maxScoreValue){
                                            father.maxScoreValue = father.scoreValue;
                                        }

                                        //將被消去行上的所有俄羅斯方塊向下移動(dòng)一行
                                        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){
                    //游戲結(jié)束
                    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;

        //俄羅斯方塊顏色數(shù)組
        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()]);          //設(shè)置畫筆為俄羅斯方塊的顏色
                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);

        //繪制網(wǎng)格
        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 {
    /*
     * 用以顯示下一個(gè)俄羅斯方塊
     */

    public TetrisBlock nextBlock = null; //保存下一個(gè)俄羅斯方塊

    public TetrisBlock createNextBlock(){
        /*
         * 創(chuàng)建下一個(gè)俄羅斯方塊
         */
        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) {
            //畫出下一個(gè)俄羅斯方塊

            paint.setColor(color[nextBlock.getColor()]);
            for (BlockUnit u : nextBlock.getUnits()) {

                //設(shè)置填充風(fēng)格
                paint.setStyle(Paint.Style.FILL);
                paint.setColor(color[nextBlock.getColor()]);

                //獲取每個(gè)方塊的橫縱坐標(biāo)
                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) {
        /*
         * 構(gòu)造函數(shù)
         */
        super(context);
    }
    public ShowNextBlockView(Context context, AttributeSet attrs) {
        /*
         * 構(gòu)造函數(shù)
         */
        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="分?jǐn)?shù):" />

                <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>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容