Java实现俄罗斯方块

java实现俄罗斯方块

附上源码,有比较详细的注释

package com.Els;

/**
 * 集合类包
 */
import java.awt.image.BufferedImage;//BufferedImage类
import java.awt.event.*;
import java.awt.Font;//提供与字体相关的类和接口
import java.awt.Graphics;//画笔

/**
 * 扩展包
 */
import javax.imageio.ImageIO;
import javax.swing.*;

/**
 * 主类*********继承面板
 * 
 * @author HAO-影流之主
 */

public class Els extends JPanel {

	// serializable 类 Els 声明类型为 long 的静态终态 serialVersionUID 字段
	private static final long serialVersionUID = 1L;

	// 生成方块
	private Ter currentOne = Ter.randomOne();
	private Ter nextOne = Ter.randomOne();

	// 下降速度
	protected static int[] speeds = new int[] { 1000, 500, 250, 125 };
	String[] showSpeed = { "UP【简单】", "UP【一般】", "UP【困难】", "UP【起飞】" };
	public static final int EASY = 0;
	public static final int COMMEN = 1;
	public static final int TOUGH = 2;
	public static final int FLY = 3;
	protected static int speedIndex = 0;

	// 面板
	private static final int CELL_SIZE = 27;// 一个格子的大小
	private static final int HEIGHT = 20;
	private static final int WEIGHT = 10;
	private Cell[][] wall = new Cell[HEIGHT][WEIGHT];// 面板的方格

	// 分数池
	int[] scores_pool = { 0, 1, 2, 5, 10 };
	private int totalScore = 0;
	private int totalLine = 0;

	// 游戏的状态
	private int gamestate;
	String[] showState = { "P 【暂停】", "P 【继续】" };
	public static final int PLAYING = 0;
	public static final int PAUSE = 1;
	public static final int GAMEOVER = 2;

	// 申明一大堆图片的引用
	public static BufferedImage T;
	public static BufferedImage I;
	public static BufferedImage O;
	public static BufferedImage J;
	public static BufferedImage L;
	public static BufferedImage S;
	public static BufferedImage Z;
	public static BufferedImage background;
	public static BufferedImage gameover;

	// 让这些引用指向图片
	static {
		try {
			T = ImageIO.read(Els.class.getResource("T.png"));
			O = ImageIO.read(Els.class.getResource("O.png"));
			I = ImageIO.read(Els.class.getResource("I.png"));
			J = ImageIO.read(Els.class.getResource("J.png"));
			L = ImageIO.read(Els.class.getResource("L.png"));
			S = ImageIO.read(Els.class.getResource("S.png"));
			Z = ImageIO.read(Els.class.getResource("Z.png"));
			background = ImageIO.read(Els.class.getResource("background.jpg"));
			gameover = ImageIO.read(Els.class.getResource("gameover.png"));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 主方法
	 */
	public static void main(String[] args) {
		
		// 1.创建一个窗口对象
		JFrame frame = new JFrame("教主的俄罗斯方块");

		Els panel = new Els();// 用方块类型创建面板, 即界面
		frame.add(panel); // 将面板嵌入窗口

		// 2.设置为可见
		frame.setVisible(true);

		// 3.设置窗口的大小 600*600
		frame.setSize(580, 580);

		// 4.设置窗口居中
		frame.setLocationRelativeTo(null);

		// 5.设置窗口关闭方式
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// 6.运行游戏
		panel.game();
	}

	/**
	 * 游戏
	 */
	public void game() {
		gamestate = PLAYING;
		KeyListener listener = new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				int code = e.getKeyCode();

				/**
				 * 执行操作
				 */
				switch (code) {
				case KeyEvent.VK_P:// 暂停
					if (gamestate == PLAYING) {
						gamestate = PAUSE;
					} else if (gamestate == PAUSE) {
						gamestate = PLAYING;
					}
					break;
				case KeyEvent.VK_R:// 重新开始
					gamestate = PLAYING;
					wall = new Cell[20][10];
					currentOne = Ter.randomOne();
					nextOne = Ter.randomOne();
					totalScore = 0;
					totalLine = 0;
					break;
				case KeyEvent.VK_ESCAPE:
					System.exit(0);
				case KeyEvent.VK_UP:// 调整难度
					if (speedIndex == FLY) {// 处在最高难度
						speedIndex = 0;
						break;
					}
					if (speedIndex >= EASY && speedIndex <= FLY) {
						speedIndex++;
						break;
					}
				case KeyEvent.VK_W:// 旋转
					rotateRightAction();
					break;
				case KeyEvent.VK_S:// 下降
					softDropAction();
					break;
				case KeyEvent.VK_A:// 左移
					moveLeftAction();
					break;
				case KeyEvent.VK_D:// 右移
					moveRightAction();
					break;
				case KeyEvent.VK_SPACE:// 速降
					quickDropAction();
					break;
				}
				repaint();
			}
		};

		this.addKeyListener(listener);// 添加指定的按键侦听器,以接收发自此组件的按键事件

		this.requestFocus();// 把输入焦点放在调用这个方法的控件上。

		while (true) {
			try {
				Thread.sleep(speeds[speedIndex]);// 使线程挂起 若干毫秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			if (gamestate == PLAYING) {// 如果正在游戏
				if (canDrop() == true) {
					currentOne.softDrop();
				} else {
					landToWall();
					destroyLine();
					if (isGameOver() == true) {// 如果游戏结束
						gamestate = GAMEOVER;
					} else {
						currentOne = nextOne;
						nextOne = Ter.randomOne();
					}
				}
				repaint();// 刷新
			}
		}
	}

