文档章节

Swing俄罗斯游戏编写详解(附源码)

王孟君
 王孟君
发布于 2016/10/28 11:28
字数 9442
阅读 495
收藏 4

俄罗斯方块游戏是一个上手简单,老少皆宜的游戏,它的基本规则是移动旋转摆放游戏自动产生的各种方块,使之排列成完整的一行或多行并且消除得分。

你能学到什么?

通过本文的阅读,读者可以对Swing版俄罗斯方块游戏的本身,对游戏中的关键点,如图形变换、键盘事件处理、游戏进度保存、满行和消行等都会有较好的理解。

游戏界面

界面组成

游戏界面有四个部分组成:

  1. 游戏面板区 -- 游戏主面板
  2. 下一个图形预览区 -- 呈现下一个图形
  3. 分数显示区 -- 显示目前的得分
  4. 菜单栏 -- 具备帮助、游戏设置、进度保存等功能

 

 

图形选择

一般来讲,一个图形有四个点,可以表示出常用的“一字型”,“T字型”,“Z字型”以及“L字型”方块。

如果将四个点的一个或者多个重叠,或者不采用常用的“一字型”,“T字型”,“Z字型”以及“L字型”方块,那么可以演变出更多的图形出来。如果想要更加丰富和复杂的图形,可以使用更多的点去表示想要的图形。

四个点组成的图形有如下几种:

常规图形

 

 

 

 

 

非重合的常规图形

如果将四个点中的一个或者多个点重合,就可以有如下几种类型的图形:


 

无重合点的不常规图

如果四个点不重合,还可以有如下几种比较诡异的图形,可以为游戏增加难度。

图形对象类表示

格子类( Grid.java ) ,俄罗斯方块游戏中的图形由四个格子组成。每个格子类有下x , y两个坐标位置, 颜色,以及格子图形绘制方法等。

如:

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

 

如有任何错误,请大家指正。

如有任何建议,也请告诉我,谢谢。

 

 

© 著作权归作者所有

王孟君

王孟君

粉丝 227
博文 94
码字总数 221044
作品 0
杭州
高级程序员
私信 提问
Qt Quick实现的疯狂算数游戏

使用 Qt Quick 写了个小游戏:疯狂算数。支持 Windows 和 Android 两个平台。 游戏简单,但牵涉到下面你的 Qt Quick 主题: 自己实现一个按钮 自适应分辨率 国际化 QML与C++混合编程 APK图标...

foruok
2015/01/10
0
0
CoderZh首款Python联机对战游戏 - NancyTetris1.0倾情发布(一)

第一次写游戏,第一次用Python写游戏,所以决定写个俄罗斯,还支持联机对战! 由于在游戏编写的数据结构及算法设计上经验不足,同时在实现联机对战过程中仅仅使用了一些简单TCP/IP数据传输的...

zting科技
2017/01/11
0
0
和平之翼代码生成器单机 1.6 版--PeaceWingSMEU_Swing_1_6

和平之翼代码生成器SMEU Swing单机1.6版 ,是和平之翼代码生成器的Swing单机版,是可运行的Jar包。 在装好JDK或JRE的计算机上双击即可执行。也可在命令窗口以java -jar PeaceWingSMEUSwing1...

火箭船
2017/08/28
1K
1
和平之翼代码生成器 SMEU Swing 单机版 - PeaceWingSMEU_Swing

和平之翼代码生成器SMEU Swing单机版,是可运行的Jar包。在装好JDK或JRE的计算机上双击即可执行。也可在命令窗口以java -jar PeaceWingSMEUSwing16.jar运行。 本软件包含两个分支:SMEUSwing...

火箭船
2018/10/25
861
2
各位大神,小弟昨天写的俄罗斯方块,出了个bug始终解决不了

小弟初学,用swing写了个俄罗斯方块,本来没毛病,但后来老是出现游戏窗口方块混乱的情况, 正常情况应该是这样的 但有时就是这样 求大神解决,感激不尽 源码奉上 http://pan.baidu.com/s/1...

Vicete
2017/04/03
301
1

没有更多内容

加载失败,请刷新页面

加载更多

一、docker 入坑(win10和Ubuntu 安装)

前言 终究还是绕不过去了,要学的知识真的是太多了,好在我们还有时间,docker 之前只闻其声,不曾真正的接触过,现在docker 越来越火,很多公司也都开始使用了。所以对于我们程序员而言,又...

quellanan2
19分钟前
4
0
AutoCompleteTextView

小技巧按菜单键 当菜单打开之前会调用onMenuOpened(int featereId,Menu menu),可以重写这个方法,弹出对话框或者Popmenu 再布局中添加控件AutoCompleteTextView. <AutoCompleteTextVie...

逆天游云
23分钟前
4
0
谷歌软件商店:推出5美元会员 可用数百个软件

腾讯科技讯,谷歌和苹果是全球两大智能手机操作系统的运营者,两家公司旗下分别拥有占据行业垄断地位的谷歌软件商店和苹果软件商店。据外媒最新消息,手机软件商店的商业模式正在发生一些变化...

linuxCool
45分钟前
3
0
RocketMQ 多副本前置篇:初探raft协议

Raft协议是分布式领域解决一致性的又一著名协议,主要包含Leader选举、日志复制两个部分。 温馨提示: 本文根据raft官方给出的raft动画进行学习,其动画展示地址:http://thesecretlivesofda...

中间件兴趣圈
45分钟前
3
0
elasticsearch 6.8.0 添加认证

1. 修改elasticsearch-6.8.0/config/elasticsearch.yml 最后添加一行:xpack.security.enabled: true 2. 初始化用户和密码 ./bin/elasticsearch-setup-passwords interactive 我这里初始化为......

coord
47分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部