Swing俄羅斯遊戲編寫詳解(附源碼)

俄羅斯方塊遊戲是一個上手簡單,老小皆宜的遊戲,它的基本規則是移動旋轉擺放遊戲自動產生的各類方塊,使之排列成完整的一行或多行而且消除得分。java

你能學到什麼?

經過本文的閱讀,讀者能夠對Swing版俄羅斯方塊遊戲的自己,對遊戲中的關鍵點,如圖形變換、鍵盤事件處理、遊戲進度保存、滿行和消行等都會有較好的理解。數組

遊戲界面

界面組成

遊戲界面有四個部分組成:app

  1. 遊戲面板區 -- 遊戲主面板
  2. 下一個圖形預覽區 -- 呈現下一個圖形
  3. 分數顯示區 -- 顯示目前的得分
  4. 菜單欄 -- 具有幫助、遊戲設置、進度保存等功能

 

 

圖形選擇

通常來說,一個圖形有四個點,能夠表示出經常使用的「一字型」,「T字型」,「Z字型」以及「L字型」方塊。dom

若是將四個點的一個或者多個重疊,或者不採用經常使用的「一字型」,「T字型」,「Z字型」以及「L字型」方塊,那麼能夠演變出更多的圖形出來。若是想要更加豐富和複雜的圖形,可使用更多的點去表示想要的圖形。ide

四個點組成的圖形有以下幾種:ui

常規圖形

 

 

 

 

 

非重合的常規圖形

若是將四個點中的一個或者多個點重合,就能夠有以下幾種類型的圖形:this


 

無重合點的不常規圖

若是四個點不重合,還能夠有以下幾種比較詭異的圖形,能夠爲遊戲增長難度。spa

圖形對象類表示code

格子類( Grid.java ) ,俄羅斯方塊遊戲中的圖形由四個格子組成。每一個格子類有下x , y兩個座標位置, 顏色,以及格子圖形繪製方法等。orm

如:

package my.games.russia.model;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;

import my.games.russia.constants.RussiaGameConstant;

/**
 * 格子,俄羅斯方塊遊戲中的圖形由四個格子組成
 * @author wangmengjun
 *
 */
public class Grid implements Serializable {

    private static final long serialVersionUID = -3722886719784770741L;

    /**x位置*/
    private int x;

    /**y位置*/
    private int y;

    /**格子的顏色*/
    private Color color;
    
    public Grid() {
    }

    public Grid(int x, int y, Color color) {
        this.x = x;
        this.y = y;
        this.color = color;
    }
    
    public void draw(Graphics2D g2) {
       //TODO:
    }
 
}

圖形抽象類 ( AbstractRussiaSquare .java )。抽象類包含一些公用的屬性(如:每一個圖形由四個方塊組成)、公用的方法(如向左移動、向右移動)、抽象方法(如圖形變換須要子類實現細節)。

如:

package my.games.russia.model;

import java.awt.Color;
import java.awt.Graphics2D;
import java.io.Serializable;
import my.games.russia.constants.RussiaGameConstant;

/**
 * 俄羅斯方塊遊戲,圖形的抽象類
 * @author wangmengjun
 *
 */
public abstract class AbstractRussiaSquare implements Serializable {

    private static final long serialVersionUID = 192398482620404584L;

    /**每個圖形都是有四個小方塊Grid組成*/
    protected Grid[] grids = { null, null, null, null };

    /**xLocations*/
    protected int[] xLocations = { 0, 0, 0, 0 };

    /**yLocations*/
    protected int[] yLocations = { 0, 0, 0, 0 };

    /**圖形是否alive,便是否還能變換*/
    protected boolean alive;

    /**圖形格子的顏色*/
    protected Color color;

    /**圖形初始狀態,圖形轉換的狀態*/
    public int state;

    public AbstractRussiaSquare() {
        int r = (int) (Math.random() * 256);
        int g = (int) (Math.random() * 256);
        int b = (int) (Math.random() * 256);
        this.color = new Color(r, g, b);
        grids[0] = new Grid(0, 0, color);
        grids[1] = new Grid(0, 0, color);
        grids[2] = new Grid(0, 0, color);
        grids[3] = new Grid(0, 0, color);
        alive = true;
    }

    /**
     * 圖形繪製
     */
    public void draw(Graphics2D g2) {
        for(Grid grid : grids) {
            grid.draw(g2);
        }
    }

    /**
     * 往左移動
     * 
     * @param flag
     */
    protected void moveLeft(int[][] flags) {
        if (!alive) {
            return;
        }
        //TODO:座標變化
    }

    /**
     * 往右移動
     * 
     * @param flag
     */
    protected void moveLeft(int[][] flags) {
        if (!alive) {
            return;
        }
        //TODO:座標變化
    }

    /**
     * 往下移動
     * 
     * @param flag
     */
    protected void moveDown(int[][] flags) {
        if (!alive) {
            return;
        }
        //TODO:座標變化
    }
    protected void isStateChangeAllowed(int[][] flags, int state) {
      //TODO
    }
    /**

    /**
     * 每一個圖形的圖形變化各不相同,須要子類實現細節
     */
    protected abstract void changeState(int[][] flags);

}

每一個子類,只要繼承了該抽象父類便可擁有公有的方法和屬性,實現父類定義的抽象方法就能夠擁有自身的實現細節。

關鍵點

圖形變換

圖形變換或者圖形旋轉是俄羅斯方塊遊戲中比較關鍵的一個部分。

圖形變換的思想?

由於一個圖形有四個點來表示,能夠先肯定其中的一個點的變換位置而後其它的三個點根據這個肯定的基點進行位置調整就能夠了。 
 

好比,

一字型的圖形爲例,只要第二號格子爲固定點,相繼算出其它格子的位置便可。

 

package my.games.russia.model;

import java.util.Random;

/**
 * 一字型
 * @author wangmengjun
 *
 */
public class RussiaSquare_2 extends AbstractRussiaSquare {

    private static final long serialVersionUID = -4746450405460864752L;

    public RussiaSquare_2() {
        initGrids();
    }

    /* (non-Javadoc)
     * @see my.games.russia.model.AbstractRussiaSquare#changeState(int[][])
     */
    @Override
    public void changeState(int[][] flags) {

        int fixedXLocation = grids[1].getX();
        int fixedYLocation = grids[1].getY();

        switch (state) {
            case 0:
                /**
                 * 橫向到豎直方向轉換
                 */
                /**
                 * 第二個點保持不變
                 */

                xLocations[1] = fixedXLocation;
                yLocations[1] = fixedYLocation;

                xLocations[0] = fixedXLocation;
                yLocations[0] = fixedYLocation - 1;

                xLocations[2] = fixedXLocation;
                yLocations[2] = fixedYLocation + 1;
                xLocations[3] = fixedXLocation;
                yLocations[3] = fixedYLocation + 2;
                isAllowChangeState(flags, 2);
                break;
            case 1:
                xLocations[1] = fixedXLocation;
                yLocations[1] = fixedYLocation;

                /**
                 * 豎直到橫向轉換
                 */
                xLocations[0] = fixedXLocation - 1;
                yLocations[0] = fixedYLocation;

                xLocations[2] = fixedXLocation + 1;
                yLocations[2] = fixedYLocation;
                xLocations[3] = fixedXLocation + 2;
                yLocations[3] = fixedYLocation;
                isAllowChangeState(flags, 2);
                break;

            default:
                break;
        }

    }

    /* (non-Javadoc)
     * @see my.games.russia.model.AbstractRussiaSquare#initGrids()
     */
    @Override
    public void initGrids() {
        state = new Random().nextInt(2);
        switch (state) {
            case 0:
                /**
                 * 豎直
                 */
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX());
                grids[1].setY(grids[0].getY() + 1);
                grids[2].setX(grids[0].getX());
                grids[2].setY(grids[0].getY() + 2);
                grids[3].setX(grids[0].getX());
                grids[3].setY(grids[0].getY() + 3);
                break;

            case 1:
                /**
                 * 橫向
                 */
                grids[0].setX(8);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX() + 1);
                grids[1].setY(grids[0].getY());
                grids[2].setX(grids[0].getX() + 2);
                grids[2].setY(grids[0].getY());
                grids[3].setX(grids[0].getX() + 3);
                grids[3].setY(grids[0].getY());
                break;
            default:
                break;
        }

    }

}

 

採用相似的方法,能夠寫出T字形的方塊轉換:

package my.games.russia.model;

import java.util.Random;


/**
 * T字型方塊
 * @author wangmengjun
 *
 */
public class RussiaSquare_3 extends AbstractRussiaSquare {

    private static final long serialVersionUID = -8336206016924545562L;

    public RussiaSquare_3() {
        initGrids();
    }

    /* (non-Javadoc)
     * @see my.games.russia.model.AbstractRussiaSquare#changeState(int[][])
     */
    @Override
    public void changeState(int[][] flags) {
        switch (state) {
            case 0:
                xLocations[0] = grids[0].getX();
                yLocations[0] = grids[0].getY();
                xLocations[1] = xLocations[0];
                yLocations[1] = yLocations[0] + 1;
                xLocations[2] = xLocations[0] + 1;
                yLocations[2] = yLocations[0] + 1;
                xLocations[3] = xLocations[0];
                yLocations[3] = yLocations[0] + 2;
                isAllowChangeState(flags, 4);
                break;

            case 1:
                xLocations[0] = grids[0].getX() - 1;
                yLocations[0] = grids[0].getY() + 1;
                xLocations[1] = xLocations[0] + 1;
                yLocations[1] = yLocations[0];
                xLocations[2] = xLocations[0] + 2;
                yLocations[2] = yLocations[0];
                xLocations[3] = xLocations[0] + 1;
                yLocations[3] = yLocations[0] + 1;
                isAllowChangeState(flags, 4);
                break;
            case 2:
                xLocations[0] = grids[0].getX() + 1;
                yLocations[0] = grids[0].getY() - 1;
                xLocations[1] = xLocations[0] - 1;
                yLocations[1] = yLocations[0] + 1;
                xLocations[2] = xLocations[0];
                yLocations[2] = yLocations[0] + 1;
                xLocations[3] = xLocations[0];
                yLocations[3] = yLocations[0] + 2;
                isAllowChangeState(flags, 4);
                break;

            case 3:
                xLocations[0] = grids[0].getX();
                yLocations[0] = grids[0].getY();
                xLocations[1] = xLocations[0] - 1;
                yLocations[1] = yLocations[0] + 1;
                xLocations[2] = xLocations[0];
                yLocations[2] = yLocations[0] + 1;
                xLocations[3] = xLocations[0] + 1;
                yLocations[3] = yLocations[0] + 1;
                isAllowChangeState(flags, 4);
                break;

            default:
                break;
        }

    }

