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 } }) };
}
}