Java实现俄罗斯方块小游戏。(附完整源代码)

326 阅读8分钟

一、游戏背景 俄罗斯方块是俄罗斯人发明的。这人叫阿列克谢·帕基特诺夫(Алексей Пажитнов 英文:Alexey Pazhitnov)。俄罗斯方块原名是俄语Тетрис(英语是Tetris),这个名字来源于希腊语tetra,意思是“四”,而游戏的作者最喜欢网球(tennis)。于是,他把两个词tetra和tennis合而为一,命名为Tetris,这也就是俄罗斯方块名字的由来。

规则说明:

由小方块组成的不同形状的板块陆续从屏幕上方落下来,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条。这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束。

二、功能实现 开发工具:idea、jdk8

技术汇总:Java基础知识、数组、面向对象、多线程、IO流、Swing。

整体代码分为三个模块:方格模块,七种图形模块,俄罗斯方块主模块。

小方块类:Cell

public class Cell { // 行 private int row; // 列 private int col; private BufferedImage image;

public Cell() {
}

public Cell(int row, int col, BufferedImage image) {
    this.row = row;
    this.col = col;
    this.image = image;
}

public int getRow() {
    return row;
}

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

public int getCol() {
    return col;
}

public void setCol(int col) {
    this.col = col;
}

public BufferedImage getImage() {
    return image;
}

public void setImage(BufferedImage image) {
    this.image = image;
}

@Override
public String toString() {
    return "Cell{" +
            "row=" + row +
            ", col=" + col +
            ", image=" + image +
            '}';
}

@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }

    if (!(o instanceof Cell)) {
        return false;
    }
    Cell cell = (Cell) o;
    return getRow() == cell.getRow() &&
            getCol() == cell.getCol() &&
            Objects.equals(getImage(), cell.getImage());
}

@Override
public int hashCode() {
    return Objects.hash(getRow(), getCol(), getImage());
}

//左移动一格
public void left(){
    col--;
}

//右移动一格
public void right(){
    col++;
}

//下移动一格
public void down(){
    row++;
}

} 四方格图形的父类:Tetromino

public class Tetromino {

public Cell[] cells = new Cell[4];

//旋转的状态
protected State[] states;
//声明旋转次数
protected int count = 10000;


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

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

//单元格下落
public void moveDrop() {
    for (Cell cell : cells) {
        cell.down();
    }
}

//编写随机生成四方格
public static Tetromino randomOne() {
    int num = (int) (Math.random() * 7);
    Tetromino tetromino = null;
    switch (num) {
        case 0:
            tetromino = new I();
            break;
        case 1:
            tetromino = new J();
            break;
        case 2:
            tetromino = new L();
            break;
        case 3:
            tetromino = new O();
            break;
        case 4:
            tetromino = new S();
            break;
        case 5:
            tetromino = new T();
            break;
        case 6:
            tetromino = new Z();
            break;
    }

    return tetromino;
}

//顺时针旋转的方法
public void rotateRight() {
    if (states.length == 0) {
        return;
    }

    //旋转次数+1
    count++;
    State s = states[count % states.length];
    Cell cell = cells[0];
    int row = cell.getRow();
    int col = cell.getCol();
    cells[1].setRow(row + s.row1);
    cells[1].setCol(col + s.col1);
    cells[2].setRow(row + s.row2);
    cells[2].setCol(col + s.col2);
    cells[3].setRow(row + s.row3);
    cells[3].setCol(col + s.col3);
}

//逆时针旋转的方法
public void rotateLeft() {
    if (states.length == 0) {
        return;
    }

    //旋转次数+1
    count--;
    State s = states[count % states.length];
    Cell cell = cells[0];
    int row = cell.getRow();
    int col = cell.getCol();
    cells[1].setRow(row + s.row1);
    cells[1].setCol(col + s.col1);
    cells[2].setRow(row + s.row2);
    cells[2].setCol(col + s.col2);
    cells[3].setRow(row + s.row3);
    cells[3].setCol(col + s.col3);
}

//四方格旋转状态的内部类
protected class State {
    //存储四方格各元素的位置
    int row0, col0, row1, col1, row2, col2, row3, col3;

