俄罗斯方块JAVA

 

  上学的时候用 C 写过俄罗斯方块,用 JAVA 再实现一次。

  外公走了两个多月了,年初刚刚疫情的时候,外公还常给我打电话,让我在外面注意安全,不要乱跑。现在想想就是黄粱一梦。

  外公为人极好,真心的希望每个他遇到的人能过好。好到从一个村里的民办教师,被众人生生的托举到了副市级的位置。

  外公也极不容易,家里无权无势,每天回家是六个等着吃饭的弟弟,后来又加上了三个子女,全靠他在外面爬冰卧雪、。

  我大舅说,直到我和我弟弟出生,他们才知道外公还会笑。

  外公不只是我的外公,也是我的挚友。不管我学习好学习差,不管我要去做什么,永远是他看的起我,我看的起他。

  外公让自己的子女考体制,给家里创造稳定的条件。但到了我和我弟弟这儿就变成了不要进体制里来,做你自己想做的事,快乐就好。

  上次外公过生日,回家的时候嘴里一直嘟嘟囔囔的,嫌一个大爷太抠,送的烟太差。我拉着他说我挣钱了我给你买好的,一路半推半就,夹杂着他嘴里的“不用吧不用吧”和诚实的双手,我们带了四条好烟回家。这可能是我唯一的孝顺了。

  因为再后来我给他买的好东西,他都吃不下去了。临走那天我喂他吃了一颗葡萄,也没有咽下去。我欠外公的这一辈子,不差这一颗葡萄,但我还是难以释怀。

  外公跟我说的最后一句话是,别在这儿耗着了,和你弟弟回家把院里的葡萄摘了。

  我照做了。但是没能减轻我一丝一毫的剧痛。

  我一个受过高等教育的人,对我们还能团聚深信不疑。

  红楼开篇写的真好:满纸荒唐言,一把辛酸泪。都云作者痴,谁解其中味。

  胡言乱语到此为止,进入正题。

  俄罗斯方块实现思路很简单,在一个二维矩阵上渲染不断位移的图形,在图形无法下落时进行进行计分处理。

  二维矩阵上的图形由一个个基本的坐标点组成,首先实现坐标点:

public class Cell {
    private int row;
    private int column;
    private BufferedImage bufferedImage;

    public int getRow() {
        return row;
    }

    public void setRow(int row) {
        this.row = row;
    }

    public int getColumn() {
        return column;
    }

    public void setColumn(int column) {
        this.column = column;
    }

    public BufferedImage getBufferedImage() {
        return bufferedImage;
    }

    public void setBufferedImage(BufferedImage bufferedImage) {
        this.bufferedImage = bufferedImage;
    }

    public Cell(int row, int column, BufferedImage bufferedImage) {
        this.row = row;
        this.column = column;
        this.bufferedImage = bufferedImage;
    }

    public void moveLeft() {
        this.column--;
    }

    public void moveRight() {
        this.column++;
    }

    public void moveDown() {
        this.row++;
    }
}

  基于坐标点封装图形的基类:

public abstract class BaseTetromino {
    protected Cell[] cells = new Cell[4];

    Cell baseCell = new Cell(0, 0, null);

    //当前图形索引
    protected int currentShape;

    //图形集合
    protected Cell[][] shapes = new Cell[4][4];

    //初始化图形集合
    protected abstract void initShips();

    //图形顺时针旋转
    public void spin(Cell[][] cellWall) {
        Cell[] nextShape = copyCells(shapes[(currentShape + 1) % shapes.length]);
        long offSet = getOffset();
        int rowOffset = (int) (offSet >> 32), columnOffset = (int) offSet;
        for (Cell cell : nextShape) {
            cell.setColumn(cell.getColumn() + columnOffset);
            cell.setRow(cell.getRow() + rowOffset);
            if (isOutOfWall(cellWall, cell) || isOccupied(cellWall, cell)) return;
        }
        cells = nextShape;
        currentShape++;
    }

    public Cell[] getCells() {
        return cells;
    }

    public void setCells(Cell[] cells) {
        this.cells = cells;
    }