	/**
	 * 满行消除
	 */
	public void destroyLine() {
		int lines = 0;// 统计销毁行的行数

		Cell[] cells = currentOne.cells;// 让cells指向正在掉落的方块
		for (Cell c : cells) {
			int row = c.getRow();// 得到正在掉落的方块的行号
			while (row < 20) {
				if (isFullLine(row) == true) {// 如果满行
					lines++;// 销毁的行数+1

					wall[row] = new Cell[10];// 第row行用新的小方块组填充
					for (int i = row; i > 0; i--) {
						/**
						 * public static void arraycopy(Object src,int srcPos,Object dest,intdestPos,int
						 * length) 从destPos位置起 将src从 srcPos开始 按值传递 复制到 dest中
						 */
						// 将清空后的行的上一行的空慢状态赋值给清空的这一行
						System.arraycopy(wall[i - 1], 0, wall[i], 0, 10);// 向下平移一行
					}
					wall[0] = new Cell[10];
				}
				row++;
			}
		}
		// 从分数池中取出分数, 加入总分数
		totalScore += scores_pool[lines];
		totalLine += lines;
	}

	/**
	 * 绘制
	 */
	public void paint(Graphics g) {
		// 1.绘制背景
		g.drawImage(background, 0, 0, null);

		// 2.确定坐标原点的位置
		g.translate(0, 0);

		paintWall(g);// 绘制墙

		paintCurrentOne(g);// 绘制正在下落的四个方块

		paintNextOne(g);// 绘制下一个方块

		paintScore(g);// 绘制分数

		paintState(g);// 绘制状态
	}

	/**
	 * 绘制组:绘制墙
	 */
	public void paintWall(Graphics g) {
		for (int i = 0; i < HEIGHT; i++) {
			for (int j = 0; j < WEIGHT; j++) {
				int x = j * CELL_SIZE;
				int y = i * CELL_SIZE;
				Cell cell = wall[i][j];// 让cell指向面板格子的第 [i][j]个格子
				if (cell == null) {
					g.drawRect(x, y, CELL_SIZE, CELL_SIZE);// 在(x, y)位置绘制一个 CELL_SIZE*CELL_SIZE大小的矩形方格
				} else {
					g.drawImage(cell.getImage(), x, y, null);// 在(x, y)位置绘制指定图像中当前可用的图像
				}

			}
		}
	}

