1. 程式人生 > >Android 俄羅斯方塊(附原始碼)

Android 俄羅斯方塊(附原始碼)

  • 首先要畫出遊戲背景牆;
  • 其次,要有方塊,以及方塊單元;
  • 方塊的不同形狀,顏色隨機產生;
  • 遊戲的控制面板。

可能會出現的問題或者難點:

  • 邊界問題:
    ①處於邊界的時候,方塊不可以再左右移動;
    ②下降的時候,到達邊界即底部,則不可繼續下落,此時應該產生一個新的方塊;
  • 與其它方塊接觸問題:
    ①下落的時候,如果碰到其它的方塊則停止下落;
    ②左右移動的時候,移動的過程中,如果接觸到其他方快,則不可再繼續左右移動;
  • 方塊的消除:
    ①呼叫方塊消除方法的時間:當方塊下落到底部的時候,判斷是否有需要消除的行;
    ②消除某一行之後,應該把這一行上面的全部方塊下移一行;
  • 方塊的旋轉:
    在當前專案中,我採用的是順時針旋轉。
    ①當旋轉的時候,如果出現方塊部分超出了邊界,應該對方塊進行平移,使其回到邊界以內。(曾在網上看到有人做過,判斷旋轉之後是否會超出邊界,如果會超出,則不進行旋轉,我覺得不好,方塊只要沒有下落到底部,我覺得都可以進行旋轉,除了沒有空間讓其旋轉外);
    ②如果空間不足以旋轉,也不可以旋轉。空間不足以旋轉的意思是:比如橫向方向只有兩個的空間,而方塊旋轉後會佔用三個空間,此時也不可進行旋轉;
    ③當無法繼續下落或者下落到了底部也不可再進行旋轉
  • 控制面板:
    ①遊戲開始、暫停、繼續、結束,這些狀態應該怎麼去控制,以及遊戲與控制檯的事件關聯。
  • 未發現的問題:
    因為本人能力,只做到這麼多,如果有人發現問題,可以留言交流,歡迎挑問題。

遊戲的執行介面如下所示,基本的功能以及操作很簡單。

這裡寫圖片描述

下面直接看專案程式碼
專案檔案結構
這裡寫圖片描述

下面分別介紹每個類的功能

  • TetrisViewAW.java遊戲的主介面,背景牆以及方塊都在此TetrisViewAW.Java裡面,就是一個自定義的View ,(預設大家對於自定義View是熟悉的),在改類裡面,有一個遊戲主執行緒,用於控制遊戲的開始,暫停,繼續,停止,以及方塊下落的速率。程式碼我加了很多註釋,看不懂的可以留言。還有一點需要注意,當停止遊戲時,要釋放執行緒,養成好習慣
/**
 * 俄羅斯方塊Game主介面
 * 
 * @sign Created by wang.ao on 2017年1月12日
 */