    //左移
    public void moveLeft() {
        for (Cell cell : cells) cell.moveLeft();
        baseCell.moveLeft();
    }

    //右移
    public void moveRight() {
        for (Cell cell : cells) cell.moveRight();
        baseCell.moveRight();
    }

    //下移
    public void moveDown() {
        for (Cell cell : cells) cell.moveDown();
        baseCell.moveDown();
    }

    //图像顺时针旋转,O(N方),弃用
    protected final void rotate(int[][] matrix) {
        int n = matrix.length;
        // transpose matrix
        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                int tmp = matrix[j][i];
                matrix[j][i] = matrix[i][j];
                matrix[i][j] = tmp;
            }
        }
        // reverse each row
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n / 2; j++) {
                int tmp = matrix[i][j];
                matrix[i][j] = matrix[i][n - j - 1];
                matrix[i][n - j - 1] = tmp;
            }
        }
    }

    //获取原点偏移量
    protected final long getOffset() {
        return ((long) baseCell.getRow() << 32) | baseCell.getColumn();
    }

    //获取最左上方偏移量
    protected final long getRealOffset() {
        int rowOffset = Integer.MAX_VALUE, columnOffet = Integer.MAX_VALUE;
        for (Cell cell : cells) {
            rowOffset = Math.min(rowOffset, cell.getRow());
            columnOffet = Math.min(columnOffet, cell.getColumn());
        }
        return (((long) rowOffset) << 32) | columnOffet;
    }

    //深拷贝 Cell 集合
    protected final Cell[] copyCells(Cell[] source) {
        Cell[] re = new Cell[source.length];
        for (int i = 0; i < source.length; i++) {
            re[i] = new Cell(source[i].getRow(), source[i].getColumn(), source[i].getBufferedImage());
        }
        return re;
    }

    // cell 是否被占位
    protected final boolean isOccupied(Cell[][] wall, Cell cell) {
        if (wall[cell.getColumn()][cell.getRow()] == null) return false;
        return true;
    }

    // cell 是否在墙外
    protected final boolean isOutOfWall(Cell[][] wall, Cell cell) {
        if (cell.getColumn() >= wall.length || cell.getRow() >= wall[0].length) return true;
        if (cell.getColumn() < 0 || cell.getRow() < 0) return true;
        return false;
    }

    // 随机获取图形
    public static BaseTetromino randomTetromino() {
        int random = (int) (Math.random() * 7);
        switch (random) {
            case 0:
                return new Tetromino0();
            case 1:
                return new Tetromino1();
            case 2:
                return new Tetromino2();
            case 3:
                return new Tetromino3();
            case 4:
                return new Tetromino4();
            case 5:
                return new Tetromino5();
            case 6:
                return new Tetromino6();
        }
        return null;
    }

}

  七种图形的实现:

public class Tetromino0 extends BaseTetromino {
    //正方形
    public Tetromino0() {
        cells[0] = new Cell(0, 0, Tetris.bufferedImage0);
        cells[1] = new Cell(0, 1, Tetris.bufferedImage1);
        cells[2] = new Cell(1, 0, Tetris.bufferedImage2);
        cells[3] = new Cell(1, 1, Tetris.bufferedImage3);
    }

    @Override
    protected void initShips() {
        shapes = null;
    }

    @Override
    public void spin(Cell[][] wall) {
        return;
    }
}
/**
* @Author Niuxy
* @Date 2020/11/25 8:34 下午
* @Description T 字型
*/
public class Tetromino1 extends BaseTetromino {
    private int currentShape;

    //T 字型
    public Tetromino1() {
        shapes = new Cell[4][];
        initShips();
        currentShape = (int) (Math.random() * 3);
        cells = copyCells(shapes[currentShape%4]);
    }

