Cell
package com.tetris;
import java.awt.image.BufferedImage;
//格子
public class Cell {
private int row;
private int col;
private BufferedImage image;
public Cell(int row, int col, BufferedImage image) {
super();
this.row = row;
this.col = col;
this.image = image;
}
public void drop() {
row++;
}
public void moveLeft() {
col--;
}
public void moveRight() {
col++;
}
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;
}
public int getRow() {
return row;
}
public void setRow(int row) {this.row = row;}
public String toString() {
return row + "," + col;
}
}
Tertis
package com.tetris;
/**
* coincide():判断当前方块是否与墙壁上的方块重合。
* moveLeftAction():尝试将当前方块向左移动一格,如果超出边界或与墙壁上的方块重合,则向右移动一格。
* outOfBounds():检查当前方块是否超出边界。
* softDropAction():如果可以下落,则让当前方块下落一格,否则将其固定在墙上,检查并销毁满行,并检查游戏是否结束,然后更新当前方块为下一个方块。
* hardDropAction():让当前方块尽可能多地向下移动,然后将其固定在墙上,检查并销毁满行,并检查游戏是否结束,然后更新当前方块为下一个方块。
* destroyLines():检查并销毁满行,更新得分。
* fullCells(int row):检查指定行是否全部被方块占据。
* removeLine(int row):移除指定行的方块,将上方的方块下移一行。
* landToWall():将当前方块固定在墙上。
* canDrop():检查当前方块是否可以下落。
* rotateRightAction():尝试将当前方块顺时针旋转90度,如果超出边界或与墙壁上的方块重合,则逆时针旋转90度。
* rotateLeftAction():尝试将当前方块逆时针旋转90度,如果超出边界或与墙壁上的方块重合,则顺时针旋转90度。
* startAction():开始游戏,重置游戏状态,初始化当前方块和下一个方块,设置定时器每700毫秒触发一次软下降动作。
* clearWall():清除墙上的所有方块。
* pauseAction():暂停游戏,停止定时器。
* continueAction():继续游戏,重新开始定时器每700毫秒触发一次软下降动作。
* checkGameOver():检查游戏是否结束,如果墙的最下方有方块,则游戏结束。
* main(String[] args):游戏主方法,创建游戏窗口和游戏实例,并开始游戏。
*/
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
//俄罗斯方块类
public class Tetris extends JPanel {
// 分数
private int score;
//行数
private int lines;
public static final int ROWS = 20;//行数
public static final int COLS = 10;//列数
// 方块墙, 墙是Tetris的组件
private Cell[][] wall = new Cell[ROWS][COLS];
// 正在下落的方块
private Tetromino tetromino;
//下一个下落的方块
private Tetromino nextOne;
// 图片素材的装载
public static BufferedImage background;
public static BufferedImage gameOver;
public static BufferedImage I;
public static BufferedImage T;
public static BufferedImage S;
public static BufferedImage J;
public static BufferedImage L;
public static BufferedImage O;
public static BufferedImage Z;
// 加载静态的图片资源
static {
try {
I = ImageIO.read(Tetris.class.getClassLoader().getResourceAsStream("I.png"));
J = ImageIO.read(Tetris.class.getClassLoader().getResourceAsStream("J.png"));
L = ImageIO.read(Tetris.class.getClassLoader().getResourceAsStream("L.png"));
O = ImageIO.read(Tetris.class.getClassLoader().getResourceAsStream("O.png"));
S = ImageIO.read(Tetris.class.getClassLoader().getResourceAsStream("S.png"));
T = ImageIO.read(Tetris.class.getClassLoader().getResourceAsStream("T.png"));
Z = ImageIO.read(Tetris.class.getClassLoader().getResourceAsStream("Z.png"));
background = ImageIO.read(Tetris.class.getClassLoader().getResourceAsStream("background.png"));
BufferedImage gameover = ImageIO.read(Tetris.class.getClassLoader().getResourceAsStream("gameover.png"));
} catch (Exception e) {
e.printStackTrace();
}
}
//重绘
public void paint(Graphics g) {
g.drawImage(background, 0, 0, null);
//translate 可以平移绘制坐标系
g.translate(15, 15);
paintWall(g);//画墙
paintTetromino(g);//画正在下落的方块
paintNextOne(g);//画下一个方块
paintScore(g);//画分数
g.translate(-15, -15);
if (finish) {
g.drawImage(gameOver, 0, 0, null);
}
}
public static final int FONT_COLOR = 0x667799;
public static final int FONT_SIZE = 30;//字号
private void paintScore(Graphics g) {
int x = 290;
int y = 162;
g.setColor(new Color(FONT_COLOR));
//font 字体
Font font = g.getFont();//获取当前画笔的默认字体
//用新字号创建字体
font = new Font(font.getName(), Font.BOLD, FONT_SIZE);
g.setFont(font);
g.drawString("SCORE:" + score, x, y);
y += 56;
g.drawString("LINES:" + lines, x, y);
y += 56;
String str = "[P]Pause";
if (pause) {
str = "[C]Continue";
}
if (finish) {
str = "[S]Start!";
}
g.drawString(str, x, y);
}
private void paintNextOne(Graphics g) {
if (nextOne == null) {
return;
}
Cell[] cells = nextOne.cells;
for (int i = 0; i < cells.length; i++) {
Cell cell = cells[i];
int x = CELL_SIZE * (cell.getCol() + 10);
int y = CELL_SIZE * (cell.getRow() + 1);
g.drawImage(cell.getImage(), x - 1, y - 1, null);
}
}
private void paintTetromino(Graphics g) {
//System.out.println(this.tetromino);
if (tetromino == null) {
return;
}
Cell[] cells = tetromino.cells;
for (int i = 0; i < cells.length; i++) {
Cell cell = cells[i];
int x = CELL_SIZE * cell.getCol();
int y = CELL_SIZE * cell.getRow();
g.drawImage(cell.getImage(), x - 1, y - 1, null);
}
}
public static final int CELL_SIZE = 26;
/* 将wall(二维数组)的内容绘制到面板上
* 每个格子(迭代)都绘制出来.
* 如果: 没有方块的地方绘制空白格
* 有方块(Cell)的地方, 绘制方块的贴图
*/
private void paintWall(Graphics g) {
for (int row = 0; row < wall.length; row++) {
Cell[] line = wall[row];
for (int col = 0; col < line.length; col++) {
Cell cell = line[col];
int x = col * CELL_SIZE;
int y = row * CELL_SIZE;
if (cell == null) {
g.setColor(new Color(0x0FFFFFF, true));
//drawRect 绘制矩形格子
g.drawRect(x, y, CELL_SIZE, CELL_SIZE);
} else {
g.drawImage(cell.getImage(), x - 1, y - 1, null);
}
}
}
}
// Tetris 类中 添加方法 action() 用于 启动软件
public void action() {
startAction();//启动游戏
KeyListener l = new KeyAdapter() {
//在按键按下时候执行
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (finish) {
if (key == KeyEvent.VK_S) {
startAction();
repaint();
}
return;
}
if (pause) {
if (key == KeyEvent.VK_C) {
continueAction();
repaint();
}
return;
}
switch (key) {
case KeyEvent.VK_P:
pauseAction();
break;
case KeyEvent.VK_DOWN:
softDropAction();
break;
case KeyEvent.VK_RIGHT:
moveRightAction();
break;
case KeyEvent.VK_LEFT:
moveLeftAction();
break;
case KeyEvent.VK_SPACE:
hardDropAction();
break;
case KeyEvent.VK_UP:
rotateRightAction();
break;
case KeyEvent.VK_Z:
rotateLeftAction();
break;
}
repaint();
}
};
this.addKeyListener(l);//在当前面板上绑定了键盘监听l
this.requestFocus();//为当前面板请求键盘输入焦点
}
// 在Tetris类中添加 向右移动流程控制方法
public void moveRightAction() {
tetromino.moveRight();
if (outOfBounds() || coincide()) {//coincide重合
tetromino.moveLeft();
}
}
// 检查正在下落的方块是否与墙上的砖块重合
private boolean coincide() {
Cell[] cells = tetromino.cells;
for (int i = 0; i < cells.length; i++) {
Cell cell = cells[i];
int row = cell.getRow();
int col = cell.getCol();
if (row >= 0 && row < ROWS && col >= 0 && col < COLS &&
wall[row][col] != null) {
//重合
return true;
}
}
return false;
}
public void moveLeftAction() {
tetromino.moveLeft();
if (outOfBounds() || coincide()) {
tetromino.moveRight();
}
}
// Bound 边界, 检查是否出界的算法
private boolean outOfBounds() {
Cell[] cells = tetromino.cells;
for (int i = 0; i < cells.length; i++) {
Cell cell = cells[i];
int col = cell.getCol();
if (col < 0 || col >= COLS) {
return true;
}
}
return false;
}
public void softDropAction() {
if (canDrop()) {// 能下落吗?
tetromino.softDrop();
} else {
landToWall();// 四格方块着陆到墙上
destroyLines();// 销毁满行
checkGameOver();// 检查游戏是否结束
tetromino = nextOne;
nextOne = Tetromino.randomOne();
}
}
public void hardDropAction() {
while (canDrop()) {// 能下落吗?
tetromino.softDrop();
}
landToWall();// 四格方块着陆到墙上
destroyLines();// 销毁满行
checkGameOver();// 检查游戏是否结束
tetromino = nextOne;
nextOne = Tetromino.randomOne();
}
public static final int[] SCORE_TABLE = {0, 1, 10, 50, 100};
private void destroyLines() {
int lines = 0;
for (int row = 0; row < wall.length; row++) {
if (fullCells(row)) {
removeLine(row);
lines++;
}
}
this.lines += lines;
this.score += SCORE_TABLE[lines];
}
private boolean fullCells(int row) {
Cell[] line = wall[row];
for (Cell cell : line) {
if (cell == null) {
return false;
}
}
return true;
}
private void removeLine(int row) {
for (int i = row; i >= 1; i--) {
//cp [i-1] -> [i]
System.arraycopy(wall[i - 1], 0, wall[i], 0, COLS);
}
Arrays.fill(wall[0], null);
}
private void landToWall() {
Cell[] cells = tetromino.cells;
for (Cell cell : cells) {
int row = cell.getRow();
int col = cell.getCol();
wall[row][col] = cell;
}
}
private boolean canDrop() {
Cell[] cells = tetromino.cells;
for (int i = 0; i < cells.length; i++) {
Cell cell = cells[i];
int row = cell.getRow();
if (row == ROWS - 1) {
return false;
}
}
for (Cell cell : cells) {//增强版foreach循环
int row = cell.getRow();
int col = cell.getCol();
if (wall[row + 1][col] != null) {
return false;
}
}
return true;
}
// Tetris 类中的 rotateRightAction() 旋转流程控制
public void rotateRightAction() {
tetromino.rotateRight();
if (outOfBounds() || coincide()) {
tetromino.rotateLeft();
}
}
public void rotateLeftAction() {
tetromino.rotateLeft();
if (outOfBounds() || coincide()) {
tetromino.rotateRight();
}
}
// 为了配合 定时事件,
// 还修改了键盘事件监听action()
//以及界面绘制方法 paint() paintScrore()
// Tetris 类中 增加2个属性, 和一个Timer实例
private boolean pause;//暂停
private boolean finish;//结束
private Timer timer;//下落定时器
// 开始流程
public void startAction() {
clearWall();
pause = false;
finish = false;
tetromino = Tetromino.randomOne();
nextOne = Tetromino.randomOne();
lines = 0;
score = 0;
timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
softDropAction();
repaint();
}
}, 700, 700);
}
private void clearWall() {
for (Cell[] line : wall) {
Arrays.fill(line, null);
}
}
public void pauseAction() {
pause = true;
timer.cancel();//停止定时器上的一切任务!
}
public void continueAction() {
pause = false;
timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
softDropAction();
repaint();
}
}, 700, 700);
}
private void checkGameOver() {
if (wall[0][4] != null) {//游戏该结束了!
finish = true;
timer.cancel();
repaint();
}
}
//Tetris 类中的main方法, 用于初始化界面
public static void main(String[] args) {
JFrame frame = new JFrame("俄罗斯方块");
frame.setSize(525, 580);
Tetris tetris = new Tetris();//继承JPanel
//Background 背景
tetris.setBackground(new Color(0xffff00));
frame.add(tetris);//添加俄罗斯方块游戏到窗口框中
//Default默认 Close关闭 Operation操作
//当关闭窗口时候, 将Java软件也结束
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);//会尽快调用paint()
tetris.action();
}
}
Teromino
package com.tetris;
import java.util.Arrays;
import java.util.Random;
//四格方块
public abstract class Tetromino {
protected Cell[] cells = new Cell[4];
//旋转状态
protected State[] states;
//获取旋转状态序号
protected int index = 10000;
protected class State {
int row0, col0, row1, col1, row2, col2, row3, col3;
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 void rotateRight() {
// 1) 获得当前的轴
// 2) 获得下个状态的变化数据
// 3) 利用+法实现数据变化
//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);
Cell o = cells[0];//t0=[3,4][3,3][3,5][4,4]
int row = o.getRow();//3
int col = o.getCol();//4
index++;//10001
// 10001 % 4 = 1
State s = states[index % states.length];//
//s = (0,0,-1,0,1,0,0,-1)
//t1=[3,4][2,4][4,4][3,3]
cells[1].setRow(row + s.row1);//3+-1 = 2->cells[1].row
cells[1].setCol(col + s.col1);//4+0 = 4->cells[1].col
cells[2].setRow(row + s.row2);//3+1 = 4->cells[2].row
cells[2].setCol(col + s.col2);//4+0 = 4->cells[2].col
cells[3].setRow(row + s.row3);//3+0 = 3->cells[2].row
cells[3].setCol(col + s.col3);//4+-1 = 3->cells[2].col
}
//向右转
public void rotateLeft() {
Cell o = cells[0];//t0=[3,4][3,3][3,5][4,4]
int row = o.getRow();//3
int col = o.getCol();//4
index--;//10000
State s = states[index % states.length];//
cells[1].setRow(row + s.row1);//3+-1 = 2->cells[1].row
cells[1].setCol(col + s.col1);//4+0 = 4->cells[1].col
cells[2].setRow(row + s.row2);//3+1 = 4->cells[2].row
cells[2].setCol(col + s.col2);//4+0 = 4->cells[2].col
cells[3].setRow(row + s.row3);//3+0 = 3->cells[2].row
cells[3].setCol(col + s.col3);//4+-1 = 3->cells[2].col
}
//私有构造器
private Tetromino() {
}
//下落一步
public void softDrop() {
for (int i = 0; i < cells.length; i++) {
cells[i].drop();
}
}
public void moveLeft() {
for (int i = 0; i < cells.length; i++) {
cells[i].moveLeft();
}
}
public void moveRight() {
for (int i = 0; i < cells.length; i++) {
cells[i].moveRight();
}
}
public String toString() {
return Arrays.toString(cells);
}
//只能通过这个工厂方法创建 4格方块的实例
public static Tetromino randomOne() {
Random r = new Random();
int type = r.nextInt(7);
switch (type) {
case 0:
return new T();//静态方法只能使用静态内部类
case 1:
return new S();
case 2:
return new Z();
case 3:
return new L();
case 4:
return new J();
case 5:
return new O();
case 6:
return new I();
}
return null;
}
// 利用内部类封装了子类实现的细节
private static 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);
}
}
private static 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[]{new State(0, 0, 0, 1, 0, -1, 0, -2),
new State(0, 0, -1, 0, 1, 0, 2, 0)};
}
}
private static 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[]{
new State(0, 0, 0, 1, 1, -1, 1, 0),
new State(0, 0, -1, 0, 1, 1, 0, 1)};
}
}
private static 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[]{
new State(0, 0, -1, -1, -1, 0, 0, 1),
new State(0, 0, -1, 1, 0, 1, 1, 0)};
}
}
private static 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[]{
new State(0, 0, 0, -1, 0, 1, 1, -1),
new State(0, 0, -1, 0, 1, 0, -1, -1),
new State(0, 0, 0, 1, 0, -1, -1, 1),
new State(0, 0, 1, 0, -1, 0, 1, 1)};
}
}
private static 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[]{
new State(0, 0, 0, -1, 0, 1, 1, 1),
new State(0, 0, -1, 0, 1, 0, 1, -1),
new State(0, 0, 0, 1, 0, -1, -1, -1),
new State(0, 0, 1, 0, -1, 0, -1, 1)};
}
}
private static 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[]{
new State(0, 0, 0, 1, 1, 0, 1, 1),
new State(0, 0, 0, 1, 1, 0, 1, 1)};
}
}
}