    /* (non-Javadoc)
     * @see my.games.russia.model.AbstractRussiaSquare#initGrids()
     */
    @Override
    public void initGrids() {
        state = new Random().nextInt(4);
        switch (state) {
            case 0:
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX() - 1);
                grids[1].setY(grids[0].getY() + 1);
                grids[2].setX(grids[0].getX());
                grids[2].setY(grids[0].getY() + 1);
                grids[3].setX(grids[0].getX() + 1);
                grids[3].setY(grids[0].getY() + 1);
                break;

            case 1:
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX());
                grids[1].setY(grids[0].getY() + 1);
                grids[2].setX(grids[0].getX() + 1);
                grids[2].setY(grids[0].getY() + 1);
                grids[3].setX(grids[0].getX());
                grids[3].setY(grids[0].getY() + 2);
                break;

            case 2:
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX() + 1);
                grids[1].setY(grids[0].getY());
                grids[2].setX(grids[0].getX() + 2);
                grids[2].setY(grids[0].getY());
                grids[3].setX(grids[0].getX() + 1);
                grids[3].setY(grids[0].getY() + 1);
                break;

            case 3:
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX() - 1);
                grids[1].setY(grids[0].getY() + 1);
                grids[2].setX(grids[0].getX());
                grids[2].setY(grids[0].getY() + 1);
                grids[3].setX(grids[0].getX());
                grids[3].setY(grids[0].getY() + 2);
                break;

            default:
                break;
        }

    }

}

 

鍵盤事件的處理 

鍵盤事件主要有以下幾個部分

  1. 向左
  2. 向右
  3. 向下
  4. 按向上鍵變換圖形
  5. 按空格鍵直接到最底部

向左

/**
     * 往左移動
     * 
     */
    public void moveLeft(int[][] flags) {
        if (!alive) {
            return;
        }
        /**
         * 將現有的點賦值到xLocation和yLocation上去
         */
        for (int i = 0; i < grids.length; i++) {
            xLocations[i] = grids[i].getX() - 1;
            yLocations[i] = grids[i].getY();
        }

        if (xLocations[0] >= RussiaGameConstant.LEFT && flags[xLocations[0]][yLocations[0]] == 0
                && xLocations[1] >= RussiaGameConstant.LEFT
                && flags[xLocations[1]][yLocations[1]] == 0
                && xLocations[2] >= RussiaGameConstant.LEFT
                && flags[xLocations[2]][yLocations[2]] == 0
                && xLocations[3] >= RussiaGameConstant.LEFT
                && flags[xLocations[3]][yLocations[3]] == 0) {
            for (int i = 0; i < grids.length; i++) {
                grids[i].setX(xLocations[i]);
            }
        }
    }

向右以及向下都和向左操做相似。

向右

/**
     * 往右移動
     */
    public void moveRight(int flags[][]) {
        if (!alive) {
            return;
        }

        for (int i = 0; i < grids.length; i++) {
            xLocations[i] = grids[i].getX() + 1;
            yLocations[i] = grids[i].getY();
        }

        if (xLocations[0] <= RussiaGameConstant.RIGHT && flags[xLocations[0]][yLocations[0]] == 0
                && xLocations[1] <= RussiaGameConstant.RIGHT
                && flags[xLocations[1]][yLocations[1]] == 0
                && xLocations[2] <= RussiaGameConstant.RIGHT
                && flags[xLocations[2]][yLocations[2]] == 0
                && xLocations[3] <= RussiaGameConstant.RIGHT
                && flags[xLocations[3]][yLocations[3]] == 0) {
            for (int i = 0; i < grids.length; i++) {
                grids[i].setX(xLocations[i]);
            }
        }
    }

向下

/**
     * 往下移動
     */
    public void moveDown(int[][] flags) {
        for (int i = 0; i < grids.length; i++) {
            xLocations[i] = grids[i].getX();
            yLocations[i] = grids[i].getY() + 1;
        }

        if (yLocations[0] <= RussiaGameConstant.DOWN && flags[xLocations[0]][yLocations[0]] == 0
                && yLocations[1] <= RussiaGameConstant.DOWN
                && flags[xLocations[1]][yLocations[1]] == 0
                && yLocations[2] <= RussiaGameConstant.DOWN
                && flags[xLocations[2]][yLocations[2]] == 0
                && yLocations[3] <= RussiaGameConstant.DOWN
                && flags[xLocations[3]][yLocations[3]] == 0) {
            for (int i = 0; i < grids.length; i++) {
                grids[i].setY(yLocations[i]);
                ;
            }
        } else {
            alive = false;
        }
    }

變換圖形

每一個圖形,或有一種變換,或者有2種或者有四種變換,能夠根據圖形的特性進行處理。 

好比,將「T字型」圖型按照順時針旋轉,擁有四種變換,建立對象的時候已經肯定了state是多少(state肯定初始的位置是哪裏),此後的變換隻要對state加1並對4求餘就能夠知道怎麼變換。 

/* (non-Javadoc)
     * @see my.games.russia.model.AbstractRussiaSquare#changeState(int[][])
     */
    @Override
    public void changeState(int[][] flags) {
        switch (state) {
            case 0:
                xLocations[0] = grids[0].getX();
                yLocations[0] = grids[0].getY();
                xLocations[1] = xLocations[0];
                yLocations[1] = yLocations[0] + 1;
                xLocations[2] = xLocations[0] + 1;
                yLocations[2] = yLocations[0] + 1;
                xLocations[3] = xLocations[0];
                yLocations[3] = yLocations[0] + 2;
                isAllowChangeState(flags, 4);
                break;

            case 1:
                xLocations[0] = grids[0].getX() - 1;
                yLocations[0] = grids[0].getY() + 1;
                xLocations[1] = xLocations[0] + 1;
                yLocations[1] = yLocations[0];
                xLocations[2] = xLocations[0] + 2;
                yLocations[2] = yLocations[0];
                xLocations[3] = xLocations[0] + 1;
                yLocations[3] = yLocations[0] + 1;
                isAllowChangeState(flags, 4);
                break;
            case 2:
                xLocations[0] = grids[0].getX() + 1;
                yLocations[0] = grids[0].getY() - 1;
                xLocations[1] = xLocations[0] - 1;
                yLocations[1] = yLocations[0] + 1;
                xLocations[2] = xLocations[0];
                yLocations[2] = yLocations[0] + 1;
                xLocations[3] = xLocations[0];
                yLocations[3] = yLocations[0] + 2;
                isAllowChangeState(flags, 4);
                break;

            case 3:
                xLocations[0] = grids[0].getX();
                yLocations[0] = grids[0].getY();
                xLocations[1] = xLocations[0] - 1;
                yLocations[1] = yLocations[0] + 1;
                xLocations[2] = xLocations[0];
                yLocations[2] = yLocations[0] + 1;
                xLocations[3] = xLocations[0] + 1;
                yLocations[3] = yLocations[0] + 1;
                isAllowChangeState(flags, 4);
                break;

            default:
                break;
        }

    }

一鍵到底

按下空格鍵一直向下的操做,其實就是在能夠動的範圍下,一直調用moveDown()方法

case KeyEvent.VK_SPACE:
                    while (sr1.isAlive()) {
                        sr1.moveDown(flag);
                    }

針對鍵盤的操做,你們能夠定義一個內部的事件處理器。

private class KeyHandler implements KeyListener {
        public void keyPressed(KeyEvent event) {
            if (!gameState.isRunState()) {
                return;
            }

            int keyCode = event.getKeyCode();
            switch (keyCode) {
                case KeyEvent.VK_LEFT:
                    sr1.moveLeft(flag);
                    break;

                case KeyEvent.VK_RIGHT:
                    sr1.moveRight(flag);
                    break;

                case KeyEvent.VK_UP:
                    sr1.changeState(flag);
                    break;

                case KeyEvent.VK_DOWN:
                    sr1.moveDown(flag);
                    break;
                case KeyEvent.VK_SPACE:
                    while (sr1.isAlive()) {
                        sr1.moveDown(flag);
                    }
                default:
                    break;
            }
            repaint();
        }

        public void keyReleased(KeyEvent event) {
        }

        public void keyTyped(KeyEvent event) {
        }
    }

而後,在遊戲Panel建立的時候,添加上去便可。

addKeyListener(new KeyHandler());

 

滿行及其消行操做 

用一個二維數組記錄當前屏幕上的方塊狀態,0表示沒有方塊,1表示有方塊

滿行條件?

滿行的判斷就歸結到某一行1的個數是否等於該行列的總數,若是是就知足滿行條件。 

當有滿行狀況出現的時候,須要進行消除和計分操做。 

如何消行?
 

消除行的一個作法就是將該行以上的行統統往下移,移動以後在將第一行的flag所有置爲0

for (int i = RussiaGameConstant.UP; i <= RussiaGameConstant.DOWN; i++) {
                    int count = 0;
                    for (int j = RussiaGameConstant.LEFT; j <= RussiaGameConstant.RIGHT; j++) {
                        count += flag[j][i];
                    }

                    /*
                     * flag[i][j] =1 表示這個位置有小方塊,若是一行的位置都有小方塊,那麼滿行的個數num加1.
                     * 而且消除行。
                     */
                    if (count == RussiaGameConstant.GRID_COLUMN_NUMBER) {
                        num++;

                        /**
                         * 消除行操做。
                         */
                        for (int m = i; m > RussiaGameConstant.UP; m--) {
                            for (int n = RussiaGameConstant.LEFT; n <= RussiaGameConstant.RIGHT; n++) {
                                flag[n][m] = flag[n][m - 1];
                                color[n][m] = color[n][m - 1];
                            }
                        }
                        /*
                         * 從新將第一行的flag[s][0]置爲0
                         */
                        for (int s = RussiaGameConstant.LEFT; s <= RussiaGameConstant.RIGHT; s++) {
                            flag[s][RussiaGameConstant.UP] = 0;
                        }
                    }
                }
/**
     * @param num
     *            方塊滿行的個數
     */
    private void calculateScore(int num) {
        switch (num) {
            case 1:
                score += 10;
                break;
            case 2:
                score += 20;
                break;
            case 3:
                score += 50;
                break;
            case 4:
                score += 100;
                break;
            default:
                break;

        }
    }

遊戲結束判斷 