    @Override
    protected void initShips() {
        Cell[] shape0 = new Cell[4];
        shape0[0] = new Cell(1, 0, Tetris.bufferedImage3);
        shape0[1] = new Cell(1, 1, Tetris.bufferedImage4);
        shape0[2] = new Cell(1, 2, Tetris.bufferedImage5);
        shape0[3] = new Cell(2, 1, Tetris.bufferedImage6);
        Cell[] shape1 = new Cell[4];
        shape1[0] = new Cell(0, 1, Tetris.bufferedImage3);
        shape1[1] = new Cell(1, 1, Tetris.bufferedImage4);
        shape1[2] = new Cell(2, 1, Tetris.bufferedImage5);
        shape1[3] = new Cell(1, 0, Tetris.bufferedImage6);
        Cell[] shape2 = new Cell[4];
        shape2[0] = new Cell(0, 1, Tetris.bufferedImage3);
        shape2[1] = new Cell(1, 0, Tetris.bufferedImage4);
        shape2[2] = new Cell(1, 1, Tetris.bufferedImage5);
        shape2[3] = new Cell(1, 2, Tetris.bufferedImage6);
        Cell[] shape3 = new Cell[4];
        shape3[0] = new Cell(0, 1, Tetris.bufferedImage3);
        shape3[1] = new Cell(1, 1, Tetris.bufferedImage4);
        shape3[2] = new Cell(2, 1, Tetris.bufferedImage5);
        shape3[3] = new Cell(1, 2, Tetris.bufferedImage6);
        shapes[0] = shape0;
        shapes[1] = shape1;
        shapes[2] = shape2;
        shapes[3] = shape3;
    }
}
/**
 * @Author Niuxy
 * @Date 2020/11/25 8:12 下午
 * @Description L 型
 */
public class Tetromino2 extends BaseTetromino {

    public Tetromino2() {
        shapes = new Cell[4][];
        initShips();
        currentShape = (int) (Math.random() * 3);
        cells = copyCells(shapes[currentShape]);
    }

    protected void initShips() {
        Cell[] shape0 = new Cell[4];
        shape0[0] = new Cell(0, 0, Tetris.bufferedImage3);
        shape0[1] = new Cell(0, 1, Tetris.bufferedImage4);
        shape0[2] = new Cell(0, 2, Tetris.bufferedImage5);
        shape0[3] = new Cell(1, 0, Tetris.bufferedImage6);
        Cell[] shape1 = new Cell[4];
        shape1[0] = new Cell(0, 0, Tetris.bufferedImage3);
        shape1[1] = new Cell(1, 0, Tetris.bufferedImage4);
        shape1[2] = new Cell(2, 0, Tetris.bufferedImage5);
        shape1[3] = new Cell(2, 1, Tetris.bufferedImage6);
        Cell[] shape2 = new Cell[4];
        shape2[0] = new Cell(0, 2, Tetris.bufferedImage3);
        shape2[1] = new Cell(1, 0, Tetris.bufferedImage4);
        shape2[2] = new Cell(1, 1, Tetris.bufferedImage5);
        shape2[3] = new Cell(1, 2, Tetris.bufferedImage6);
        Cell[] shape3 = new Cell[4];
        shape3[0] = new Cell(0, 0, Tetris.bufferedImage3);
        shape3[1] = new Cell(0, 1, Tetris.bufferedImage4);
        shape3[2] = new Cell(1, 1, Tetris.bufferedImage5);
        shape3[3] = new Cell(2, 1, Tetris.bufferedImage6);
        shapes[0] = shape0;
        shapes[1] = shape1;
        shapes[2] = shape2;
        shapes[3] = shape3;
    }
}
public class Tetromino3 extends BaseTetromino {
    //长条形
    public Tetromino3() {
        shapes = new Cell[2][];
        initShips();
        currentShape = (int) (Math.random() * 2);
        cells = copyCells(shapes[currentShape]);
    }

    @Override
    protected void initShips() {
        Cell[] shape0 = new Cell[4];
        shape0[0] = new Cell(1, 0, Tetris.bufferedImage3);
        shape0[1] = new Cell(1, 1, Tetris.bufferedImage4);
        shape0[2] = new Cell(1, 2, Tetris.bufferedImage5);
        shape0[3] = new Cell(1, 3, Tetris.bufferedImage6);
        Cell[] shape1 = new Cell[4];
        shape1[0] = new Cell(0, 1, Tetris.bufferedImage3);
        shape1[1] = new Cell(1, 1, Tetris.bufferedImage4);
        shape1[2] = new Cell(2, 1, Tetris.bufferedImage5);
        shape1[3] = new Cell(3, 1, Tetris.bufferedImage6);
        shapes[0] = shape0;
        shapes[1] = shape1;
    }
}
public class Tetromino4 extends BaseTetromino {
    //Z 字形
    public Tetromino4() {
        shapes = new Cell[2][];
        initShips();
        currentShape = (int) (Math.random() * 2);
        cells = copyCells(shapes[currentShape]);
    }