    public State() {
    }

    public State(int row0, int col0, int row1, int col1, int row2, int col2, int row3, int col3) {
        this.row0 = row0;
        this.col0 = col0;
        this.row1 = row1;
        this.col1 = col1;
        this.row2 = row2;
        this.col2 = col2;
        this.row3 = row3;
        this.col3 = col3;
    }

    public int getRow0() {
        return row0;
    }

    public void setRow0(int row0) {
        this.row0 = row0;
    }

    public int getCol0() {
        return col0;
    }

    public void setCol0(int col0) {
        this.col0 = col0;
    }

    public int getRow1() {
        return row1;
    }

    public void setRow1(int row1) {
        this.row1 = row1;
    }

    public int getCol1() {
        return col1;
    }

    public void setCol1(int col1) {
        this.col1 = col1;
    }

    public int getRow2() {
        return row2;
    }

    public void setRow2(int row2) {
        this.row2 = row2;
    }

    public int getCol2() {
        return col2;
    }

    public void setCol2(int col2) {
        this.col2 = col2;
    }

    public int getRow3() {
        return row3;
    }

    public void setRow3(int row3) {
        this.row3 = row3;
    }

    public int getCol3() {
        return col3;
    }

    public void setCol3(int col3) {
        this.col3 = col3;
    }

    @Override
    public String toString() {
        return "State{" +
                "row0=" + row0 +
                ", col0=" + col0 +
                ", row1=" + row1 +
                ", col1=" + col1 +
                ", row2=" + row2 +
                ", col2=" + col2 +
                ", row3=" + row3 +
                ", col3=" + col3 +
                '}';
    }
}

} 七种图形类:I、J、L、O、S、T、Z

public class I extends Tetromino {

public I() {
    cells[0] = new Cell(0,4, Tetris.I);
    cells[1] = new Cell(0,3, Tetris.I);
    cells[2] = new Cell(0,5, Tetris.I);
    cells[3] = new Cell(0,6, Tetris.I);

    //共有两种旋转状态
    states =new State[2];
    //初始化两种状态的相对坐标
    states[0]=new State(0,0,0,-1,0,1,0,2);
    states[1]=new State(0,0,-1,0,1,0,2,0);
}

} public class J extends Tetromino { public J() { cells[0] = new Cell(0,4, Tetris.J); cells[1] = new Cell(0,3, Tetris.J); cells[2] = new Cell(0,5, Tetris.J); cells[3] = new Cell(1,5, Tetris.J);

    states=new State[4];
    states[0]=new State(0,0,0,-1,0,1,1,1);
    states[1]=new State(0,0,-1,0,1,0,1,-1);
    states[2]=new State(0,0,0,1,0,-1,-1,-1);
    states[3]=new State(0,0,1,0,-1,0,-1,1);
}

} public class L extends Tetromino { public L() { cells[0] = new Cell(0,4, Tetris.L); cells[1] = new Cell(0,3, Tetris.L); cells[2] = new Cell(0,5, Tetris.L); cells[3] = new Cell(1,3, Tetris.L);

    states=new State[4];
    states[0]=new State(0,0,0,-1,0,1,1,-1);
    states[1]=new State(0,0,-1,0,1,0,-1,-1);
    states[2]=new State(0,0,0,1,0,-1,-1,1);
    states[3]=new State(0,0,1,0,-1,0,1,1);
}

} public class O extends Tetromino { public O() { cells[0] = new Cell(0, 4, Tetris.O); cells[1] = new Cell(0, 5, Tetris.O); cells[2] = new Cell(1, 4, Tetris.O); cells[3] = new Cell(1, 5, Tetris.O);

    //无旋转状态
    states = new State[0];
}

} public class S extends Tetromino { public S() { cells[0] = new Cell(0,4, Tetris.S); cells[1] = new Cell(0,5, Tetris.S); cells[2] = new Cell(1,3, Tetris.S); cells[3] = new Cell(1,4, Tetris.S);

    //共有两种旋转状态
    states =new State[2];
    //初始化两种状态的相对坐标
    states[0]=new State(0,0,0,1,1,-1,1,0);
    states[1]=new State(0,0,1,0,-1,-1,0,-1);
}

} public class T extends Tetromino { public T() { cells[0] = new Cell(0,4, Tetris.T); cells[1] = new Cell(0,3, Tetris.T); cells[2] = new Cell(0,5, Tetris.T); cells[3] = new Cell(1,4, Tetris.T);

    states=new State[4];
    states[0]=new State(0,0,0,-1,0,1,1,0);
    states[1]=new State(0,0,-1,0,1,0,0,-1);
    states[2]=new State(0,0,0,1,0,-1,-1,0);
    states[3]=new State(0,0,1,0,-1,0,0,1);
}

} public class Z extends Tetromino { public Z() { cells[0] = new Cell(1,4, Tetris.Z); cells[1] = new Cell(0,3, Tetris.Z); cells[2] = new Cell(0,4, Tetris.Z); cells[3] = new Cell(1,5, Tetris.Z);

    //共有两种旋转状态
    states =new State[2];
    //初始化两种状态的相对坐标
    states[0]=new State(0,0,-1,-1,-1,0,0,1);
    states[1]=new State(0,0,-1,1,0,1,1,0);
}

} 俄罗斯方块游戏主类:Tetris

