一、先看一下游戏运行时的画面
二、代码部分
1. Cell.java
Cell.java:
package demo1;
import java.awt.image.BufferedImage;
import java.util.Objects;
/*
编写小方块类
属性:行、列、每个小方格的图片
方法:左移一格、右移一格、下落一格
编写小方格实体类
*/
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 == null || getClass() != o.getClass()) return false;
Cell cell = (Cell) o;
return row == cell.row &&
col == cell.col &&
Objects.equals(image, cell.image);
}
@Override
public int hashCode() {
return Objects.hash(row, col, image);
}
//编写左移一格方法
public void left(){
col--;
}
//编写右移一格方法
public void right(){
col++;
}
//编写下落一格方法
public void drop(){
row++;
}
}
2. I.java
I.java:
package demo1;
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);
}
}
3. J.java
J.java:
package demo1;
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);
}
}
4. L.java
L.java:
package demo1;
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);
}
}
5. O.java
O.java:
package demo1;
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];
}
}
6. S.java
S.java:
package demo1;
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);
}
}
7. T.java
T.java:
package demo1;
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);
}
}
8. Tetris.java
Tetris.java:
package demo1;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
//编写俄罗斯方块主类
public class Tetris extends JPanel {
//声明正在下落的方块
private Tetromino currentOne = Tetromino.randomOne();
//声明将要下落的方块
private Tetromino nextOne = Tetromino.randomOne();
//声明游戏主区域
private Cell[][] wall = new Cell[18][9];
//声明单元格的值为48像素
private static final int CELL_SIZE = 40;
//声明游戏分数池
int[] scores_pool = {0,1,2,5,10};
//声明当前获得游戏的分数
private int totalScore = 0;
//当前已消除的行数
private int totalLine = 0;
//声明游戏的三种状态,分别是:游戏中、暂停、游戏结束
public static final int PLAYING = 0;
public static final int PAUSE = 1;
public static final int GAMEOVER = 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 backImage;
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"));
backImage = ImageIO.read(new File("images/background.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void paint(Graphics g) {
g.drawImage(backImage, 0, 0, null);
//平移坐标轴
g.translate(22, 15);
//绘制游戏主区域
paintWall(g);
//绘制正在下落的四方格
paintCurrentOne(g);
//绘制下一个将要下落的四方格
paintNextOne(g);
//绘制游戏得分
paintScore(g);
//绘制游戏当前状态
paintState(g);
}
public void start(){
game_state = PLAYING;
KeyListener l = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
switch (code){
case KeyEvent.VK_DOWN:
sortDropAction(); //下落一格
break;
case KeyEvent.VK_LEFT:
moveLeftAction(); //左移
break;
case KeyEvent.VK_RIGHT:
moveRightAction(); //右移
break;
case KeyEvent.VK_UP:
rotateRightAction(); //顺时针旋转
break;
case KeyEvent.VK_SPACE:
handDropAction(); //瞬间下落
break;
case KeyEvent.VK_P:
//判断当前游戏的状态
if(game_state == PLAYING){
game_state = PAUSE;
}
break;
case KeyEvent.VK_C:
//判断游戏状态
if(game_state == PAUSE){
game_state = PLAYING;
}
break;
case KeyEvent.VK_S:
//表示游戏重新开始
game_state = PLAYING;
wall = new Cell[18][9];
currentOne = Tetromino.randomOne();
nextOne = Tetromino.randomOne();
totalScore = 0;
totalLine = 0;
break;
}
}
};
//将俄罗斯方块窗口设置为焦点
this.addKeyListener(l);
this.requestFocus();
while(true){
//判断,当前游戏状态在游戏中时,每隔0.5秒下落
if(game_state == PLAYING){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//判断能否下落
if(canDrop()){
currentOne.softDrop();
}else{
//嵌入到墙中
landToWall();
//判断能否消行
destroyLine();
//判断游戏是否结束
if(isGameOver()){
game_state = GAMEOVER;
}else{
currentOne = nextOne;
nextOne = Tetromino.randomOne();
}
}
}
repaint();
}
}
//创建顺时针旋转
public void rotateRightAction(){
currentOne.rotateRight();
//判断是否越界或者是否重合
if(outOfBounds() || coincide()){
currentOne.rotateLeft();
}
}
//瞬间下落
public void handDropAction(){
while(true){
//判断四方格能否下落
if(canDrop()){
currentOne.softDrop();
}else{
break;
}
}
//嵌入到墙中
landToWall();
//判断能否消行
destroyLine();
//判断游戏是否结束
if(isGameOver()){
game_state = GAMEOVER;
}else{
//游戏没有结束,继续生成新的四方格
currentOne = nextOne;
nextOne = Tetromino.randomOne();
}
}
//按键一次四方格下落一格
public void sortDropAction(){
//判断能否下落
if(canDrop()){
//当前四方格下落一格
currentOne.softDrop();
}else{
//将四方格嵌入到墙中
landToWall();
//判断能否消行
destroyLine();
//判断游戏是否结束
if(isGameOver()){
game_state = GAMEOVER;
}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 canDrop(){
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 == PLAYING){
g.drawString(show_state[0],500,660);
}else if(game_state == PAUSE){
g.drawString(show_state[1],500,660);
}else if(game_state == GAMEOVER){
g.drawString(show_state[2],500,660);
g.setColor(Color.red);
g.setFont(new Font(Font.SANS_SERIF, Font.BOLD,60));
g.drawString("GAMEOVER!", 30, 400);
}
}
//绘制游戏得分
private void paintScore(Graphics g) {
g.setFont(new Font(Font.SANS_SERIF, Font.BOLD,30));
g.drawString("SCORES: " + totalScore, 500, 248);
g.drawString("LINES: " + 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, CELL_SIZE, CELL_SIZE, 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, CELL_SIZE, CELL_SIZE,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 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 void moveLeftAction(){
currentOne.moveLeft();
//判断是否越界或者四方格是否重合
if(outOfBounds() || coincide()){
currentOne.moveRight();
}
}
//按键一次四方格右移一次
public void moveRightAction(){
currentOne.moveRight();
//判断是否越界
if(outOfBounds() || coincide()){
currentOne.moveLeft();
}
}
public static void main(String[] args) {
//创建一个窗口对象
JFrame frame = new JFrame("俄罗斯方块");
//创建游戏界面,也就是面板
Tetris panel = new Tetris();
//将面板嵌入到窗口中
frame.add(panel);
//设置可见
frame.setVisible(true);
//设置窗口尺寸
frame.setSize(810, 940);
//设置窗口居中
frame.setLocationRelativeTo(null);
//设置窗口关闭时程序终止
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//游戏主要逻辑封装在方法中
panel.start();
}
}
9. Tetromino.java
Tetromino.java:
package demo1;
/*
编写四方格父类
属性:Cell数组用于创建4个小方块
方法:左移一格、右移一格、下落一格、变形(暂时不考虑)
*/
public class Tetromino {
protected Cell[] cells = new Cell[4];
//编写旋转状态
protected State[] states;
//声明旋转次数
protected int count = 10000;
//编写顺时针旋转四方格方法
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(){
//旋转次数减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);
}
//编写四方格旋转状态的内部类
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 +
'}';
}
}
//编写左移方法
public void moveLeft(){
for (Cell cell : cells) {
cell.left();
}
}
//编写右移方法
public void moveRight(){
for (Cell cell : cells) {
cell.right();
}
}
//编写下落方法
public void softDrop(){
for (Cell cell : cells) {
cell.drop();
}
}
//编写随机生成四方格的方法
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;
}
}
10. Z.java
Z.java:
package demo1;
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);
}
}
总结
↑:改变方位
←:向左移动
→:向右移动
↓:快速向下移动
空格:直接移动到最下面
P:暂停
C:开始