    @Override
    protected void initShips() {
        Cell[] shape0 = new Cell[4];
        shape0[0] = new Cell(0, 0, Tetris.bufferedImage3);
        shape0[1] = new Cell(0, 1, Tetris.bufferedImage4);
        shape0[2] = new Cell(1, 1, Tetris.bufferedImage5);
        shape0[3] = new Cell(1, 2, Tetris.bufferedImage6);
        Cell[] shape1 = new Cell[4];
        shape1[0] = new Cell(0, 1, Tetris.bufferedImage3);
        shape1[1] = new Cell(1, 0, Tetris.bufferedImage4);
        shape1[2] = new Cell(1, 1, Tetris.bufferedImage5);
        shape1[3] = new Cell(2, 0, Tetris.bufferedImage6);
        shapes[0] = shape0;
        shapes[1] = shape1;
    }
}
public class Tetromino5 extends BaseTetromino {
    //倒 Z 字形
    public Tetromino5() {
        shapes = new Cell[2][];
        initShips();
        currentShape = (int) (Math.random() * 2);
        cells = copyCells(shapes[currentShape]);
    }

    @Override
    protected void initShips() {
        Cell[] shape0 = new Cell[4];
        shape0[0] = new Cell(0, 1, Tetris.bufferedImage3);
        shape0[1] = new Cell(0, 2, Tetris.bufferedImage4);
        shape0[2] = new Cell(1, 0, Tetris.bufferedImage5);
        shape0[3] = new Cell(1, 1, Tetris.bufferedImage6);
        Cell[] shape1 = new Cell[4];
        shape1[0] = new Cell(0, 1, Tetris.bufferedImage3);
        shape1[1] = new Cell(1, 1, Tetris.bufferedImage4);
        shape1[2] = new Cell(1, 2, Tetris.bufferedImage5);
        shape1[3] = new Cell(2, 2, Tetris.bufferedImage6);
        shapes[0] = shape0;
        shapes[1] = shape1;
    }
}
/**
 * @Author Niuxy
 * @Date 2020/11/25 8:12 下午
 * @Description 倒 L 型
 */
public class Tetromino6 extends BaseTetromino {

    public Tetromino6() {
        shapes = new Cell[4][];
        initShips();
        currentShape = (int) (Math.random() * 3);
        cells = copyCells(shapes[currentShape]);
    }


    protected void initShips() {
        Cell[] shape0 = new Cell[4];
        shape0[0] = new Cell(0, 1, Tetris.bufferedImage3);
        shape0[1] = new Cell(1, 1, Tetris.bufferedImage4);
        shape0[2] = new Cell(2, 1, Tetris.bufferedImage5);
        shape0[3] = new Cell(2, 0, Tetris.bufferedImage6);
        Cell[] shape1 = new Cell[4];
        shape1[0] = new Cell(0, 0, Tetris.bufferedImage3);
        shape1[1] = new Cell(1, 0, Tetris.bufferedImage4);
        shape1[2] = new Cell(1, 1, Tetris.bufferedImage5);
        shape1[3] = new Cell(1, 2, Tetris.bufferedImage6);
        Cell[] shape2 = new Cell[4];
        shape2[0] = new Cell(0, 1, Tetris.bufferedImage3);
        shape2[1] = new Cell(0, 2, Tetris.bufferedImage4);
        shape2[2] = new Cell(1, 1, Tetris.bufferedImage5);
        shape2[3] = new Cell(2, 1, Tetris.bufferedImage6);
        Cell[] shape3 = new Cell[4];
        shape3[0] = new Cell(1, 0, Tetris.bufferedImage3);
        shape3[1] = new Cell(1, 1, Tetris.bufferedImage4);
        shape3[2] = new Cell(1, 2, Tetris.bufferedImage5);
        shape3[3] = new Cell(2, 2, Tetris.bufferedImage6);
        shapes[0] = shape0;
        shapes[1] = shape1;
        shapes[2] = shape2;
        shapes[3] = shape3;
    }
}

  封装游戏规则:

public class Tetris extends JPanel {
    private BaseTetromino currentTeromino = BaseTetromino.randomTetromino();
    private BaseTetromino nextTeromino = BaseTetromino.randomTetromino();
    private static final int CELL_SIZE = 26;
    private static final int X_SIZE = 10;
    private static final int Y_SIZE = 20;
    //刷新时间
    private static final int INTERVAL = 1000;
    private static int mark = 0;
    private Cell[][] wall = new Cell[X_SIZE][Y_SIZE];


    public static BufferedImage bufferedImage0;
    public static BufferedImage bufferedImage1;
    public static BufferedImage bufferedImage2;
    public static BufferedImage bufferedImage3;
    public static BufferedImage bufferedImage4;
    public static BufferedImage bufferedImage5;
    public static BufferedImage bufferedImage6;
    public static BufferedImage background;
    public static BufferedImage gameOver;

    static {
        try {
            bufferedImage0 = ImageIO.read(Tetris.class.getResource("img/stone0.png"));
            bufferedImage1 = ImageIO.read(Tetris.class.getResource("img/stone1.png"));
            bufferedImage2 = ImageIO.read(Tetris.class.getResource("img/stone2.png"));
            bufferedImage3 = ImageIO.read(Tetris.class.getResource("img/stone3.png"));
            bufferedImage4 = ImageIO.read(Tetris.class.getResource("img/stone4.png"));
            bufferedImage5 = ImageIO.read(Tetris.class.getResource("img/stone5.png"));
            bufferedImage6 = ImageIO.read(Tetris.class.getResource("img/stone6.png"));
            background = ImageIO.read(Tetris.class.getResource("img/background1.png"));
            gameOver = ImageIO.read(Tetris.class.getResource("img/background1.png"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void paint(Graphics g) {
        g.drawImage(background, 0, 0, null);
        g.translate(15, 15);
        paintWall(g);
        paintCurrentOne(g);
        paintNextOne(g);
    }

    // 渲染下一个图形
    private void paintNextOne(Graphics g) {
        Cell[] cells = nextTeromino.getCells();
        for (Cell cell : cells) {
            int x = cell.getColumn();
            int y = cell.getRow();
            g.drawImage(cell.getBufferedImage(), x * CELL_SIZE + 260, y * CELL_SIZE + 26, null);
        }
    }

    //渲染当前图形
    private void paintCurrentOne(Graphics g) {
        Cell[] cells = currentTeromino.getCells();
        for (Cell cell : cells) {
            g.drawImage(cell.getBufferedImage(), cell.getColumn() * CELL_SIZE, cell.getRow() * CELL_SIZE, null);
        }
    }

    //渲染墙壁
    private void paintWall(Graphics g) {
        for (int i = 0; i < X_SIZE; i++) {
            for (int j = 0; j < Y_SIZE; j++) {
                if (wall[i][j] == null) g.drawRect(i, j, CELL_SIZE, CELL_SIZE);
                else g.drawImage(wall[i][j].getBufferedImage(), i * CELL_SIZE, j * CELL_SIZE, null);
            }
        }
    }

    //渲染得分
    private void paintMark(Graphics g) {
        g.drawString(String.valueOf(mark), 300, 30);
    }

    public void start() {
        KeyListener listener = new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                int code = e.getKeyCode();
                switch (code) {
                    case KeyEvent.VK_DOWN:
                        moveDown();
                        break;
                    case KeyEvent.VK_LEFT:
                        moveLeft();
                        break;
                    case KeyEvent.VK_RIGHT:
                        moveRight();
                        break;
                    case KeyEvent.VK_UP:
                        spin();
                        break;
                    default:
                        break;
                }
                repaint();
            }
        };

        this.addKeyListener(listener);
        this.requestFocus();
        while (true) {
            try {
                Thread.sleep(INTERVAL);
            } catch (InterruptedException interruptedException) {
                interruptedException.printStackTrace();
            }
            if (canDrop()) currentTeromino.moveDown();
            else {
                landToWall();
                currentTeromino = nextTeromino;
                nextTeromino = BaseTetromino.randomTetromino();
            }
            repaint();
        }
    }

    /**
     * @Author Niuxy
     * @Date 2020/11/29 7:26 下午
     * @Description 画布下沉并计分
     * 递归的进行行下沉处理
     * 每处理完一行,下一次递归由该行继续
     * 不再重复遍历该行以下的行
     */
    private final void elimination() {
        int index = Y_SIZE - 1;
        while (index > -1) {
            if (!isFull(index)) {
                index--;
                continue;
            }
            //空行剪枝
            if (isEmpty(index)) break;
            sink(index);
            //计分
            this.mark++;
        }
    }

    //画布下沉
    private final void sink(int rowIndex) {
        if (rowIndex == 0) {
            for (int i = 0; i < X_SIZE; i++) {
                wall[i][0] = null;
            }
            return;
        }
        for (int i = 0; i < X_SIZE; i++) {
            wall[i][rowIndex] = wall[i][rowIndex - 1];
        }
        sink(rowIndex - 1);
    }

    //判断 index 行是否已满
    private boolean isFull(int index) {
        if (index < 0 || index >= Y_SIZE) throw new RuntimeException("index is out of the wall!");
        for (int i = 0; i < X_SIZE; i++) {
            if (wall[i][index] == null) return false;
        }
        return true;
    }

    //判断空行
    private final boolean isEmpty(int index) {
        if (index < 0 || index >= Y_SIZE) throw new RuntimeException("index is out of the wall!");
        for (int i = 0; i < X_SIZE; i++) {
            if (wall[i][index] != null) return false;
        }
        return true;
    }

    // 旋转图形
    public void spin() {
        currentTeromino.spin(wall);
    }

    //键盘监听事件-图形右移
    public void moveRight() {
        currentTeromino.moveRight();
        if (outOfBounds() || coincide()) currentTeromino.moveLeft();
    }

    //键盘监听事件-图形左移
    public void moveLeft() {
        currentTeromino.moveLeft();
        if (outOfBounds() || coincide()) currentTeromino.moveRight();
    }

    //键盘监听事件-图形下移
    public void moveDown() {
        if (canDrop()) currentTeromino.moveDown();
        else {
            landToWall();
            currentTeromino = nextTeromino;
            nextTeromino = BaseTetromino.randomTetromino();
        }
    }

    //图形坐标是否重复
    private boolean coincide() {
        Cell[] cells = currentTeromino.getCells();
        for (Cell cell : cells) {
            if (wall[cell.getColumn()][cell.getRow()] != null) return true;
        }
        return false;
    }

    //图形是否超出画布
    private boolean outOfBounds() {
        Cell[] cells = currentTeromino.getCells();
        for (Cell cell : cells) {
            if (cell.getColumn() < 0 || cell.getColumn() >= X_SIZE || cell.getRow() >= Y_SIZE - 1) return true;
        }
        return false;
    }

    //图形是否可以下移
    public boolean canDrop() {
        Cell[] cells = currentTeromino.getCells();
        for (Cell c : cells) {
            if (c.getRow() == Y_SIZE - 1 || wall[c.getColumn()][c.getRow() + 1] != null) return false;
        }
        return true;
    }

    //图形位置确定,载入画布
    private void landToWall() {
        Cell[] cells = currentTeromino.getCells();
        for (Cell c : cells) wall[c.getColumn()][c.getRow()] = c;
        elimination();
    }

    public static void main(String[] args) {
        JFrame jFrame = new JFrame("斯巴达!");
        Tetris tetris = new Tetris();
        jFrame.add(tetris);
        jFrame.setVisible(true);
        jFrame.setSize(535, 595);
        jFrame.setLocationRelativeTo(null);
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        tetris.start();
    }
}

 

posted @ 2020-11-30 22:22  牛有肉  阅读(265)  评论(0编辑  收藏  举报