public class Tetris extends JPanel {

//正在下落的方块
private Tetromino currentOne = Tetromino.randomOne();
//将要下落的方块
private Tetromino nextOne = Tetromino.randomOne();
//游戏主区域
private Cell[][] wall = new Cell[18][9];
//声明单元格的值
private static final int CELL_SIZE = 48;

//游戏分数池
int[] scores_pool = {0, 1, 2, 5, 10};
//当前游戏的分数
private int totalScore = 0;
//当前消除的行数
private int totalLine = 0;

//游戏三种状态 游戏中、暂停、结束
public static final int PLING = 0;
public static final int STOP = 1;
public static final int OVER = 2;
//当前游戏状态值
private int game_state;
//显示游戏状态
String[] show_state = {"P[pause]", "C[continue]", "S[replay]"};


//载入方块图片
public static BufferedImage I;
public static BufferedImage J;
public static BufferedImage L;
public static BufferedImage O;
public static BufferedImage S;
public static BufferedImage T;
public static BufferedImage Z;
public static BufferedImage background;

static {
    try {
        I = ImageIO.read(new File("images/I.png"));
        J = ImageIO.read(new File("images/J.png"));
        L = ImageIO.read(new File("images/L.png"));
        O = ImageIO.read(new File("images/O.png"));
        S = ImageIO.read(new File("images/S.png"));
        T = ImageIO.read(new File("images/T.png"));
        Z = ImageIO.read(new File("images/Z.png"));
        background = ImageIO.read(new File("images/background.png"));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Override
public void paint(Graphics g) {
    g.drawImage(background, 0, 0, null);
    //平移坐标轴
    g.translate(22, 15);
    //绘制游戏主区域
    paintWall(g);
    //绘制正在下落的四方格
    paintCurrentOne(g);
    //绘制下一个将要下落的四方格
    paintNextOne(g);
    //绘制游戏得分
    paintSource(g);
    //绘制当前游戏状态
    paintState(g);
}

public void start() {
    game_state = PLING;
    KeyListener l = new KeyAdapter() {
        @Override
        public void keyPressed(KeyEvent e) {
            int code = e.getKeyCode();
            switch (code) {
                case KeyEvent.VK_DOWN:
                    sortDropActive();
                    break;
                case KeyEvent.VK_LEFT:
                    moveleftActive();
                    break;
                case KeyEvent.VK_RIGHT:
                    moveRightActive();
                    break;
                case KeyEvent.VK_UP:
                    rotateRightActive();
                    break;
                case KeyEvent.VK_SPACE:
                        hadnDropActive();
                    break;
                case KeyEvent.VK_P:
                    //判断当前游戏状态
                    if (game_state == PLING) {
                        game_state = STOP;
                    }
                    break;
                case KeyEvent.VK_C:
                    if (game_state == STOP) {
                        game_state = PLING;
                    }
                    break;
                case KeyEvent.VK_S:
                    //重新开始
                    game_state = PLING;
                    wall = new Cell[18][9];
                    currentOne = Tetromino.randomOne();
                    nextOne = Tetromino.randomOne();
                    totalScore = 0;
                    totalLine = 0;
                    break;
            }
        }
    };
    //将窗口设置为焦点
    this.addKeyListener(l);
    this.requestFocus();

    while (true) {
        if (game_state == PLING) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (camDrop()) {
                currentOne.moveDrop();
            } else {
                landToWall();
                destroyLine();
                if (isGameOver()) {
                    game_state = OVER;
                } else {
                    //游戏没有结束
                    currentOne = nextOne;
                    nextOne = Tetromino.randomOne();
                }
            }
        }
        repaint();
    }
}

//创建顺时针旋转
public void rotateRightActive() {
    currentOne.rotateRight();
    if (outOFBounds() || coincide()) {
        currentOne.rotateLeft();
    }
}

//瞬间下落
public void hadnDropActive() {
    while (true) {
        //判断能否下落
        if (camDrop()) {
            currentOne.moveDrop();
        } else {
            break;
        }
    }
    //嵌入到墙中
    landToWall();
    destroyLine();
    if (isGameOver()) {
        game_state = OVER;
    } else {
        //游戏没有结束
        currentOne = nextOne;
        nextOne = Tetromino.randomOne();
    }
}

//按键一次,下落一格
public void sortDropActive() {
    if (camDrop()) {
        //当前四方格下落一格
        currentOne.moveDrop();
    } else {
        landToWall();
        destroyLine();
        if (isGameOver()) {
            game_state = OVER;
        } else {
            //游戏没有结束
            currentOne = nextOne;
            nextOne = Tetromino.randomOne();
        }
    }
}

//单元格嵌入墙中
private void landToWall() {
    Cell[] cells = currentOne.cells;
    for (Cell cell : cells) {
        int row = cell.getRow();
        int col = cell.getCol();
        wall[row][col] = cell;
    }
}

//判断四方格能否下落
public boolean camDrop() {
    Cell[] cells = currentOne.cells;
    for (Cell cell : cells) {
        int row = cell.getRow();
        int col = cell.getCol();
        //判断是否到达底部
        if (row == wall.length - 1) {
            return false;
        } else if (wall[row + 1][col] != null) {
            return false;
        }
    }
    return true;
}

//消除行
public void destroyLine() {
    int line = 0;
    Cell[] cells = currentOne.cells;
    for (Cell cell : cells) {
        int row = cell.getRow();
        if (isFullLine(row)) {
            line++;
            for (int i = row; i > 0; i--) {
                System.arraycopy(wall[i - 1], 0, wall[i], 0, wall[0].length);
            }
            wall[0] = new Cell[9];
        }
    }
    //分数池获取分数,累加到总分
    totalScore += scores_pool[line];
    //总行数
    totalLine += line;
}

//判断当前行是否已经满了
public boolean isFullLine(int row) {
    Cell[] cells = wall[row];
    for (Cell cell : cells) {
        if (cell == null) {
            return false;
        }
    }
    return true;
}

//判断游戏是否结束
public boolean isGameOver() {
    Cell[] cells = nextOne.cells;
    for (Cell cell : cells) {
        int row = cell.getRow();
        int col = cell.getCol();
        if (wall[row][col] != null) {
            return true;
        }
    }
    return false;
}

private void paintState(Graphics g) {
    if (game_state == PLING) {
        g.drawString(show_state[PLING], 500, 660);
    } else if (game_state == STOP) {
        g.drawString(show_state[STOP], 500, 660);
    } else {
        g.drawString(show_state[OVER], 500, 660);
        g.setColor(Color.RED);
        g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 60));
        g.drawString("GAME OVER!", 30, 400);
    }
}