@SuppressLint("DrawAllocation")
public class TetrisViewAW extends View {
    /** 網格開始座標值,橫縱座標的開始值都是此值 */
    public static final int beginPoint = 10;
    /** 俄羅斯方塊的最大座標 */
    private static int max_x, max_y;
    /** 行數和列數 */
    private
static int num_x = 0, num_y = 0; /** 背景牆畫筆 */ private static Paint paintWall = null; /** 俄羅斯方塊的單元塊畫筆 */ private static Paint paintBlock = null; private static final int BOUND_WIDTH_OF_WALL = 2; /** 當前正在下落的方塊 */ private List<BlockUnit> blockUnits = new ArrayList<BlockUnit>(); /** 下一個要顯示的方塊 */ private List<BlockUnit> blockUnitBufs = new ArrayList<BlockUnit>(); /** 下一個要顯示的方塊 */ private List<BlockUnit> routeBlockUnitBufs = new ArrayList<BlockUnit>(); /** 全部的方塊allBlockUnits */ private List<BlockUnit> allBlockUnits = new ArrayList<BlockUnit>(); /** 呼叫此物件的Activity物件 */ private TetrisActivityAW father = null; private int[] map = new int[100]; // 儲存每行網格中包含俄羅斯方塊單元的個數 /** 遊戲的主執行緒 */ private Thread mainThread = null; // 遊戲的幾種狀態 /** 標識遊戲是開始還是停止 */ private boolean gameStatus = false; /** 標識遊戲是暫停還是執行 */ private boolean runningStatus = false; /** 俄羅斯方塊顏色陣列 */ private static final int color[] = { Color.parseColor("#FF6600"), Color.BLUE, Color.RED, Color.GREEN, Color.GRAY }; /** 方塊的中心方塊單元的座標, */ private int xx, yy; /** 方塊,使用者隨機獲取各種形狀的方塊 */ private TetrisBlock tetrisBlock; /** 分數 */ private int score = 0; /** 當前方塊的型別 */ private int blockType = 0; public TetrisViewAW(Context context) { this(context, null); } public TetrisViewAW(Context context, AttributeSet attrs) { super(context, attrs); if (paintWall == null) {// 初始化化背景牆畫筆 paintWall = new Paint(); paintWall.setColor(Color.LTGRAY); paintWall.setStyle(Paint.Style.STROKE); paintWall.setStrokeWidth(BOUND_WIDTH_OF_WALL + 1); } if (paintBlock == null) {// 初始化化背景牆畫筆 paintBlock = new Paint(); paintBlock.setColor(Color.parseColor("#FF6600")); } tetrisBlock = new TetrisBlock(); routeBlockUnitBufs = tetrisBlock.getUnits(beginPoint, beginPoint); Arrays.fill(map, 0); // 每行網格中包含俄羅斯方塊單元的個數全部初始化為0 // 繪製方塊 } /** * 設定當前遊戲頁面的父類activity * * @param tetrisActivityAW */ public void setFather(TetrisActivityAW tetrisActivityAW) { father = tetrisActivityAW; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); max_x = getWidth(); max_y = getHeight(); RectF rel; // 繪製網格 num_x = 0; num_y = 0; for (int i = beginPoint; i < max_x - BlockUnit.UNIT_SIZE; i += BlockUnit.UNIT_SIZE) { for (int j = beginPoint; j < max_y - BlockUnit.UNIT_SIZE; j += BlockUnit.UNIT_SIZE) { rel = new RectF(i, j, i + BlockUnit.UNIT_SIZE, j + BlockUnit.UNIT_SIZE); canvas.drawRoundRect(rel, 8, 8, paintWall); num_y++; } num_x++; } // 隨機產生一個俄羅斯方塊 int len = blockUnits.size(); // 繪製方塊 // Toast.makeText(context, "" + len, Toast.LENGTH_SHORT).show(); for (int i = 0; i < len; i++) { int x = blockUnits.get(i).x; int y = blockUnits.get(i).y; // 設定當前方塊的顏色 paintBlock.setColor(color[blockUnits.get(i).color]); rel = new RectF(x + BOUND_WIDTH_OF_WALL, y + BOUND_WIDTH_OF_WALL, x + BlockUnit.UNIT_SIZE - BOUND_WIDTH_OF_WALL, y + BlockUnit.UNIT_SIZE - BOUND_WIDTH_OF_WALL); canvas.drawRoundRect(rel, 8, 8, paintBlock); } // 隨機產生一個俄羅斯方塊 len = allBlockUnits.size(); // 繪製方塊 // Toast.makeText(context, "" + len, Toast.LENGTH_SHORT).show(); for (int i = 0; i < len; i++) { int x = allBlockUnits.get(i).x; int y = allBlockUnits.get(i).y; paintBlock.setColor(color[allBlockUnits.get(i).color]); rel = new RectF(x + BOUND_WIDTH_OF_WALL, y + BOUND_WIDTH_OF_WALL, x + BlockUnit.UNIT_SIZE - BOUND_WIDTH_OF_WALL, y + BlockUnit.UNIT_SIZE - BOUND_WIDTH_OF_WALL); canvas.drawRoundRect(rel, 8, 8, paintBlock); } } /** * 開始遊戲 */ public void startGame() { gameStatus = true; runningStatus = true; if (mainThread == null || !mainThread.isAlive()) { getNewBlock(); mainThread = new Thread(new MainThread()); mainThread.start(); } } /** * 暫停遊戲 */ public void pauseGame() { runningStatus = false; } /** * 繼續遊戲 */ public void continueGame() { runningStatus = true; } /** * 停止遊戲 */ public void stopGame() { // 停止遊戲,釋放遊戲主執行緒 runningStatus = false; gameStatus = false; mainThread.interrupt(); blockUnits.clear(); allBlockUnits.clear(); score = 0; invalidate(); } /** * 向左滑動 */ public void toLeft() { if (BlockUnit.toLeft(blockUnits, max_x, allBlockUnits)) { xx = xx - BlockUnit.UNIT_SIZE; } invalidate(); } /** * 向右滑動 */ public void toRight() { if (BlockUnit.toRight(blockUnits, max_x, allBlockUnits)) { xx = xx + BlockUnit.UNIT_SIZE; } invalidate(); } /** * 按順時針旋轉 */ public void route() { if (blockType == 3) {// 如果當前正在下落的方塊為正方形,則不進行旋轉 return; } if (routeBlockUnitBufs.size() != blockUnits.size()) { routeBlockUnitBufs = tetrisBlock.getUnits(xx, yy); } for (int i = 0; i < blockUnits.size(); i++) { routeBlockUnitBufs.get(i).x = blockUnits.get(i).x; routeBlockUnitBufs.get(i).y = blockUnits.get(i).y; } for (BlockUnit blockUnit : routeBlockUnitBufs) { int tx = blockUnit.x; int ty = blockUnit.y; blockUnit.x = -(ty - yy) + xx; blockUnit.y = tx - xx + yy; } routeTran(routeBlockUnitBufs); if (!BlockUnit.canRoute(routeBlockUnitBufs, allBlockUnits)) { // Toast.makeText(father, "不可旋轉", Toast.LENGTH_SHORT).show(); return; } for (BlockUnit blockUnit : blockUnits) { int tx = blockUnit.x; int ty = blockUnit.y; blockUnit.x = -(ty - yy) + xx; blockUnit.y = tx - xx + yy; } routeTran(blockUnits); invalidate(); } /** * 如果方塊處於邊緣,則翻轉過後,會出現方塊部分處於邊緣之外的情況, 因此,通過遞迴判斷是否有超出邊緣的部分, * 如果有,則進行左右平移,把處於邊緣外的方塊移動到邊緣內 */ public void routeTran(List<BlockUnit> blockUnitsBuf) { boolean needLeftTran = false; boolean needRightTran = false; for (BlockUnit u : blockUnitsBuf) { if (u.x < beginPoint) { needLeftTran = true; } if (u.x > max_x - BlockUnit.UNIT_SIZE) { needRightTran = true; } } if (needLeftTran || needRightTran) { for (BlockUnit u : blockUnitsBuf) { if (needLeftTran) { u.x = u.x + BlockUnit.UNIT_SIZE; } else if (needRightTran) { u.x = u.x - BlockUnit.UNIT_SIZE; } } routeTran(blockUnitsBuf); } else { return; } } /** * 獲取一個新的方塊 */ private void getNewBlock() { // 新的方塊的座標,x座標位於x軸的中間,y 位於起始位置 this.xx = beginPoint + (num_x / 2) * BlockUnit.UNIT_SIZE; this.yy = beginPoint; if (blockUnitBufs.size() == 0) { // 當遊戲第一次開始的時候,先初始化一個方塊 blockUnitBufs = tetrisBlock.getUnits(xx, yy); } blockUnits = blockUnitBufs; blockType = tetrisBlock.blockType; blockUnitBufs = tetrisBlock.getUnits(xx, yy); if (father != null) {// 顯示出下一個要出現的方塊 father.setNextBlockView(blockUnitBufs, (num_x / 2) * BlockUnit.UNIT_SIZE); } } /** * 遊戲的主執行緒 * * @sign Created by wang.ao on 2017年1月16日 */ private class MainThread implements Runnable { @Override public void run() { while (gameStatus) { while (runningStatus) { if (BlockUnit.canMoveToDown(blockUnits, max_y, allBlockUnits)) { // 判斷是否可以繼續下落,如果可以下落,則下落 BlockUnit.toDown(blockUnits, max_y, allBlockUnits); yy = yy + BlockUnit.UNIT_SIZE; } else { /** * 當不可以繼續下落的時候,把當前的方塊新增到allBlockUnits中, * 並且判斷是否有需要消除的方塊,然後再產生一個新的方塊 */ for (BlockUnit blockUnit : blockUnits) { blockUnit.y = blockUnit.y + BlockUnit.UNIT_SIZE; allBlockUnits.add(blockUnit); } for (BlockUnit u : blockUnits) { // 更新map,即更新每行網格中靜止俄羅斯方塊單元的個數 int index = (int) ((u.y - beginPoint) / 50); // 計算所在行數 map[index]++; } // 每行最大個數 int end = (int) ((max_y - 50 - beginPoint) / BlockUnit.UNIT_SIZE); int full = (int) ((max_x - 50 - beginPoint) / BlockUnit.UNIT_SIZE) + 1; try { Thread.sleep(GameConfig.SPEED); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i <= end; i++) { /*** * 消除需要消除的方塊(觸發條件,某一行中被塞滿了方塊,沒有空白) * 注意順序,先消除某一行,再移動這一行上邊的方塊 */ if (map[i] >= full) { BlockUnit.remove(allBlockUnits, i); score += 100; map[i] = 0; for (int j = i; j > 0; j--) map[j] = map[j - 1]; map[0] = 0; for (BlockUnit blockUnit : allBlockUnits) { if (blockUnit.y < (i * BlockUnit.UNIT_SIZE + beginPoint)) { blockUnit.y = blockUnit.y + BlockUnit.UNIT_SIZE; } } } } father.runOnUiThread(new Runnable() { @Override public void run() { /** * 重新整理分數 */ father.score.setText("" + score); invalidate(); } }); try { Thread.sleep(GameConfig.SPEED * 3); } catch (InterruptedException e) { e.printStackTrace(); } father.runOnUiThread(new Runnable() { @Override public void run() { getNewBlock(); score += 10; father.score.setText("" + score); } }); } father.runOnUiThread(new Runnable() { @Override public void run() { invalidate(); } }); try { Thread.sleep(GameConfig.SPEED); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
  • BlockUnit.java方塊的單元塊,大家都玩過俄羅斯方塊,每一個方塊由四個單元塊組成。單元快應該有以下屬性:①大小:單元塊的大小決定了主介面的容量(容納單元塊的數量);②顏色:每個單元塊都有一個顏色,美化遊戲介面(可無);③座標:包括X軸座標、Y軸座標,在繪製方塊的時候,以單元塊的座標為起點繪製,即:單元塊的座標值應該為單元塊在介面上的左上角的座標。
  • 此類的主要功能有:方塊的下落,左右移動,判斷是否可以旋轉等功能都在此類中,算是核心類。
/**
 * 俄羅斯方塊的單元快
 * 
 * @sign Created by wang.ao on 2017年1月13日
 */
public class BlockUnit {
    public static final int UNIT_SIZE = 50;
    public static final int BEGIN = 10;
    public int color;
    // 單元塊 的座標
    public int x, y;