俄羅斯方塊遊戲結束的判斷其實很簡單,只要判斷第一行的標記位是否有1便可

/**
     * 若是是top touched 則執行Game Over的相關操做
     * 
     */
    private void judgeGameOver() {
        if (isTopTouched()) {
            gameState = GameState.OVER;
            writeScore();
            int result = JOptionPane.showConfirmDialog(frame, "Game over! Continue?", "俄羅斯方塊",
                    JOptionPane.YES_NO_OPTION);
            if (result == JOptionPane.YES_OPTION) {
                for (int i = RussiaGameConstant.LEFT; i <= RussiaGameConstant.RIGHT; i++) {
                    for (int j = RussiaGameConstant.UP; j <= RussiaGameConstant.DOWN; j++) {
                        flag[i][j] = 0;
                    }
                }

                gameState = GameState.RUN;
                score = 0;
                timer.start();
            } else {
                System.exit(0);
            }
        }
    }
private boolean isTopTouched() {
        for (int i = RussiaGameConstant.LEFT; i <= RussiaGameConstant.RIGHT; i++) {
            if (flag[i][RussiaGameConstant.UP] == 1) {
                return true;
            }
        }
        return false;
    }

遊戲進度存儲和加載 

 遊戲進度的保存和加載功能是經過序列化反序列化來完成的。 

如何保存遊戲進度?

經過序列化的方式將當前遊戲運行狀態中用到的一些重要對象屬性序列化到文件中加以保存,從而達到記錄當前遊戲狀態的效果。 

如何載入遊戲進度?

經過反序列化的方式序列化後的對象讀取出來,從而達到恢復以前遊戲保存時的狀態的效果。用戶能夠在此基礎上繼續進行遊戲。 

步驟

(一)編寫保存遊戲進度、加載遊戲進度的事件監聽器

private class LoadAction implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			FileDialog dialog = new FileDialog(RussiaGameFrame.this, "Open",
					FileDialog.LOAD);
			dialog.setVisible(true);
			String dir = dialog.getDirectory();
			String fileName = dialog.getFile();
			String filePath = dir + fileName;

			if (fileName != null && fileName.trim().length() != 0) {
				File file = new File(filePath);
				panel.readSelfFromFile(file);
				startMI.setEnabled(false);
				pauseMI.setEnabled(true);
			} else {
				JOptionPane.showConfirmDialog(RussiaGameFrame.this,
						"文件名爲空\n裝載遊戲進度失敗", "俄羅斯方塊", JOptionPane.DEFAULT_OPTION);
			}

		}
	}

	private class SaveAction implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			if (panel.gameState == GameState.INITIALIZE) {
				JOptionPane
						.showConfirmDialog(RussiaGameFrame.this,
								"遊戲沒有運行\n不能保存遊戲進度", "俄羅斯方塊",
								JOptionPane.DEFAULT_OPTION);
				return;
			}

			FileDialog dialog = new FileDialog(RussiaGameFrame.this, "Save",
					FileDialog.SAVE);
			dialog.setVisible(true);
			String dir = dialog.getDirectory();
			String fileName = dialog.getFile();
			String filePath = dir + fileName;
			if (fileName != null && fileName.trim().length() != 0) {
				File file = new File(filePath);
				panel.writeSelfToFile(file);
			} else {
				JOptionPane.showConfirmDialog(RussiaGameFrame.this,
						"文件名爲空\n保存遊戲進度失敗", "俄羅斯方塊", JOptionPane.DEFAULT_OPTION);
			}

		}
	}

(二)添加用於保存和加載功能的MenuItem, 併爲它們添加ActionListenser 

private JMenuItem loadMI = new JMenuItem("Open");

	public JMenuItem saveMI = new JMenuItem("Save");
setMenu.add(loadMI);
		setMenu.add(saveMI);
loadMI.addActionListener(new LoadAction());
saveMI.addActionListener(new SaveAction());

(三)編寫具體的業務邏輯

public void writeSelfToFile(File file) {

        try {
            FileOutputStream fileStream = new FileOutputStream(file);
            ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);
            objectStream.writeObject(flag);
            objectStream.writeObject(color);
            objectStream.writeObject(sr1);
            objectStream.writeObject(sr2);
            objectStream.writeObject(new Integer(score));
            objectStream.close();
            fileStream.close();

            JOptionPane.showConfirmDialog(frame, "保存遊戲進度成功", "俄羅斯方塊", JOptionPane.DEFAULT_OPTION);
        } catch (Exception e) {
            JOptionPane.showConfirmDialog(frame, e.toString() + "\n保存遊戲進度失敗", "俄羅斯方塊",
                    JOptionPane.DEFAULT_OPTION);
        }
    }

    public void readSelfFromFile(File file) {

        try {
            int[][] f;
            AbstractRussiaSquare s1, s2;
            Integer integer;
            Color[][] c;
            FileInputStream fileStream = new FileInputStream(file);
            ObjectInputStream objectStream = new ObjectInputStream(fileStream);
            f = (int[][]) objectStream.readObject();
            c = (Color[][]) objectStream.readObject();
            s1 = (AbstractRussiaSquare) objectStream.readObject();
            s2 = (AbstractRussiaSquare) objectStream.readObject();
            integer = (Integer) objectStream.readObject();
            objectStream.close();
            fileStream.close();

            if (f != null && c != null && s1 != null && s2 != null && integer != null) {
                flag = f;
                color = c;
                sr1 = s1;
                sr2 = s2;
                score = integer.intValue();
                gameState = GameState.RUN;
                frame.saveMI.setEnabled(true);
                if (!timer.isRunning()) {
                    timer.start();
                }

                repaint();

                JOptionPane.showConfirmDialog(frame, "裝載遊戲進度成功", "俄羅斯方塊",
                        JOptionPane.DEFAULT_OPTION);
            }
        } catch (Exception e) {
            JOptionPane.showConfirmDialog(frame, e.toString() + "\n裝載遊戲進度失敗", "俄羅斯方塊",
                    JOptionPane.DEFAULT_OPTION);
        }
    }

 

遊戲玩家得分排行榜 

得分排行榜上列出Top 10的記錄信息,包括玩家名稱,得分和名次。 

該功能能夠經過以下幾個步驟完成: 

步驟


(一)建立遊戲記錄類和比較器

package my.games.russia.model;

import java.io.Serializable;

/**
 * 記錄玩家的信息
 * @author wangmengjun
 *
 */
public class Record implements Serializable {

    private static final long serialVersionUID = 9143467974370981697L;

    /**玩家姓名*/
    private String player = null;

    /**玩家得分*/
    private int score = 0;

    public Record(String player, int score) {
        this.player = player;
        this.score = score;
    }

    /**
     * @return the player
     */
    public String getPlayer() {
        return player;
    }

    /**
     * @param player the player to set
     */
    public void setPlayer(String player) {
        this.player = player;
    }

    /**
     * @return the score
     */
    public int getScore() {
        return score;
    }

    /**
     * @param score the score to set
     */
    public void setScore(int score) {
        this.score = score;
    }

}

先經過分數比較,若是分數一致,則比較玩家名字。

package my.games.russia.compare;

import java.util.Comparator;

import my.games.russia.model.Record;

/**
 * 俄羅斯記錄的比較器
 * @author wangmengjun
 *
 */
public class RecordComparator implements Comparator<Record> {

    public int compare(Record o1, Record o2) {
        Record r1 = (Record) o1;
        Record r2 = (Record) o2;
        int compareScore = compareScore(r1, r2);
        return (0 == compareScore) ? compareScore : compareName(r1, r2);
    }

    private int compareScore(Record r1, Record r2) {
        return r2.getScore() - r1.getScore();
    }

    private int compareName(Record r1, Record r2) {
        return r1.getPlayer().compareTo(r2.getPlayer());
    }

}


(二)完成遊戲結束後對記錄文件更新的操做。 

private void writeScore() {
        if (score == 0) {
            return;
        }
        File file = new File("file.dat");
        RussiaGameRecords records = new ReadRecord().readRecordsFromFile(file);
        if (records == null || records.isEmpty() || !records.isFull()
                || (records.getLastAvailableRecord().getScore() < score && records.isFull())) {
            String playerName = JOptionPane.showInputDialog("Please input your name");
            if (playerName == null || playerName.length() == 0) {
                playerName = "無名英雄";
            }
            Record record = new Record(playerName, score);
            records.addRecordToTopTen(record);
            new WriteRecord().writeRecordToFile(records, file);
        }

    }

 

/**
     * 若是是top touched 則執行Game Over的相關操做
     * 
     */
    private void judgeGameOver() {
        if (isTopTouched()) {
            gameState = GameState.OVER;
            writeScore();
            int result = JOptionPane.showConfirmDialog(frame, "Game over! Continue?", "俄羅斯方塊",
                    JOptionPane.YES_NO_OPTION);
            if (result == JOptionPane.YES_OPTION) {
                for (int i = RussiaGameConstant.LEFT; i <= RussiaGameConstant.RIGHT; i++) {
                    for (int j = RussiaGameConstant.UP; j <= RussiaGameConstant.DOWN; j++) {
                        flag[i][j] = 0;
                    }
                }

                gameState = GameState.RUN;
                score = 0;
                timer.start();
            } else {
                System.exit(0);
            }
        }
    }



(三)完成點擊Record相關的MenuItem,讀取記錄信息,並用ScrollPane展現出來。 

package my.games.russia.ui;

import java.io.File;

import javax.swing.JScrollPane;
import javax.swing.JTable;
import my.games.russia.model.Record;
import my.games.russia.model.RussiaGameRecords;

/**
 * 
 * @author wangmengjun
 *
 */
public class ReadScrollPane {

    public JScrollPane getReadScrollPane(RussiaGameRecords records, File recordFile) {
        Object[][] data = new Object[records.getNumberInRecord()][3];
        for (int i = 0; i < records.getNumberInRecord(); i++) {
            Record record = records.getRecords()[i];
            data[i][0] = String.valueOf(i + 1);
            data[i][1] = record.getPlayer();
            data[i][2] = String.valueOf(record.getScore());
        }
        Object[] columnNames = new Object[3];
        columnNames[0] = "ID";
        columnNames[1] = "Name";
        columnNames[2] = "Score";
        JTable table = new JTable(data, columnNames);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
        JScrollPane pane = new JScrollPane(table);
        return pane;
    }

}

 

隨機產生方塊

爲了遊戲更具隨機性,隨機產生方塊主要包含兩個部分的隨機性。

方塊圖形產生的隨機性

