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

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


大家好,我是百思不得小赵。

创作时间:2022 年 5 月 12 日
博客主页: 🔍点此进入博客主页
—— 新时代的农民工 🙊
—— 换一种思维逻辑去看待这个世界 👀
今天是加入CSDN的第1167天。觉得有帮助麻烦👏点赞、🍀评论、❤️收藏


目录

一、游戏背景

二、功能实现

三、效果展示


一、游戏背景

俄罗斯方块是俄罗斯人发明的。这人叫阿列克谢·帕基特诺夫(Алексей Пажитнов 英文: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  wall.length - 1 || 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键重新开始游戏。

本次游戏中所使用的素材文件以及所有的源代码文件都已经同步到Github,小伙伴们点击下方链接直接获取。

Github链接地址:点击获取完整源代码,下载到本地即可运行