private void paintSource(Graphics g) {
    g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 30));
    g.drawString("分数: " + totalScore, 500, 250);
    g.drawString("行数: " + totalLine, 500, 430);
}

private void paintNextOne(Graphics g) {
    Cell[] cells = nextOne.cells;
    for (Cell cell : cells) {
        int x = cell.getCol() * CELL_SIZE + 370;
        int y = cell.getRow() * CELL_SIZE + 25;
        g.drawImage(cell.getImage(), x, y, null);
    }
}

private void paintCurrentOne(Graphics g) {
    Cell[] cells = currentOne.cells;
    for (Cell cell : cells) {
        int x = cell.getCol() * CELL_SIZE;
        int y = cell.getRow() * CELL_SIZE;
        g.drawImage(cell.getImage(), x, y, null);
    }
}

private void paintWall(Graphics g) {
    for (int i = 0; i < wall.length; i++) {
        for (int j = 0; j < wall[i].length; j++) {
            int x = j * CELL_SIZE;
            int y = i * CELL_SIZE;
            Cell cell = wall[i][j];
            //判断是否有小方块
            if (cell == null) {
                g.drawRect(x, y, CELL_SIZE, CELL_SIZE);
            } else {
                g.drawImage(cell.getImage(), x, y, null);
            }
        }
    }
}