編寫一個工廠類,隨機產生方塊:如產生一字型的方塊、T字形的方塊等。

package my.games.russia.util;

import java.util.Random;

import my.games.russia.model.AbstractRussiaSquare;
import my.games.russia.model.RussiaSquare_1;
import my.games.russia.model.RussiaSquare_2;
import my.games.russia.model.RussiaSquare_3;

/**
 * 
 * @author wangmengjun
 *
 */
public class RussiaSquareFactory {

    private static final int TOTAL_RUSSIA_SQUARE_COUNT = 3;

    public static AbstractRussiaSquare generateNextRussiaSquareByRandom() {

        AbstractRussiaSquare rs = null;
        int index = new Random().nextInt(TOTAL_RUSSIA_SQUARE_COUNT);
        switch (index) {
            case 0:
                rs = new RussiaSquare_1();
                break;
            case 1:
                rs = new RussiaSquare_2();
                break;
            case 2:
                rs = new RussiaSquare_3();
                break;
            default:
                rs = new RussiaSquare_1();
                break;
        }
        return rs;
    }
}

方塊初始化的隨機性

每一個圖形經過旋轉,均可以有不同的初始化形態。好比T字形的方塊就能夠有四種形態,初始化的時候,也就有四種初始狀態。以下是T字形方塊的初始化:

/* (non-Javadoc)
     * @see my.games.russia.model.AbstractRussiaSquare#initGrids()
     */
    @Override
    public void initGrids() {
        state = new Random().nextInt(4);
        switch (state) {
            case 0:
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX() - 1);
                grids[1].setY(grids[0].getY() + 1);
                grids[2].setX(grids[0].getX());
                grids[2].setY(grids[0].getY() + 1);
                grids[3].setX(grids[0].getX() + 1);
                grids[3].setY(grids[0].getY() + 1);
                break;

            case 1:
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX());
                grids[1].setY(grids[0].getY() + 1);
                grids[2].setX(grids[0].getX() + 1);
                grids[2].setY(grids[0].getY() + 1);
                grids[3].setX(grids[0].getX());
                grids[3].setY(grids[0].getY() + 2);
                break;

            case 2:
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX() + 1);
                grids[1].setY(grids[0].getY());
                grids[2].setX(grids[0].getX() + 2);
                grids[2].setY(grids[0].getY());
                grids[3].setX(grids[0].getX() + 1);
                grids[3].setY(grids[0].getY() + 1);
                break;

            case 3:
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX() - 1);
                grids[1].setY(grids[0].getY() + 1);
                grids[2].setX(grids[0].getX());
                grids[2].setY(grids[0].getY() + 1);
                grids[3].setX(grids[0].getX());
                grids[3].setY(grids[0].getY() + 2);
                break;

            default:
                break;
        }

    }

方塊降低和速度改變

方塊降低能夠採用Timer來控制。如:

public Timer timer;
    
    public TimerAction timerAction;
timerAction = new TimerAction();
        timer = new Timer(1000, timerAction);

TimeAction類執行相關的邏輯行爲,如:

private class TimerAction implements ActionListener, Serializable {

        private static final long serialVersionUID = -6117702515382009989L;

        public void actionPerformed(ActionEvent event) {
            if (!gameState.isRunState()) {
                return;
            }

            //滿行的個數
            int num = 0;

            sr1.moveDown(flag);

            if (!sr1.isAlive()) {
                for (int i = 0; i < 4; i++) {
                    Grid[] grids = sr1.getGrids();
                    flag[grids[i].getX()][grids[i].getY()] = 1;
                    color[grids[i].getX()][grids[i].getY()] = sr1.getColor();
                }

                judgeGameOver();

                for (int i = RussiaGameConstant.UP; i <= RussiaGameConstant.DOWN; i++) {
                    int count = 0;
                    for (int j = RussiaGameConstant.LEFT; j <= RussiaGameConstant.RIGHT; j++) {
                        count += flag[j][i];
                    }

                    /*
                     * flag[i][j] =1 表示這個位置有小方塊,若是一行的位置都有小方塊,那麼滿行的個數num加1.
                     * 而且消除行。
                     */
                    if (count == RussiaGameConstant.GRID_COLUMN_NUMBER) {
                        num++;

                        /**
                         * 消除行操做。
                         */
                        for (int m = i; m > RussiaGameConstant.UP; m--) {
                            for (int n = RussiaGameConstant.LEFT; n <= RussiaGameConstant.RIGHT; n++) {
                                flag[n][m] = flag[n][m - 1];
                                color[n][m] = color[n][m - 1];
                            }
                        }
                        /*
                         * 從新將第一行的flag[s][0]置爲0
                         */
                        for (int s = RussiaGameConstant.LEFT; s <= RussiaGameConstant.RIGHT; s++) {
                            flag[s][RussiaGameConstant.UP] = 0;
                        }
                    }
                }

                /*
                 * 將下一個圖形做爲當前運動的圖形,並隨機產生下一個圖形。
                 */
                sr1 = sr2;
                sr2 = RussiaSquareFactory.generateNextRussiaSquareByRandom();
            }
            // 計算分數
            calculateScore(num);

            repaint();
        }
    }

 

小結

經過如上的步驟,一個單機版的Swing遊戲就實現了,本文描述和實現了三種方塊類型的旋轉。

  1. 田字形
  2. 一字型
  3. T字形

你們能夠參考上述的變形方法完成其餘類型的變化,如L型, Z字形

遊戲效果以下:

 

源碼

貌似OSChina博客沒有附件上傳,寫把代碼列出來,下次找個時間放到GITHUB上去,再提供下載地址。

下面按照package包的名字來列出源碼吧。

application

MyRussiaGameApplication.java

package my.games.russia.application;

import javax.swing.JFrame;
import my.games.russia.ui.RussiaGameFrame;

public class MyRussiaGameApplication {
	@SuppressWarnings("deprecation")
	public static void main(String[] args) {
		RussiaGameFrame frame = new RussiaGameFrame();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.show();
	}
}

compare

RecordComparator.java

package my.games.russia.compare;

import java.util.Comparator;

import my.games.russia.model.Record;

/**
 * 俄羅斯記錄的比較器
 * @author wangmengjun
 *
 */
public class RecordComparator implements Comparator<Record> {

    public int compare(Record o1, Record o2) {
        Record r1 = (Record) o1;
        Record r2 = (Record) o2;
        int compareScore = compareScore(r1, r2);
        return (0 == compareScore) ? compareScore : compareName(r1, r2);
    }

    private int compareScore(Record r1, Record r2) {
        return r2.getScore() - r1.getScore();
    }

    private int compareName(Record r1, Record r2) {
        return r1.getPlayer().compareTo(r2.getPlayer());
    }

}

contants

RussiaGameConstant.java

package my.games.russia.constants;

/**
 * 俄羅斯方塊的常量類
 * @author wangmengjun
 *
 */
public class RussiaGameConstant {

	public static final int GRID_SIZE = 20;

	public static final int RUSSIA_GAME_PANEL_LEFT = 10;

	public static final int RUSSIA_GAME_PANEL_RIGHT = RUSSIA_GAME_PANEL_LEFT + 400;

	public static final int RUSSIA_GAME_PANEL_TOP = 0;

	public static final int RUSSIA_GAME_PANEL_BOTTOM = RUSSIA_GAME_PANEL_TOP + 600;

	public static final int RUSSIA_GAME_NEXT_PANEL_LEFT = RUSSIA_GAME_PANEL_RIGHT
			+ GRID_SIZE;

	public static final int RUSSIA_GAME_NEXT_PANEL_RIGHT = RUSSIA_GAME_NEXT_PANEL_LEFT + 80;

	public static final int RUSSIA_GAME_NEXT_PANEL_TOP = RUSSIA_GAME_PANEL_TOP;

	public static final int RUSSIA_GAME_NEXT_PANEL_BOTTOM = RUSSIA_GAME_NEXT_PANEL_TOP + 80;

	public static final int LEFT = 0;

	public static final int RIGHT = 19;

	public static final int UP = 0;

	public static final int DOWN = 29;

	public static final int LITTLEX = 21;

	public static final int RUSSIA_GAME_FRAME_WIDTH = 540;

	public static final int RUSSIA_GAME_FRAME_HEIGHT = 660;

	public static final int GRID_COLUMN_NUMBER = 20;

	public static final int GRID_ROW_NUMBER = 30;

}

enums

GameState.java

package my.games.russia.enums;

import java.util.HashMap;
import java.util.Map;

public enum GameState {

	INITIALIZE("I", "Initial Game State"), RUN("R", "Run State"), PAUSE("P",
			"Pause State"), OVER("O", "Over State"), UNKNOWN("U", "UNKNOWN");

	private String gameStateCode = null;

	private String gameStateValue = null;

	private static Map<String, GameState> MAP = new HashMap<String, GameState>();

	static {
		for (GameState gameState : GameState.values()) {
			MAP.put(gameState.getGameStateCode(), gameState);
		}
	}

	private GameState(String gameStateCode, String gameStateValue) {
		this.gameStateCode = gameStateCode;
		this.gameStateValue = gameStateValue;
	}

	public static GameState getGameStateByCode(String gameStateCode) {
		return MAP.containsKey(gameStateCode) ? MAP.get(gameStateCode)
				: UNKNOWN;
	}

	/**
	 * @return the gameStateCode
	 */
	public String getGameStateCode() {
		return gameStateCode;
	}

	/**
	 * @param gameStateCode
	 *            the gameStateCode to set
	 */
	public void setGameStateCode(String gameStateCode) {
		this.gameStateCode = gameStateCode;
	}

	/**
	 * @return the gameStateValue
	 */
	public String getGameStateValue() {
		return gameStateValue;
	}

	/**
	 * @param gameStateValue
	 *            the gameStateValue to set
	 */
	public void setGameStateValue(String gameStateValue) {
		this.gameStateValue = gameStateValue;
	}

	public boolean isInitializedState() {
		return this == INITIALIZE;
	}

	public boolean isRunState() {
		return this == RUN;
	}

	public boolean isPausedState() {
		return this == PAUSE;
	}

	public boolean isOverState() {
		return this == OVER;
	}
}

model

Grid.java

package my.games.russia.model;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;

import my.games.russia.constants.RussiaGameConstant;

/**
 * 格子,俄羅斯方塊遊戲中的圖形由四個格子組成
 * @author wangmengjun
 *
 */
public class Grid implements Serializable {

    private static final long serialVersionUID = -3722886719784770741L;