	/**
	 * 绘制组:绘制正在下落的方块
	 * 
	 * @param g
	 */
	public void paintCurrentOne(Graphics g) {
		// 获取currentOne对象的四个元素
		Cell[] cells = currentOne.cells;
		for (Cell c : cells) {
			int x = c.getCol() * CELL_SIZE;// 获取横坐标和列坐标
			int y = c.getRow() * CELL_SIZE;
			g.drawImage(c.getImage(), x, y, null);
		}
	}

	/**
	 * 绘制组:绘制将要下落的下一个方块
	 * 
	 * @param g
	 */
	public void paintNextOne(Graphics g) {
		// 绘制边框
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				g.drawRect(i * CELL_SIZE + 365, j * CELL_SIZE, CELL_SIZE, CELL_SIZE);
			}
		}
		Cell[] cells = nextOne.cells;
		for (Cell c : cells) {
			int x = c.getCol() * CELL_SIZE + 285;// 获取横坐标和列坐标
			int y = c.getRow() * CELL_SIZE + 26;
			g.drawImage(c.getImage(), x, y, null);
		}
	}

	/**
	 * 绘制组:绘制分数
	 * 
	 * @param g
	 */
	public void paintScore(Graphics g) {
		g.setFont(new Font("楷体", Font.BOLD, 20));// 设置字体大小为20号
		g.drawString("预览:", 285, 20);

		g.drawRect(280, 125, 275, 60);
		g.drawString("分数: " + totalScore, 285, 150);
		g.drawString("消行: " + totalLine, 285, 180);
		g.drawString("R    [新游戏]", 360, 270);
		g.drawString("SPACE[速降]", 360, 300);
		g.drawString("ESC  [退出游戏]", 360, 330);
		g.drawString("W[翻转]", 415, 370);

		g.drawRect(360, 380, 125, 55);
		g.drawString("A", 370, 400);
		g.drawString("S", 415, 430);
		g.drawString("D", 465, 400);
	}

	/**
	 * 绘制组:绘制状态
	 * 
	 * @param g
	 */
	public void paintState(Graphics g) {
		// 简单
		if (speedIndex == EASY) {
			g.drawString(showSpeed[EASY], 360, 210);
		}
		// 一般
		if (speedIndex == COMMEN) {
			g.drawString(showSpeed[COMMEN], 360, 210);
		}
		// 困难
		if (speedIndex == TOUGH) {
			g.drawString(showSpeed[TOUGH], 360, 210);
		}
		if (speedIndex == FLY) {
			g.drawString(showSpeed[FLY], 360, 210);
		}
		// 正在进行
		if (gamestate == PLAYING) {
			g.drawString(showState[PLAYING], 360, 240);
		}
		// 游戏暂停
		if (gamestate == PAUSE) {
			g.drawString(showState[PAUSE], 360, 240);
		}
		// 游戏结束
		if (gamestate == GAMEOVER) {
			g.drawImage(gameover, 0, 0, null);
		}
	}

	/**
	 * 监听事件组:向下
	 */
	public void softDropAction() {
		/**
		 * 判断能否继续下落,调用下落方法,每次按键调用后都要重新绘制,以保证画面流畅
		 */
		if (canDrop() == true) {// 可以下落
			currentOne.softDrop();
		} else {// 不可以下落
			landToWall();
			destroyLine();// 消除
			currentOne = nextOne;// 将当前的掉落对象的引用指向即将掉落的对象
			nextOne = Ter.randomOne();// 随机生成下一个
		}
	}

	/**
	 * 监听事件组:快速下落
	 */
	public void quickDropAction() {
		/**
		 * 多次无休眠调用方块下移
		 */
		for (;;) {
			if (canDrop() == true) {
				currentOne.softDrop();
			} else {
				break;
			}
		}
		landToWall();
		destroyLine();
		if (isGameOver() == true) {// 游戏结束
			gamestate = GAMEOVER;
		} else {
			currentOne = nextOne;
			nextOne = Ter.randomOne();
		}
	}

	/**
	 * 监听事件组:速降到墙
	 */
	public void landToWall() {
		Cell[] cells = currentOne.cells;
		for (Cell c : cells) {
			int row = c.getRow();
			int col = c.getCol();
			wall[row][col] = c;// 让面板填充为这个方块的小方块
		}
	}

	/**
	 * 监听事件组:向左
	 */
	public void moveLeftAction() {
		/**
		 * 如果大方块不越界,或不予其他方块重合, 就可向左移动
		 */
		currentOne.moveLeft();
		if (outOfBounds() == true || coincide() == true) {// 如果数组下标越界,则向右移回来
			currentOne.moveRight();
		}
	}

	/**
	 * 监听事件组: 向右
	 */
	public void moveRightAction() {
		currentOne.moveRight();
		if (outOfBounds() == true || coincide() == true) {
			currentOne.moveLeft();
		}
	}

	/**
	 * 监听事件组: 顺时针旋转
	 */
	public void rotateRightAction() {
		currentOne.rotateRight();
		if (outOfBounds() == true || coincide() == true) {
			currentOne.rotateLeft();// 向左转回来
		}
	}

	/**
	 * 判断组: 是否越界
	 */
	public boolean outOfBounds() {
		Cell[] cells = currentOne.cells;
		for (Cell c : cells) {
			int row = c.getRow();
			int col = c.getCol();
			if (col < 0 || col > 9 || row < 0 || row > 19) {// 越界
				return true;
			}
		}
		return false;
	}

	/**
	 * 判断组: 格子是否为空
	 */
	public boolean coincide() {
		Cell[] cells = currentOne.cells;
		for (Cell c : cells) {
			int row = c.getRow();
			int col = c.getCol();
			if (wall[row][col] != null) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 判断组:是否能下落
	 */
	public boolean canDrop() {
		Cell[] cells = currentOne.cells;
		for (Cell c : cells) {
			int row = c.getRow();
			int col = c.getCol();
			if (row == 19) {
				return false;
			}
			if (wall[row + 1][col] != null) {// 下一行不为空
				return false;
			}
		}
		return true;
	}

	/**
	 * 判断组:是否满行
	 */
	public boolean isFullLine(int row) {
		Cell[] lines = wall[row];
		for (Cell c : lines) {
			if (c == null) {
				return false;
			}
		}
		return true;
	}

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

}
package com.Els;

import java.awt.image.BufferedImage;//集合类包中的	BufferedImage 类

/**
 * 小方块
 * 
 * @author HAO-影流之主
 */
public class Cell {
	/**
	 * 俄罗斯方块中的最小单位的基本属性
	 */
	private BufferedImage image;// 图片
	private int row;// 行号
	private int col;// 列号

	/**
	 * 构造方法
	 */
	public Cell() {
	}

	public Cell(BufferedImage image, int row, int col) {
		super();// 子类调用父类的构造方法
		this.image = image;
		this.row = row;
		this.col = col;
	}

	/**
	 * 操作属性
	 */
	public BufferedImage getImage() {
		return image;
	}

	public void setImage(BufferedImage image) {
		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 void left() {
		col--;
	}

	/**
	 * 小方块向右移动
	 */
	public void right() {
		col++;
	}

	/**
	 * 小方块向下移动
	 */
	public void drop() {
		row++;
	}
}
package com.Els;

import java.util.Arrays;

/**
 * 7种不同方块的父类,由小方块Cell组成
 * 
 * @author HAO-影流之主
 */
public class Ter {
	/**
	 * 属性
	 */
	protected Cell[] cells = new Cell[4];// 所有的方块都是由最小的4个方块组成的

	protected State[] states;// 1~4种

	private int count = 10000;

	/**
	 * 方块行为:向左移动
	 */
	public void moveLeft() {
		// foreach语句 类型名 迭代变量:数组的引用名
		for (Cell c : cells) {
			c.left();// 每个小方块都向左移动
		}
	}

	/**
	 * 方块行为:向右移动
	 */
	public void moveRight() {
		for (Cell c : cells) {
			c.right();
		}
	}

	/**
	 * 方块行为:向下移动
	 */
	public void softDrop() {
		for (Cell c : cells) {
			c.drop();
		}
	}

	/**
	 * 方块行为:逆时针转
	 */
	public void rotateRight() {
		count++;
		State s = states[count % states.length];//对len求余, 会得到    0~len   之间的数

		Cell c = cells[0];
		int row = c.getRow();//得到第一个小方块的行、列坐标
		int col = c.getCol();
		// 以此为基础,以s的属性作为偏移量, 重设每一个小方块的行、列坐标
		for (int i = 1; i < 4; i++) {
			cells[i].setRow(row + s.rows[i]);
			cells[i].setCol(col + s.cols[i]);
		}
	}

	/**
	 * 方块行为:顺时针转
	 */
	public void rotateLeft() {
		count--;
		State s = states[count % states.length];

		Cell c = cells[0];
		int row = c.getRow();
		int col = c.getCol();
		for (int i = 1; i < 4; i++) {
			cells[i].setRow(row + s.rows[i]);
			cells[i].setCol(col + s.cols[i]);
		}
	}

	/**
	 * 随机生成一个4小方块
	 * 
	 * @return
	 */
	public static Ter randomOne() {
		Ter t = null;
		int num = (int) (Math.random() * 7);// 随机生成一个0~6的随机数来表示7种方块的序号

		// 让t指向随机生成的一个方块
		switch (num) {
		case 0:
			t = new T();
			break;
		case 1:
			t = new Z();
			break;
		case 2:
			t = new O();
			break;
		case 3:
			t = new I();
			break;
		case 4:
			t = new J();
			break;
		case 5:
			t = new L();
			break;
		case 6:
			t = new S();
			break;
		}
		return t;
	}

	@Override
	public String toString() {// 将cells按字符串形式返回
		return "[" + Arrays.toString(cells) + "]";
	}

}
package com.Els;

/**
 * 形态类
 * 
 * @author HAO-影流之主
 */
public class State {
	/**
	 * 设置八个属性
	 */
	int[] rows = new int[4];
	int[] cols = new int[4];

	/**
	 * 构造方法
	 */
	public State() {
	}

	public State(int[][] rowAndCol) {
		super();
		for (int i = 0; i < 4; i++) {
			this.rows[i] = rowAndCol[i][0];
			this.cols[i] = rowAndCol[i][1];
		}
	}
}
package com.Els;

/**
 * ■ ■ ■ ■
 * 
 * @author HAO-影流之主
 */
public class I extends Ter {
	public I() {
		// 左边空3格
		cells[0] = new Cell(Els.I, 0, 3);// 第(图片I,第0+1行, 第3+1个)个格子
		cells[1] = new Cell(Els.I, 0, 4);
		cells[2] = new Cell(Els.I, 0, 5);
		cells[3] = new Cell(Els.I, 0, 6);

		/**
		 * 构造State类型的对象数组
		 */
		states = new State[] { new State(new int[][] { { 0, 0 }, { 0, -1 }, { 0, 1 }, { 0, 2 } }),
				               new State(new int[][] { { 0, 0 }, { -1, 0 }, { 1, 0 }, { 2, 0 } }) };
	}
}
package com.Els;

/**
 * 0 ■ 0 ■ ■ ■
 * 
 * @author HAO-影流之主
 *
 */
public class J extends Ter {
	public J() {
		cells[0] = new Cell(Els.J, 0, 4);
		cells[1] = new Cell(Els.J, 0, 3);
		cells[2] = new Cell(Els.J, 0, 5);
		cells[3] = new Cell(Els.J, 1, 5);

		/**
		 * 构造State类型的对象数组
		 */
		states = new State[] { new State(new int[][] { { 0, 0 }, { 0, 1 }, { 0, -1 }, { -1, -1 } }),
				               new State(new int[][] { { 0, 0 }, { 1, 0 }, { -1, 0 }, { -1, 1 } }),
				               new State(new int[][] { { 0, 0 }, { 0, -1 }, { 0, 1 }, { 1, 1 } }),
				               new State(new int[][] { { 0, 0 }, { -1, 0 }, { 1, 0 }, { 1, -1 } }) };
	}
}
package com.Els;

/**
 * ■ 0 ■ 0 ■ ■
 * 
 * @author HAO-影流之主
 */
public class L extends Ter {
	public L() {
		cells[0] = new Cell(Els.T, 0, 4);
		cells[1] = new Cell(Els.T, 0, 3);
		cells[2] = new Cell(Els.T, 0, 5);
		cells[3] = new Cell(Els.T, 1, 3);

		/**
		 * 构造State类型的对象数组
		 */
		states = new State[] { new State(new int[][] { { 0, 0 }, { 0, 1 }, { 0, -1 }, { -1, 1 } }),
				               new State(new int[][] { { 0, 0 }, { 1, 0 }, { -1, 0 }, { 1, 1 } }),
				               new State(new int[][] { { 0, 0 }, { 0, -1 }, { 0, 1 }, { 1, -1 } }),
				               new State(new int[][] { { 0, 0 }, { -1, 0 }, { 1, 0 }, { -1, -1 } }) };
	}
}
package com.Els;

/**
 * ■ ■ ■ ■
 * 
 * @author HAO-影流之主
 */
public class O extends Ter {
	public O() {
		cells[0] = new Cell(Els.O, 0, 4);
		cells[1] = new Cell(Els.O, 0, 5);
		cells[2] = new Cell(Els.O, 1, 4);
		cells[3] = new Cell(Els.O, 1, 5);

		/**
		 * 构造State类型的对象数组
		 */
		states = new State[] { new State(new int[][] { { 0, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 } }) };
	}
}
package com.Els;

/**
 * 0 0 ■ ■ 0 ■ ■
 * 
 * @author HAO-影流之主
 */
public class S extends Ter {
	public S() {
		cells[0] = new Cell(Els.S, 0, 4);
		cells[1] = new Cell(Els.S, 0, 5);
		cells[2] = new Cell(Els.S, 1, 3);
		cells[3] = new Cell(Els.S, 1, 4);

		/**
		 * 构造State类型的对象数组
		 */
		states = new State[] { new State(new int[][] { { 0, 0 }, { 0, 1 }, { 1, -1 }, { 1, 0 } }),
				               new State(new int[][] { { 0, 0 }, { -1, 0 }, { 1, 1 }, { 0, 1 } }) };
	}
}
package com.Els;

/**
 * 0 ■ ■ ■ 0 0 ■ 0
 * 
 * @author HAO-影流之主
 */
public class T extends Ter {
	public T() {
		cells[0] = new Cell(Els.T, 0, 4);
		cells[1] = new Cell(Els.T, 0, 3);
		cells[2] = new Cell(Els.T, 0, 5);
		cells[3] = new Cell(Els.T, 1, 4);

		/**
		 * 构造State类型的对象数组
		 */
		states = new State[] { new State(new int[][] { { 0, 0 }, { 0, -1 }, { 0, 1 }, { 1, 0 } }),
				               new State(new int[][] { { 0, 0 }, { -1, 0 }, { 1, 0 }, { 0, -1 } }),
				               new State(new int[][] { { 0, 0 }, { 0, 1 }, { 0, -1 }, { -1, 0 } }),
				               new State(new int[][] { { 0, 0 }, { 1, 0 }, { -1, 0 }, { 0, 1 } }) };
	}
}
package com.Els;

/**
 * 0 ■ ■ 0 0 0 ■ ■
 * 
 * @author HAO-影流之主
 */
public class Z extends Ter {
	public Z() {
		cells[0] = new Cell(Els.Z, 1, 4);
		cells[1] = new Cell(Els.Z, 0, 3);
		cells[2] = new Cell(Els.Z, 0, 4);
		cells[3] = new Cell(Els.Z, 1, 5);

		/**
		 * 让states指向 State类型的对象数组
		 */
		states = new State[] { new State(new int[][] { { 0, 0 }, { -1, -1 }, { -1, 0 }, { 0, 1 } }),
				               new State(new int[][] { { 0, 0 }, { -1, 1 }, { 0, 1 }, { 1, 0 } }) };
	}

}

Java实现俄罗斯方块