    public BlockUnit() {
    }

    public BlockUnit(int x, int y, int color) {
        /*
         * @param 單元塊橫縱座標 建構函式
         */
        this.x = x;
        this.y = y;
        this.color = color;
    }

    /**
     * 判斷方塊是否可以向左移動,1是否在邊緣,2是否會與其他方塊重合
     * @param blockUnits 當前正在下落的方塊
     * @param max_x 遊戲主介面X軸的最大值 ,下同
     * @param allBlockUnits 所有的方塊
     * @return 能移動true;不能移動false
     */
    public static boolean canMoveToLeft(List<BlockUnit> blockUnits, int max_x, List<BlockUnit> allBlockUnits) {
        for (BlockUnit blockUnit : blockUnits) {
            int x = blockUnit.x;
            if (x - UNIT_SIZE < BEGIN) {
                return false;
            }
            int y = blockUnit.y;
            if (isSameUnit(x - UNIT_SIZE, y, allBlockUnits)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 判斷方塊是否可以向右移動,1是否在邊緣,2是否會與其他方塊重合
     * @param blockUnits 當前正在下落的方塊
     * @param max_x 遊戲主介面X軸的最大值 ,下同
     * @param allBlockUnits 所有的方塊
     * @return 能移動true;不能移動false
     */
    public static boolean canMoveToRight(List<BlockUnit> blockUnits, int max_x, List<BlockUnit> allBlockUnits) {
        for (BlockUnit blockUnit : blockUnits) {
            int x = blockUnit.x;
            if (x + UNIT_SIZE > max_x - UNIT_SIZE) {
                return false;
            }
            int y = blockUnit.y;
            if (isSameUnit(x + UNIT_SIZE, y, allBlockUnits)) {
                return false;
            }
        }
        return true;
    }
    /**
     * 判斷方塊是否可以向下移動,1是否在邊緣,2是否會與其他方塊重合
     * @param blockUnits 當前正在下落的方塊
     * @param max_x 遊戲主介面X軸的最大值 ,下同
     * @param allBlockUnits 所有的方塊
     * @return 能移動true;不能移動false
     */
    public static boolean canMoveToDown(List<BlockUnit> blockUnits, int max_y, List<BlockUnit> allBlockUnits) {
        for (BlockUnit blockUnit : blockUnits) {
            int x = blockUnit.x;
            int y = blockUnit.y + UNIT_SIZE * 2;
            if (y > max_y - UNIT_SIZE) {
                return false;
            }
            if (isSameUnit(x, y, allBlockUnits)) {
                return false;
            }
        }
        return true;
    }
    public static boolean canRoute(List<BlockUnit> blockUnits, List<BlockUnit> allBlockUnits){
        for (BlockUnit blockUnit: blockUnits) {
            if(isSameUnit(blockUnit.x, blockUnit.y, allBlockUnits)){
                return false;
            }
        }
        return true;
    }

    /**
     * 把當前方塊向左移動一格
     * @param blockUnits
     * @param max_x
     * @param allBlockUnits
     * @return 是否成功移動一格,是:true,否:false
     */
    public static boolean toLeft(List<BlockUnit> blockUnits, int max_x, List<BlockUnit> allBlockUnits) {
        if (canMoveToLeft(blockUnits, max_x, allBlockUnits)) {
            for (BlockUnit blockUnit : blockUnits) {
                blockUnit.x = blockUnit.x - UNIT_SIZE;
            }
            return true;
        }
        return false;
    }
    /**
     * 把當前方塊向右移動一格
     * @param blockUnits
     * @param max_x
     * @param allBlockUnits
     * @return 是否成功移動一格,是:true,否:false
     */
    public static boolean toRight(List<BlockUnit> blockUnits, int max_x, List<BlockUnit> allBlockUnits) {
        if (canMoveToRight(blockUnits, max_x, allBlockUnits)) {
            for (BlockUnit blockUnit : blockUnits) {
                blockUnit.x = blockUnit.x + UNIT_SIZE;
            }
            return true;
        }
        return false;
    }

    /**
     * 把當前方塊下落一格
     * @param blockUnits
     * @param allBlockUnits
     * @return 是否成功移動一格,是:true,否:false
     */
    public static void toDown(List<BlockUnit> blockUnits, int max_Y, List<BlockUnit> allBlockUnits) {
        for (BlockUnit blockUnit : blockUnits) {
            blockUnit.y = blockUnit.y + BlockUnit.UNIT_SIZE;
        }
    }

    /**
     * 判斷 方塊單元是否和所有方塊有重合
     * @param x
     * @param y
     * @param allBlockUnits
     * @return
     */
    public static boolean isSameUnit(int x, int y, List<BlockUnit> allBlockUnits) {
        for (BlockUnit blockUnit : allBlockUnits) {
            if (Math.abs(x - blockUnit.x) < UNIT_SIZE && Math.abs(y - blockUnit.y) < UNIT_SIZE) {
                return true;
            }
        }
        return false;
    }

    /**
     * 刪除在第j行上的方塊單元
     * @param allBlockUnits
     * @param j 需刪除行標
     */
    public static void remove(List<BlockUnit> allBlockUnits, int j) {
        for (int i = allBlockUnits.size() - 1; i >= 0; i--) {
            /*
             * ①逆向遍歷 ②根據y座標計算單元所在行,若為j行則從units中刪除
             */
            if ((int) ((allBlockUnits.get(i).y - BEGIN) / 50) == j)
                allBlockUnits.remove(i);
        }
    }
}
  • TetrisBlock.java用於產生不同形狀的方塊,共有其中型別。
/**
 * 方塊
 * 
 * @sign Created by wang.ao on 2017年1月13日
 */
public class TetrisBlock {
    private static final int TYPE_SUM = 7;
    public int blockType, blockDirection; // 方塊種類,方塊朝向

    private int color; // 方塊顏色

    private int x, y; // 方塊座標

    public TetrisBlock() {

    }

    public TetrisBlock(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public List<BlockUnit> getUnits(int x, int y) {
        this.x = x;
        this.y = y;
        return returnUnit();
    }

    /**
     * 隨機產生一種方塊
     * @return
     */
    public List<BlockUnit> returnUnit() {
        List<BlockUnit> units = new ArrayList<BlockUnit>(); // 方塊組成部分
        blockType = (int) (Math.random() * TYPE_SUM) + 1; // 隨機生成一個種類
        blockDirection = 1; // 預設初始方向
        color = (int) (Math.random() * 4) + 1; // 隨機生成一個顏色
        units.clear();
        switch (blockType) {
        case 1:// 橫線
            for (int i = 0; i < 4; i++) {
                units.add(new BlockUnit(x + (-2 + i) * BlockUnit.UNIT_SIZE, y, color));
            }
            break;
        case 2:
            units.add(new BlockUnit(x + (-1 + 1) * BlockUnit.UNIT_SIZE, y - BlockUnit.UNIT_SIZE, color));
            for (int i = 0; i < 3; i++) {
                units.add(new BlockUnit(x + (-1 + i) * BlockUnit.UNIT_SIZE, y, color));
            }
            break;
        case 3:
            for (int i = 0; i < 2; i++) {
                units.add(new BlockUnit(x + (i - 1) * BlockUnit.UNIT_SIZE, y - BlockUnit.UNIT_SIZE, color));
                units.add(new BlockUnit(x + (i - 1) * BlockUnit.UNIT_SIZE, y, color));
            }
            break;
        case 4:
            units.add(new BlockUnit(x + (-1 + 0) * BlockUnit.UNIT_SIZE, y - BlockUnit.UNIT_SIZE, color));
            for (int i = 0; i < 3; i++) {
                units.add(new BlockUnit(x + (-1 + i) * BlockUnit.UNIT_SIZE, y, color));
            }
            break;
        case 5:
            units.add(new BlockUnit(x + (-1 + 2) * BlockUnit.UNIT_SIZE, y - BlockUnit.UNIT_SIZE, color));
            for (int i = 0; i < 3; i++) {
                units.add(new BlockUnit(x + (-1 + i) * BlockUnit.UNIT_SIZE, y, color));
            }
            break;
        case 6:
            for (int i = 0; i < 2; i++) {
                units.add(new BlockUnit(x + (-1 + i) * BlockUnit.UNIT_SIZE, y - BlockUnit.UNIT_SIZE, color));
                units.add(new BlockUnit(x + i * BlockUnit.UNIT_SIZE, y, color));
            }
            break;
        case 7:
            for (int i = 0; i < 2; i++) {
                units.add(new BlockUnit(x + i * BlockUnit.UNIT_SIZE, y - BlockUnit.UNIT_SIZE, color));
                units.add(new BlockUnit(x + (-1 + i) * BlockUnit.UNIT_SIZE, y, color));
            }
            break;
        }
        return units;
    }
  • NextBlockView.java其實就是遊戲主介面的一個縮減版,用於顯示下一個要出現的方塊的,玩家可以明確的知道下一個方塊的形狀和顏色。
/**
 * 下一個要展示的方塊
 * 
 * @sign Created by wang.ao on 2017年1月13日
 */
@SuppressLint("DrawAllocation")
public class NextBlockView extends View {
    /** 網格開始座標值,橫縱座標的開始值都是此值 */
    public static final int beginPoint = 10;
    /** 俄羅斯方塊的最大座標 */
    private static int max_x, max_y;
    private List<BlockUnit> blockUnits = new ArrayList<BlockUnit>();
    /** 背景牆畫筆 */
    private static Paint paintWall = null;
    private static final int BOUND_WIDTH_OF_WALL = 2;
    private static Paint paintBlock = null;
    private int div_x = 0;
    // 俄羅斯方塊顏色陣列
    private static final int color[] ={ Color.parseColor("#FF6600"), Color.BLUE, Color.RED, Color.GREEN, Color.GRAY };

    public NextBlockView(Context context) {
        this(context, null);
    }

    public NextBlockView(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (paintWall == null) {// 初始化化背景牆畫筆
            paintWall = new Paint();
            paintWall.setColor(Color.LTGRAY);
            paintWall.setStyle(Paint.Style.STROKE);
            paintWall.setStrokeWidth(BOUND_WIDTH_OF_WALL + 1);
        }
        if (paintBlock == null) {// 初始化化背景牆畫筆
            paintBlock = new Paint();
            paintBlock.setColor(Color.parseColor("#FF6600"));
        }
    }

    public void setBlockUnits(List<BlockUnit> blockUnits, int div_x) {
        this.blockUnits = blockUnits;
        this.div_x = div_x;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        max_x = getWidth();
        max_y = getHeight();
        RectF rel;
        // 繪製網格
        int len = blockUnits.size();
        // 繪製方塊
        // Toast.makeText(context, "" + len, Toast.LENGTH_SHORT).show();
        for (int i = 0; i < len; i++) {
            paintBlock.setColor(color[blockUnits.get(i).color]);
            int x = blockUnits.get(i).x - div_x + BlockUnit.UNIT_SIZE * 2;
            int y = blockUnits.get(i).y + BlockUnit.UNIT_SIZE * 2;
            rel = new RectF(x + BOUND_WIDTH_OF_WALL, y + BOUND_WIDTH_OF_WALL,
                    x + BlockUnit.UNIT_SIZE - BOUND_WIDTH_OF_WALL, y + BlockUnit.UNIT_SIZE - BOUND_WIDTH_OF_WALL);
            canvas.drawRoundRect(rel, 8, 8, paintBlock);
            rel = new RectF(x, y, x + BlockUnit.UNIT_SIZE, y + BlockUnit.UNIT_SIZE);
            canvas.drawRoundRect(rel, 8, 8, paintWall);
        }
    }

}
  • GameConfig.java用於配置方塊的下落速度
public class GameConfig {
    /**方塊下落的速度*/
    public static final int SPEED = 300;
}
  • TetrisActivityAW.java主介面,包括遊戲主介面和控制檯,很簡單,直接貼程式碼。
public class TetrisActivityAW extends Activity {
    private NextBlockView nextBlockView;
    private TetrisViewAW tetrisViewAW;
    private TextView gameStatusTip;
    public TextView score;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tetris_activity_aw);
        nextBlockView = (NextBlockView) findViewById(R.id.nextBlockView1);
        tetrisViewAW = (TetrisViewAW) findViewById(R.id.tetrisViewAW1);
        tetrisViewAW.setFather(this);
        gameStatusTip = (TextView) findViewById(R.id.game_staus_tip);
        score = (TextView) findViewById(R.id.score);
    }

    public void setNextBlockView(List<BlockUnit> blockUnits, int div_x) {
        nextBlockView.setBlockUnits(blockUnits, div_x);
    }

    /**
     * 開始遊戲
     * 
     * @param view
     */
    public void startGame(View view) {
        tetrisViewAW.startGame();
        gameStatusTip.setText("遊戲執行中");
    }

    /**
     * 暫停遊戲
     */
    public void pauseGame(View view) {
        tetrisViewAW.pauseGame();
        gameStatusTip.setText("遊戲已暫停");
    }

    /**
     * 繼續遊戲
     */
    public void continueGame(View view) {
        tetrisViewAW.continueGame();
        gameStatusTip.setText("遊戲執行中");
    }

    /**
     * 停止遊戲
     */
    public void stopGame(View view) {
        tetrisViewAW.stopGame();
        score.setText(""+0);
        gameStatusTip.setText("遊戲已停止");
    }

    /**
     * 向左滑動
     */
    public void toLeft(View view) {
        tetrisViewAW.toLeft();
    }

    /**
     * 向右滑動
     */
    public void toRight(View view) {
        tetrisViewAW.toRight();
    }
    /**
     * 向右滑動
     */
    public void toRoute(View view) {
        tetrisViewAW.route();
    }
}
  • TetrisActivityAW activity的xml檔案
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <com.awang.media.minetetris.TetrisViewAW
        android:id="@+id/tetrisViewAW1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="200dp"
        android:layout_marginRight="120dp" />

    <com.awang.media.minetetris.NextBlockView
        android:id="@+id/nextBlockView1"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:layout_alignParentRight="true" />

    <LinearLayout
        android:layout_width="110dp"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/nextBlockView1"
        android:layout_marginTop="20dp"
        android:orientation="vertical" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="分數" />

            <TextView
                android:id="@+id/score"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:text="1" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:orientation="horizontal" >

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="等級" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:text="1" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:orientation="horizontal" >

            <TextView
                a