    /**x位置*/
    public int x;

    /**y位置*/
    private int y;

    /**格子的顏色*/
    private Color color;

    public Grid() {
    }

    public Grid(int x, int y, Color color) {
        this.x = x;
        this.y = y;
        this.color = color;
    }

    /**
     * Draw Grid
     * @param g2
     */
    public void draw(Graphics2D g2) {
        int clientX = RussiaGameConstant.RUSSIA_GAME_PANEL_LEFT + x * RussiaGameConstant.GRID_SIZE;
        int clientY = RussiaGameConstant.RUSSIA_GAME_PANEL_TOP + y * RussiaGameConstant.GRID_SIZE;
        Rectangle2D.Double rect = new Rectangle2D.Double(clientX, clientY,
                RussiaGameConstant.GRID_SIZE, RussiaGameConstant.GRID_SIZE);
        g2.setPaint(color);
        g2.fill(rect);
        g2.setPaint(Color.BLACK);
        g2.draw(rect);
    }

    /**
     * @return the x
     */
    public int getX() {
        return x;
    }

    /**
     * @param x the x to set
     */
    public void setX(int x) {
        this.x = x;
    }

    /**
     * @return the y
     */
    public int getY() {
        return y;
    }

    /**
     * @param y the y to set
     */
    public void setY(int y) {
        this.y = y;
    }

    /**
     * @return the color
     */
    public Color getColor() {
        return color;
    }

    /**
     * @param color the color to set
     */
    public void setColor(Color color) {
        this.color = color;
    }

}

AbstractRussiaSquare.java

package my.games.russia.model;

import java.awt.Color;
import java.awt.Graphics2D;
import java.io.Serializable;
import my.games.russia.constants.RussiaGameConstant;

/**
 * 俄羅斯方塊遊戲,圖形的抽象類
 * @author wangmengjun
 *
 */
public abstract class AbstractRussiaSquare implements Serializable {

    private static final long serialVersionUID = 192398482620404584L;

    /**每個圖形都是有四個小方塊Grid組成*/
    protected Grid[] grids = { null, null, null, null };

    /**xLocations*/
    protected int[] xLocations = { 0, 0, 0, 0 };

    /**yLocations*/
    protected int[] yLocations = { 0, 0, 0, 0 };

    /**圖形是否alive,便是否還能變換*/
    protected boolean alive;

    /**圖形格子的顏色*/
    protected Color color;

    /**圖形初始狀態,圖形轉換的狀態*/
    public int state;

    public AbstractRussiaSquare() {
        int r = (int) (Math.random() * 256);
        int g = (int) (Math.random() * 256);
        int b = (int) (Math.random() * 256);
        this.color = new Color(r, g, b);
        grids[0] = new Grid(0, 0, color);
        grids[1] = new Grid(0, 0, color);
        grids[2] = new Grid(0, 0, color);
        grids[3] = new Grid(0, 0, color);
        alive = true;
        
        /**
         * 每一個圖形都有1到4種變換形態,隨機產生一種
         */
        initGrids();
    }

    /**
     * 圖形繪製
     */
    public void draw(Graphics2D g2) {
        for (Grid grid : grids) {
            grid.draw(g2);
        }
    }

    /**
     * 往左移動
     * 
     */
    public void moveLeft(int[][] flags) {
        if (!alive) {
            return;
        }
        /**
         * 將現有的點賦值到xLocation和yLocation上去
         */
        for (int i = 0; i < grids.length; i++) {
            xLocations[i] = grids[i].getX() - 1;
            yLocations[i] = grids[i].getY();
        }

        if (xLocations[0] >= RussiaGameConstant.LEFT && flags[xLocations[0]][yLocations[0]] == 0
                && xLocations[1] >= RussiaGameConstant.LEFT
                && flags[xLocations[1]][yLocations[1]] == 0
                && xLocations[2] >= RussiaGameConstant.LEFT
                && flags[xLocations[2]][yLocations[2]] == 0
                && xLocations[3] >= RussiaGameConstant.LEFT
                && flags[xLocations[3]][yLocations[3]] == 0) {
            for (int i = 0; i < grids.length; i++) {
                grids[i].setX(xLocations[i]);
            }
        }
    }

    /**
     * 往右移動
     */
    public void moveRight(int flags[][]) {
        if (!alive) {
            return;
        }

        for (int i = 0; i < grids.length; i++) {
            xLocations[i] = grids[i].getX() + 1;
            yLocations[i] = grids[i].getY();
        }

        if (xLocations[0] <= RussiaGameConstant.RIGHT && flags[xLocations[0]][yLocations[0]] == 0
                && xLocations[1] <= RussiaGameConstant.RIGHT
                && flags[xLocations[1]][yLocations[1]] == 0
                && xLocations[2] <= RussiaGameConstant.RIGHT
                && flags[xLocations[2]][yLocations[2]] == 0
                && xLocations[3] <= RussiaGameConstant.RIGHT
                && flags[xLocations[3]][yLocations[3]] == 0) {
            for (int i = 0; i < grids.length; i++) {
                grids[i].setX(xLocations[i]);
            }
        }
    }

    /**
     * 往下移動
     */
    public void moveDown(int[][] flags) {
        for (int i = 0; i < grids.length; i++) {
            xLocations[i] = grids[i].getX();
            yLocations[i] = grids[i].getY() + 1;
        }

        if (yLocations[0] <= RussiaGameConstant.DOWN && flags[xLocations[0]][yLocations[0]] == 0
                && yLocations[1] <= RussiaGameConstant.DOWN
                && flags[xLocations[1]][yLocations[1]] == 0
                && yLocations[2] <= RussiaGameConstant.DOWN
                && flags[xLocations[2]][yLocations[2]] == 0
                && yLocations[3] <= RussiaGameConstant.DOWN
                && flags[xLocations[3]][yLocations[3]] == 0) {
            for (int i = 0; i < grids.length; i++) {
                grids[i].setY(yLocations[i]);
                ;
            }
        } else {
            alive = false;
        }
    }

    public void drawNext(Graphics2D g2) {
        for (int i = 0; i < grids.length; i++) {
            xLocations[i] = grids[i].getX();
            yLocations[i] = grids[i].getY();
        }

        while (true) {
            xLocations[0]++;
            xLocations[1]++;
            xLocations[2]++;
            xLocations[3]++;

            if (xLocations[0] >= RussiaGameConstant.LITTLEX
                    && xLocations[1] >= RussiaGameConstant.LITTLEX
                    && xLocations[2] >= RussiaGameConstant.LITTLEX
                    && xLocations[3] >= RussiaGameConstant.LITTLEX) {
                break;
            }
        }

        for (int i = 0; i < grids.length; i++) {
            new Grid(xLocations[i], yLocations[i], color).draw(g2);
        }
    }

    protected void isAllowChangeState(int[][] flags, int count) {
        if (!alive) {
            return;
        }

        if (xLocations[0] >= RussiaGameConstant.LEFT && xLocations[0] <= RussiaGameConstant.RIGHT
                && yLocations[0] >= RussiaGameConstant.UP
                && yLocations[0] <= RussiaGameConstant.DOWN
                && flags[xLocations[0]][yLocations[0]] == 0
                && xLocations[1] >= RussiaGameConstant.LEFT
                && xLocations[1] <= RussiaGameConstant.RIGHT
                && yLocations[1] >= RussiaGameConstant.UP
                && yLocations[1] <= RussiaGameConstant.DOWN
                && flags[xLocations[1]][yLocations[1]] == 0
                && xLocations[2] >= RussiaGameConstant.LEFT
                && xLocations[2] <= RussiaGameConstant.RIGHT
                && yLocations[2] >= RussiaGameConstant.UP
                && yLocations[2] <= RussiaGameConstant.DOWN
                && flags[xLocations[2]][yLocations[2]] == 0
                && xLocations[3] >= RussiaGameConstant.LEFT
                && xLocations[3] <= RussiaGameConstant.RIGHT
                && yLocations[3] >= RussiaGameConstant.UP
                && yLocations[3] <= RussiaGameConstant.DOWN
                && flags[xLocations[3]][yLocations[3]] == 0) {
            for (int i = 0; i < grids.length; i++) {
                grids[i].setX(xLocations[i]);
                grids[i].setY(yLocations[i]);
            }
            /**
             * count爲圖形能夠變化的種數
             */
            state = (state + 1) % count;
        }
    }

    /**
     * 每一個圖形的圖形變化各不相同,須要子類實現細節
     */
    public abstract void changeState(int[][] flags);
    
    public abstract void initGrids();
    

    /**
     * @return the alive
     */
    public boolean isAlive() {
        return alive;
    }

    /**
     * @param alive the alive to set
     */
    public void setAlive(boolean alive) {
        this.alive = alive;
    }

    /**
     * @return the grids
     */
    public Grid[] getGrids() {
        return grids;
    }

    /**
     * @param grids the grids to set
     */
    public void setGrids(Grid[] grids) {
        this.grids = grids;
    }

    /**
     * @return the color
     */
    public Color getColor() {
        return color;
    }

    /**
     * @param color the color to set
     */
    public void setColor(Color color) {
        this.color = color;
    }

}

Record.java

package my.games.russia.model;

import java.io.Serializable;

/**
 * 記錄玩家的信息
 * @author wangmengjun
 *
 */
public class Record implements Serializable {

    private static final long serialVersionUID = 9143467974370981697L;

    /**玩家姓名*/
    private String player = null;

    /**玩家得分*/
    private int score = 0;

    public Record(String player, int score) {
        this.player = player;
        this.score = score;
    }

    /**
     * @return the player
     */
    public String getPlayer() {
        return player;
    }

    /**
     * @param player the player to set
     */
    public void setPlayer(String player) {
        this.player = player;
    }

    /**
     * @return the score
     */
    public int getScore() {
        return score;
    }

    /**
     * @param score the score to set
     */
    public void setScore(int score) {
        this.score = score;
    }

}

RussiaGameRecords.java

package my.games.russia.model;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;

import my.games.russia.compare.RecordComparator;

/**
 * 俄羅斯方塊遊戲的排行榜
 * @author wangmengjun
 *
 */
public class RussiaGameRecords implements Serializable {

    private static final long serialVersionUID = 2621026339727176509L;

    private static final int TOP_TEN = 10;

    private Record[] records = null;

    private int numberInRecord = 0; // 排行榜中已經擁有的記錄個數

    public RussiaGameRecords() {
        records = new Record[TOP_TEN];
    }