//判断是否出界
public boolean outOFBounds() {
    Cell[] cells = currentOne.cells;
    for (Cell cell : cells) {
        int col = cell.getCol();
        int row = cell.getRow();
        if (row < 0 || row > wall.length - 1 || col < 0 || col > wall[0].length-1) {
            return true;
        }
    }
    return false;
}

//按键一次,左移一次
public void moveleftActive() {
    currentOne.moveLeft();
    //判断是否越界或重合
    if (outOFBounds() || coincide()) {
        currentOne.moveRight();
    }
}

//按键一次,右移一次
public void moveRightActive() {
    currentOne.moveRight();
    //判断是否越界或重合
    if (outOFBounds() || coincide()) {
        currentOne.moveLeft();
    }
}

//判断是否重合
public boolean coincide() {
    Cell[] cells = currentOne.cells;
    for (Cell cell : cells) {
        int row = cell.getRow();
        int col = cell.getCol();
        if (wall[row][col] != null) {
            return true;
        }
    }
    return false;
}

public static void main(String[] args) {
    JFrame jFrame = new JFrame("俄罗斯方块");
    //创建游戏界面
    Tetris panel = new Tetris();
    jFrame.add(panel);
    //设置可见
    jFrame.setVisible(true);
    //设置窗口大小
    jFrame.setSize(810, 940);
    //设置剧中
    jFrame.setLocationRelativeTo(null);
    //设置窗口关闭时停止
    jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //游戏主要开始逻辑
    panel.start();
}

} 三、效果展示 游戏开始,方快下落,右边区域展示即将下落的方块图、分数、消除的行数以及游戏切换的状态。

按下空格键,方块瞬间下落, 按下P键游戏暂停,消除一行分数为1(此处由分数池进行控制)

按下C键游戏继续。

按下S键,游戏重新开始。

方块占满,游戏结束,此时可以按下S键重新开始游戏。