    public void sortRecords() {
        Collections.sort(Arrays.asList(getAvailableRecords()), new RecordComparator());
    }

    private Record[] getAvailableRecords() {
        Record[] availableRecords = new Record[numberInRecord];
        for (int i = 0; i < numberInRecord; i++) {
            availableRecords[i] = new Record(records[i].getPlayer(), records[i].getScore());
        }
        return availableRecords;
    }

    /**
     * 
     * @return
     */
    public Record getLastAvailableRecord() {
        return isEmpty() ? null : records[numberInRecord - 1];
    }

    /**
     * 
     * @param record
     */
    public void addRecordToTopTen(Record record) {
        if (isEmpty()) {
            records[0] = record;
            numberInRecord++;
            return;
        }

        if (isFull()) {
            if (records[TOP_TEN - 1].getScore() < record.getScore()) {
                records[TOP_TEN - 1] = record;
                sortRecords();
                return;
            }
        }

        records[numberInRecord] = record;
        numberInRecord++;
        sortRecords();
    }

    /**
     * 
     * @return
     */
    public boolean isEmpty() {
        return 0 == numberInRecord;
    }

    /**
     * 
     * @return
     */
    public boolean isFull() {
        return TOP_TEN == numberInRecord;
    }

    /**
     * @return the numberInRecord
     */
    public int getNumberInRecord() {
        return numberInRecord;
    }

    /**
     * @param numberInRecord
     *            the numberInRecord to set
     */
    public void setNumberInRecord(int numberInRecord) {
        this.numberInRecord = numberInRecord;
    }

    /**
     * @return the records
     */
    public Record[] getRecords() {
        return records;
    }

    /**
     * @param records
     *            the records to set
     */
    public void setRecords(Record[] records) {
        this.records = records;
    }

}

RussiaSquare_1.java

package my.games.russia.model;

/**
 * 田字形
 * 
 * @author wangmengjun
 *
 */
public class RussiaSquare_1 extends AbstractRussiaSquare {

    private static final long serialVersionUID = -2293988596788484343L;

    public RussiaSquare_1() {
        initGrids();
    }
    
    /* (non-Javadoc)
     * @see my.games.russia.model.AbstractRussiaSquare#changeState(int[][])
     */
    @Override
    public void changeState(int[][] flags) {
        // 田字形只有一種形態,不須要變換
    }

    /* (non-Javadoc)
     * @see my.games.russia.model.AbstractRussiaSquare#initGrids()
     */
    @Override
    public void initGrids() {
        grids[0].setX(9);
        grids[0].setY(0);
        grids[1].setX(grids[0].getX() + 1);
        grids[1].setY(0);
        grids[2].setX(grids[0].getX());
        grids[2].setY(grids[0].getY() + 1);
        grids[3].setX(grids[0].getX() + 1);
        grids[3].setY(grids[0].getY() + 1);
        
    }


}

RussiaSquare_2.java

package my.games.russia.model;

import java.util.Random;

/**
 * 一字型
 * @author wangmengjun
 *
 */
public class RussiaSquare_2 extends AbstractRussiaSquare {

    private static final long serialVersionUID = -4746450405460864752L;

    public RussiaSquare_2() {
        initGrids();
    }

    /* (non-Javadoc)
     * @see my.games.russia.model.AbstractRussiaSquare#changeState(int[][])
     */
    @Override
    public void changeState(int[][] flags) {

        int fixedXLocation = grids[1].getX();
        int fixedYLocation = grids[1].getY();

        switch (state) {
            case 0:
                /**
                 * 橫向到豎直方向轉換
                 */
                /**
                 * 第二個點保持不變
                 */

                xLocations[1] = fixedXLocation;
                yLocations[1] = fixedYLocation;

                xLocations[0] = fixedXLocation;
                yLocations[0] = fixedYLocation - 1;

                xLocations[2] = fixedXLocation;
                yLocations[2] = fixedYLocation + 1;
                xLocations[3] = fixedXLocation;
                yLocations[3] = fixedYLocation + 2;
                isAllowChangeState(flags, 2);
                break;
            case 1:
                xLocations[1] = fixedXLocation;
                yLocations[1] = fixedYLocation;

                /**
                 * 豎直到橫向轉換
                 */
                xLocations[0] = fixedXLocation - 1;
                yLocations[0] = fixedYLocation;

                xLocations[2] = fixedXLocation + 1;
                yLocations[2] = fixedYLocation;
                xLocations[3] = fixedXLocation + 2;
                yLocations[3] = fixedYLocation;
                isAllowChangeState(flags, 2);
                break;

            default:
                break;
        }

    }

    /* (non-Javadoc)
     * @see my.games.russia.model.AbstractRussiaSquare#initGrids()
     */
    @Override
    public void initGrids() {
        state = new Random().nextInt(2);
        switch (state) {
            case 0:
                /**
                 * 豎直
                 */
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX());
                grids[1].setY(grids[0].getY() + 1);
                grids[2].setX(grids[0].getX());
                grids[2].setY(grids[0].getY() + 2);
                grids[3].setX(grids[0].getX());
                grids[3].setY(grids[0].getY() + 3);
                break;

            case 1:
                /**
                 * 橫向
                 */
                grids[0].setX(8);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX() + 1);
                grids[1].setY(grids[0].getY());
                grids[2].setX(grids[0].getX() + 2);
                grids[2].setY(grids[0].getY());
                grids[3].setX(grids[0].getX() + 3);
                grids[3].setY(grids[0].getY());
                break;
            default:
                break;
        }

    }

}

RussiaSquare_3.java

package my.games.russia.model;

import java.util.Random;


/**
 * T字型方塊
 * @author wangmengjun
 *
 */
public class RussiaSquare_3 extends AbstractRussiaSquare {

    private static final long serialVersionUID = -8336206016924545562L;

    public RussiaSquare_3() {
        initGrids();
    }

    /* (non-Javadoc)
     * @see my.games.russia.model.AbstractRussiaSquare#changeState(int[][])
     */
    @Override
    public void changeState(int[][] flags) {
        switch (state) {
            case 0:
                xLocations[0] = grids[0].getX();
                yLocations[0] = grids[0].getY();
                xLocations[1] = xLocations[0];
                yLocations[1] = yLocations[0] + 1;
                xLocations[2] = xLocations[0] + 1;
                yLocations[2] = yLocations[0] + 1;
                xLocations[3] = xLocations[0];
                yLocations[3] = yLocations[0] + 2;
                isAllowChangeState(flags, 4);
                break;

            case 1:
                xLocations[0] = grids[0].getX() - 1;
                yLocations[0] = grids[0].getY() + 1;
                xLocations[1] = xLocations[0] + 1;
                yLocations[1] = yLocations[0];
                xLocations[2] = xLocations[0] + 2;
                yLocations[2] = yLocations[0];
                xLocations[3] = xLocations[0] + 1;
                yLocations[3] = yLocations[0] + 1;
                isAllowChangeState(flags, 4);
                break;
            case 2:
                xLocations[0] = grids[0].getX() + 1;
                yLocations[0] = grids[0].getY() - 1;
                xLocations[1] = xLocations[0] - 1;
                yLocations[1] = yLocations[0] + 1;
                xLocations[2] = xLocations[0];
                yLocations[2] = yLocations[0] + 1;
                xLocations[3] = xLocations[0];
                yLocations[3] = yLocations[0] + 2;
                isAllowChangeState(flags, 4);
                break;

            case 3:
                xLocations[0] = grids[0].getX();
                yLocations[0] = grids[0].getY();
                xLocations[1] = xLocations[0] - 1;
                yLocations[1] = yLocations[0] + 1;
                xLocations[2] = xLocations[0];
                yLocations[2] = yLocations[0] + 1;
                xLocations[3] = xLocations[0] + 1;
                yLocations[3] = yLocations[0] + 1;
                isAllowChangeState(flags, 4);
                break;

            default:
                break;
        }

    }

    /* (non-Javadoc)
     * @see my.games.russia.model.AbstractRussiaSquare#initGrids()
     */
    @Override
    public void initGrids() {
        state = new Random().nextInt(4);
        switch (state) {
            case 0:
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX() - 1);
                grids[1].setY(grids[0].getY() + 1);
                grids[2].setX(grids[0].getX());
                grids[2].setY(grids[0].getY() + 1);
                grids[3].setX(grids[0].getX() + 1);
                grids[3].setY(grids[0].getY() + 1);
                break;

            case 1:
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX());
                grids[1].setY(grids[0].getY() + 1);
                grids[2].setX(grids[0].getX() + 1);
                grids[2].setY(grids[0].getY() + 1);
                grids[3].setX(grids[0].getX());
                grids[3].setY(grids[0].getY() + 2);
                break;

            case 2:
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX() + 1);
                grids[1].setY(grids[0].getY());
                grids[2].setX(grids[0].getX() + 2);
                grids[2].setY(grids[0].getY());
                grids[3].setX(grids[0].getX() + 1);
                grids[3].setY(grids[0].getY() + 1);
                break;

            case 3:
                grids[0].setX(9);
                grids[0].setY(0);
                grids[1].setX(grids[0].getX() - 1);
                grids[1].setY(grids[0].getY() + 1);
                grids[2].setX(grids[0].getX());
                grids[2].setY(grids[0].getY() + 1);
                grids[3].setX(grids[0].getX());
                grids[3].setY(grids[0].getY() + 2);
                break;

            default:
                break;
        }

    }

}

record

ReadRecord.java

package my.games.russia.record;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import my.games.russia.model.RussiaGameRecords;

public class ReadRecord {

	public RussiaGameRecords readRecordsFromFile(File recordFile) {
		RussiaGameRecords records = new RussiaGameRecords();
		FileInputStream fileInput = null;
		ObjectInputStream objectInput = null;

		if (!recordFile.exists()) {
			return records;
		}

		try {
			fileInput = new FileInputStream(recordFile);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		try {
			objectInput = new ObjectInputStream(fileInput);
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		Object o = null;
		try {
			o = objectInput.readObject();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		try {
			objectInput.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			fileInput.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		records = (RussiaGameRecords) o;
		records.sortRecords();
		return records;
	}

}

WriteRecord.java

package my.games.russia.record;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import my.games.russia.model.RussiaGameRecords;

public class WriteRecord {

    public void writeRecordToFile(RussiaGameRecords records, File recordFile) {
        FileOutputStream fileOutput = null;
        try {
            fileOutput = new FileOutputStream(recordFile);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        ObjectOutputStream objectOutput = null;
        try {
            objectOutput = new ObjectOutputStream(fileOutput);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            objectOutput.writeObject(records);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            objectOutput.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            fileOutput.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

ui

ReadScrollPane.java

package my.games.russia.ui;

import java.io.File;

import javax.swing.JScrollPane;
import javax.swing.JTable;
import my.games.russia.model.Record;
import my.games.russia.model.RussiaGameRecords;

/**
 * 
 * @author wangmengjun
 *
 */
public class ReadScrollPane {

    public JScrollPane getReadScrollPane(RussiaGameRecords records, File recordFile) {
        Object[][] data = new Object[records.getNumberInRecord()][3];
        for (int i = 0; i < records.getNumberInRecord(); i++) {
            Record record = records.getRecords()[i];
            data[i][0] = String.valueOf(i + 1);
            data[i][1] = record.getPlayer();
            data[i][2] = String.valueOf(record.getScore());
        }
        Object[] columnNames = new Object[3];
        columnNames[0] = "ID";
        columnNames[1] = "Name";
        columnNames[2] = "Score";
        JTable table = new JTable(data, columnNames);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
        JScrollPane pane = new JScrollPane(table);
        return pane;
    }

}

RussiaGameFrame.java

package my.games.russia.ui;

import java.awt.Container;
import java.awt.FileDialog;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

import javax.swing.ButtonGroup;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;

import my.games.russia.constants.RussiaGameConstant;
import my.games.russia.enums.GameState;
import my.games.russia.model.RussiaGameRecords;
import my.games.russia.record.ReadRecord;

/**
 * 
 * @author wangmengjun
 *
 */
public class RussiaGameFrame extends JFrame {

	private static final long serialVersionUID = 2511418550392568827L;

	private final int WIDTH = RussiaGameConstant.RUSSIA_GAME_FRAME_WIDTH;

	private final int HEIGHT = RussiaGameConstant.RUSSIA_GAME_FRAME_HEIGHT;

	private RussiaGamePanel panel;

	private Container contentPane;

	private JMenuItem startMI = new JMenuItem("Start");

	private JMenuItem pauseMI = new JMenuItem("Pause");

	private JMenuItem recordMI = new JMenuItem("Record");

	private JMenu speedMenu = new JMenu("Speed");

	private JMenuItem exitMI = new JMenuItem("Exit");

	private JMenuItem aboutMI = new JMenuItem("About");

	private JMenuItem loadMI = new JMenuItem("Open");

	public JMenuItem saveMI = new JMenuItem("Save");

	private JRadioButtonMenuItem speedMI1 = new JRadioButtonMenuItem("Speed1",
			true);

	private JRadioButtonMenuItem speedMI2 = new JRadioButtonMenuItem("Speed2",
			false);

	private JRadioButtonMenuItem speedMI3 = new JRadioButtonMenuItem("Speed3",
			false);

	private JRadioButtonMenuItem speedMI4 = new JRadioButtonMenuItem("Speed4",
			false);

	private JRadioButtonMenuItem speedMI5 = new JRadioButtonMenuItem("Speed5",
			false);

	public int speedFlag = 1;

	public RussiaGameFrame() {
		setTitle("俄羅斯方塊");
		setSize(WIDTH, HEIGHT);
		setResizable(false);

		JMenuBar menuBar = new JMenuBar();
		setJMenuBar(menuBar);

		JMenu setMenu = new JMenu("Set");
		JMenu helpMenu = new JMenu("Help");

		setMenu.setMnemonic('s');
		setMenu.setMnemonic('H');

		menuBar.add(setMenu);
		menuBar.add(helpMenu);

		setMenu.add(startMI);
		setMenu.add(pauseMI);
		setMenu.addSeparator();

		setMenu.add(loadMI);
		setMenu.add(saveMI);
		setMenu.add(recordMI);

		setMenu.addSeparator();
		setMenu.add(speedMenu);
		setMenu.addSeparator();
		setMenu.add(exitMI);

		ButtonGroup group = new ButtonGroup();
		group.add(speedMI1);
		group.add(speedMI2);
		group.add(speedMI3);
		group.add(speedMI4);
		group.add(speedMI5);

		speedMenu.add(speedMI1);
		speedMenu.add(speedMI2);
		speedMenu.add(speedMI3);
		speedMenu.add(speedMI4);
		speedMenu.add(speedMI5);

		startMI.addActionListener(new StartAction());
		pauseMI.addActionListener(new PauseAction());
		loadMI.addActionListener(new LoadAction());
		saveMI.addActionListener(new SaveAction());
		recordMI.addActionListener(new RecordAction());
		exitMI.addActionListener(new ExitAction());
		speedMI1.addActionListener(new SpeedAction());
		speedMI2.addActionListener(new SpeedAction());
		speedMI3.addActionListener(new SpeedAction());
		speedMI4.addActionListener(new SpeedAction());
		speedMI5.addActionListener(new SpeedAction());

		helpMenu.add(aboutMI);
		aboutMI.addActionListener(new AboutAction());

		contentPane = getContentPane();
		panel = new RussiaGamePanel(this);
		contentPane.add(panel);

		startMI.setEnabled(true);
		pauseMI.setEnabled(false);
		saveMI.setEnabled(false);

		// 設置遊戲狀態是初始化狀態
		panel.setGameState(GameState.INITIALIZE);
	}

	private class StartAction implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			startMI.setEnabled(false);
			pauseMI.setEnabled(true);
			saveMI.setEnabled(true);
			panel.setGameState(GameState.RUN);
			panel.timer.start();
		}
	}

	private class PauseAction implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			pauseMI.setEnabled(false);
			startMI.setEnabled(true);
			panel.setGameState(GameState.PAUSE);
			if (panel.timer.isRunning()) {
				panel.timer.stop();
			}

		}
	}

	private class LoadAction implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			FileDialog dialog = new FileDialog(RussiaGameFrame.this, "Open",
					FileDialog.LOAD);
			dialog.setVisible(true);
			String dir = dialog.getDirectory();
			String fileName = dialog.getFile();
			String filePath = dir + fileName;

			if (fileName != null && fileName.trim().length() != 0) {
				File file = new File(filePath);
				panel.readSelfFromFile(file);
				startMI.setEnabled(false);
				pauseMI.setEnabled(true);
			} else {
				JOptionPane.showConfirmDialog(RussiaGameFrame.this,
						"文件名爲空\n裝載遊戲進度失敗", "俄羅斯方塊", JOptionPane.DEFAULT_OPTION);
			}

		}
	}

	private class SaveAction implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			if (panel.gameState == GameState.INITIALIZE) {
				JOptionPane
						.showConfirmDialog(RussiaGameFrame.this,
								"遊戲沒有運行\n不能保存遊戲進度", "俄羅斯方塊",
								JOptionPane.DEFAULT_OPTION);
				return;
			}

			FileDialog dialog = new FileDialog(RussiaGameFrame.this, "Save",
					FileDialog.SAVE);
			dialog.setVisible(true);
			String dir = dialog.getDirectory();
			String fileName = dialog.getFile();
			String filePath = dir + fileName;
			if (fileName != null && fileName.trim().length() != 0) {
				File file = new File(filePath);
				panel.writeSelfToFile(file);
			} else {
				JOptionPane.showConfirmDialog(RussiaGameFrame.this,
						"文件名爲空\n保存遊戲進度失敗", "俄羅斯方塊", JOptionPane.DEFAULT_OPTION);
			}

		}
	}

	private class RecordAction implements ActionListener {

		@SuppressWarnings("deprecation")
		public void actionPerformed(ActionEvent event) {
			File file = new File("file.dat");
			RussiaGameRecords records = new ReadRecord()
					.readRecordsFromFile(file);
			records.sortRecords();
			JScrollPane panel = new ReadScrollPane().getReadScrollPane(records,
					file);

			JDialog recordDialog = new JDialog(RussiaGameFrame.this, "俄羅斯方塊");
			recordDialog.setBounds(300, 300, 300, 219);

			Container container = recordDialog.getContentPane();
			container.add(panel);
			recordDialog.show();
		}
	}

	private class SpeedAction implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			Object speed = event.getSource();
			if (speed == speedMI1) {
				speedFlag = 1;
			} else if (speed == speedMI2) {
				speedFlag = 2;
			} else if (speed == speedMI3) {
				speedFlag = 3;
			} else if (speed == speedMI4) {
				speedFlag = 4;
			} else if (speed == speedMI5) {
				speedFlag = 5;
			}

			panel.timer.setDelay(1000 - 200 * (speedFlag - 1));
		}
	}

	private class ExitAction implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			int result = JOptionPane.showConfirmDialog(RussiaGameFrame.this,
					"Are you sure quit?", "俄羅斯方塊", JOptionPane.YES_NO_OPTION);
			if (result == JOptionPane.YES_OPTION) {
				System.exit(0);
			}
		}
	}

	private class AboutAction implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			String string = "說明:\n1.按左鍵向左移動\n" + "2.按右鍵向右移動\n" + "3.按向上鍵翻滾\n"
					+ "4.按向下鍵加速降低\n" + "5.按空格鍵降低到最底部";
			JOptionPane.showMessageDialog(RussiaGameFrame.this, string);
		}
	}

}

RussiaGamePanel.java

package my.games.russia.ui;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;

import my.games.russia.constants.RussiaGameConstant;
import my.games.russia.enums.GameState;
import my.games.russia.model.Grid;
import my.games.russia.model.Record;
import my.games.russia.model.RussiaGameRecords;
import my.games.russia.model.AbstractRussiaSquare;
import my.games.russia.record.ReadRecord;
import my.games.russia.record.WriteRecord;
import my.games.russia.util.RussiaSquareFactory;

/**
 * 俄羅斯方塊遊戲面板
 * @author wangmengjun
 *
 */
public class RussiaGamePanel extends JPanel {

    private static final long serialVersionUID = 3422344654252668944L;

    public int[][] flag = new int[RussiaGameConstant.GRID_COLUMN_NUMBER][RussiaGameConstant.GRID_ROW_NUMBER];// 在一個10*20的界面中,設置每一個方塊的flag

    public Color[][] color = new Color[RussiaGameConstant.GRID_COLUMN_NUMBER][RussiaGameConstant.GRID_ROW_NUMBER];// 在一個10*20的界面中,設置每一個方塊的顏色

    public AbstractRussiaSquare sr1; // 主顯示界面的圖形

    public AbstractRussiaSquare sr2; // 下一個顯示界面的圖形

    public Timer timer;
    
    public TimerAction timerAction;

    public int score;

    public RussiaGameFrame frame;


    public Grid square;

    // public int gameState;

    public GameState gameState = GameState.INITIALIZE;

    public RussiaGamePanel(RussiaGameFrame frame) {
        for (int i = RussiaGameConstant.LEFT; i <= RussiaGameConstant.RIGHT; i++) {
            for (int j = RussiaGameConstant.UP; j <= RussiaGameConstant.DOWN; j++) {
                flag[i][j] = 0;
            }
        }
        addKeyListener(new KeyHandler());
        setFocusable(true);
        timerAction = new TimerAction();
        timer = new Timer(1000, timerAction);

        sr1 = RussiaSquareFactory.generateNextRussiaSquareByRandom();
        sr2 = RussiaSquareFactory.generateNextRussiaSquareByRandom();
        score = 0;

        this.frame = frame;

        square = new Grid();
    }

    public void drawGameFrame(Graphics2D g2) {
        Rectangle2D.Double leftFrame = new Rectangle2D.Double(
                RussiaGameConstant.RUSSIA_GAME_PANEL_LEFT,
                RussiaGameConstant.RUSSIA_GAME_PANEL_TOP, 400, 600);
        Rectangle2D.Double rightFrame = new Rectangle2D.Double(
                RussiaGameConstant.RUSSIA_GAME_NEXT_PANEL_LEFT,
                RussiaGameConstant.RUSSIA_GAME_NEXT_PANEL_TOP, 80, 80);
        g2.draw(leftFrame);
        g2.draw(rightFrame);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;

        drawGameFrame(g2);

        if (gameState.isInitializedState()) {
            return;
        }

        sr1.draw(g2);
        sr2.drawNext(g2);

        for (int i = RussiaGameConstant.LEFT; i <= RussiaGameConstant.RIGHT; i++) {
            for (int j = RussiaGameConstant.UP; j <= RussiaGameConstant.DOWN; j++) {
                if (flag[i][j] == 1) {
                    square.setX(i);
                    square.setY(j);
                    square.setColor(color[i][j]);
                    square.draw(g2);
                }
            }
        }

        g.drawString("Score: " + score, RussiaGameConstant.RUSSIA_GAME_NEXT_PANEL_LEFT, 200);
    }

    public void writeSelfToFile(File file) {

        try {
            FileOutputStream fileStream = new FileOutputStream(file);
            ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);
            objectStream.writeObject(flag);
            objectStream.writeObject(color);
            objectStream.writeObject(sr1);
            objectStream.writeObject(sr2);
            objectStream.writeObject(new Integer(score));
            objectStream.close();
            fileStream.close();

            JOptionPane.showConfirmDialog(frame, "保存遊戲進度成功", "俄羅斯方塊", JOptionPane.DEFAULT_OPTION);
        } catch (Exception e) {
            JOptionPane.showConfirmDialog(frame, e.toString() + "\n保存遊戲進度失敗", "俄羅斯方塊",
                    JOptionPane.DEFAULT_OPTION);
        }
    }

    public void readSelfFromFile(File file) {

        try {
            int[][] f;
            AbstractRussiaSquare s1, s2;
            Integer integer;
            Color[][] c;
            FileInputStream fileStream = new FileInputStream(file);
            ObjectInputStream objectStream = new ObjectInputStream(fileStream);
            f = (int[][]) objectStream.readObject();
            c = (Color[][]) objectStream.readObject();
            s1 = (AbstractRussiaSquare) objectStream.readObject();
            s2 = (AbstractRussiaSquare) objectStream.readObject();
            integer = (Integer) objectStream.readObject();
            objectStream.close();
            fileStream.close();

            if (f != null && c != null && s1 != null && s2 != null && integer != null) {
                flag = f;
                color = c;
                sr1 = s1;
                sr2 = s2;
                score = integer.intValue();
                gameState = GameState.RUN;
                frame.saveMI.setEnabled(true);
                if (!timer.isRunning()) {
                    timer.start();
                }

                repaint();

                JOptionPane.showConfirmDialog(frame, "裝載遊戲進度成功", "俄羅斯方塊",
                        JOptionPane.DEFAULT_OPTION);
            }
        } catch (Exception e) {
            JOptionPane.showConfirmDialog(frame, e.toString() + "\n裝載遊戲進度失敗", "俄羅斯方塊",
                    JOptionPane.DEFAULT_OPTION);
        }
    }

    public void setGameState(GameState state) {
        gameState = state;
    }

    private void writeScore() {
        if (score == 0) {
            return;
        }
        File file = new File("file.dat");
        RussiaGameRecords records = new ReadRecord().readRecordsFromFile(file);
        if (records == null || records.isEmpty() || !records.isFull()
                || (records.getLastAvailableRecord().getScore() < score && records.isFull())) {
            String playerName = JOptionPane.showInputDialog("Please input your name");
            if (playerName == null || playerName.length() == 0) {
                playerName = "無名英雄";
            }
            Record record = new Record(playerName, score);
            records.addRecordToTopTen(record);
            new WriteRecord().writeRecordToFile(records, file);
        }

    }

    private boolean isTopTouched() {
        for (int i = RussiaGameConstant.LEFT; i <= RussiaGameConstant.RIGHT; i++) {
            if (flag[i][RussiaGameConstant.UP] == 1) {
                return true;
            }
        }
        return false;
    }

    /**
     * 若是是top touched 則執行Game Over的相關操做
     * 
     */
    private void judgeGameOver() {
        if (isTopTouched()) {
            gameState = GameState.OVER;
            writeScore();
            int result = JOptionPane.showConfirmDialog(frame, "Game over! Continue?", "俄羅斯方塊",
                    JOptionPane.YES_NO_OPTION);
            if (result == JOptionPane.YES_OPTION) {
                for (int i = RussiaGameConstant.LEFT; i <= RussiaGameConstant.RIGHT; i++) {
                    for (int j = RussiaGameConstant.UP; j <= RussiaGameConstant.DOWN; j++) {
                        flag[i][j] = 0;
                    }
                }

                gameState = GameState.RUN;
                score = 0;
                timer.start();
            } else {
                System.exit(0);
            }
        }
    }

    private class KeyHandler implements KeyListener {
        public void keyPressed(KeyEvent event) {
            if (!gameState.isRunState()) {
                return;
            }

            int keyCode = event.getKeyCode();
            switch (keyCode) {
                case KeyEvent.VK_LEFT:
                    sr1.moveLeft(flag);
                    break;

                case KeyEvent.VK_RIGHT:
                    sr1.moveRight(flag);
                    break;

                case KeyEvent.VK_UP:
                    sr1.changeState(flag);
                    break;

                case KeyEvent.VK_DOWN:
                    sr1.moveDown(flag);
                    break;
                case KeyEvent.VK_SPACE:
                    while (sr1.isAlive()) {
                        sr1.moveDown(flag);
                    }
                default:
                    break;
            }
            repaint();
        }

        public void keyReleased(KeyEvent event) {
        }

        public void keyTyped(KeyEvent event) {
        }
    }

    private class TimerAction implements ActionListener, Serializable {

        private static final long serialVersionUID = -6117702515382009989L;

        public void actionPerformed(ActionEvent event) {
            if (!gameState.isRunState()) {
                return;
            }

            //滿行的個數
            int num = 0;

            sr1.moveDown(flag);

            if (!sr1.isAlive()) {
                for (int i = 0; i < 4; i++) {
                    Grid[] grids = sr1.getGrids();
                    flag[grids[i].getX()][grids[i].getY()] = 1;
                    color[grids[i].getX()][grids[i].getY()] = sr1.getColor();
                }

                judgeGameOver();

                for (int i = RussiaGameConstant.UP; i <= RussiaGameConstant.DOWN; i++) {
                    int count = 0;
                    for (int j = RussiaGameConstant.LEFT; j <= RussiaGameConstant.RIGHT; j++) {
                        count += flag[j][i];
                    }

                    /*
                     * flag[i][j] =1 表示這個位置有小方塊,若是一行的位置都有小方塊,那麼滿行的個數num加1.
                     * 而且消除行。
                     */
                    if (count == RussiaGameConstant.GRID_COLUMN_NUMBER) {
                        num++;

                        /**
                         * 消除行操做。
                         */
                        for (int m = i; m > RussiaGameConstant.UP; m--) {
                            for (int n = RussiaGameConstant.LEFT; n <= RussiaGameConstant.RIGHT; n++) {
                                flag[n][m] = flag[n][m - 1];
                                color[n][m] = color[n][m - 1];
                            }
                        }
                        /*
                         * 從新將第一行的flag[s][0]置爲0
                         */
                        for (int s = RussiaGameConstant.LEFT; s <= RussiaGameConstant.RIGHT; s++) {
                            flag[s][RussiaGameConstant.UP] = 0;
                        }
                    }
                }

                /*
                 * 將下一個圖形做爲當前運動的圖形,並隨機產生下一個圖形。
                 */
                sr1 = sr2;
                sr2 = RussiaSquareFactory.generateNextRussiaSquareByRandom();
            }
            // 計算分數
            calculateScore(num);

            repaint();
        }
    }

    /**
     * @param num
     *            方塊滿行的個數
     */
    private void calculateScore(int num) {
        switch (num) {
            case 1:
                score += 10;
                break;
            case 2:
                score += 20;
                break;
            case 3:
                score += 50;
                break;
            case 4:
                score += 100;
                break;
            default:
                break;

        }
    }
}

util

RussiaSquareFactory.java

package my.games.russia.util;

import java.util.Random;

import my.games.russia.model.AbstractRussiaSquare;
import my.games.russia.model.RussiaSquare_1;
import my.games.russia.model.RussiaSquare_2;
import my.games.russia.model.RussiaSquare_3;

/**
 * 
 * @author wangmengjun
 *
 */
public class RussiaSquareFactory {

    private static final int TOTAL_RUSSIA_SQUARE_COUNT = 3;

    public static AbstractRussiaSquare generateNextRussiaSquareByRandom() {

        AbstractRussiaSquare rs = null;
        int index = new Random().nextInt(TOTAL_RUSSIA_SQUARE_COUNT);
        switch (index) {
            case 0:
                rs = new RussiaSquare_1();
                break;
            case 1:
                rs = new RussiaSquare_2();
                break;
            case 2:
                rs = new RussiaSquare_3();
                break;
            default:
                rs = new RussiaSquare_1();
                break;
        }
        return rs;
    }
}

 

若有任何錯誤,請你們指正。

若有任何建議,也請告訴我,謝謝。

相關文章
相關標籤/搜索