《俄罗斯方块》是一款由俄罗斯人阿列克谢·帕基特诺夫于1984年6月发明的经典休闲游戏,其基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。
下面介绍用Java开发《俄罗斯方块》。
创建一个普通Java项目,按如下结构创建类以及导入图片资源:
主要代码如下:
1、单个小方块
/**
* 格子
*/
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 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;
}
/**
* 往下落
*/
public void softDrop() {
row++;
}
/**
* 往左移
*/
public void moveLeft() {
col--;
}
/**往右移
*
*/
public void moveRight() {
col++;
}
}
2、四个小方块组合
/**
* 表示4块格子
*/
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;
}
}
/**
* 随机产生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 I();
case 2:return new S();
case 3:return new Z();
case 4:return new O();
case 5:return new L();
case 6:return new J();
default:return null;
}
}
/**
* 下落
*/
public void softDrop() {
for (int i = 0; i < cells.length; i++) {
cells[i].softDrop();
}
}
/**
* 左移
*/
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 void rotateRight(){
index++;
State s = states[index%states.length];
Cell o = this.cells[0];
int row = o.getRow();
int col = o.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(){
index--;
State s = states[index%states.length];
Cell o = this.cells[0];
int row = o.getRow();
int col = o.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);
}
}
/**
* T形方块
*/
class T extends Tetromino {
public T() {
cells[0] = new Cell(0, 4, TetrisGame.T);
cells[1] = new Cell(0, 3, TetrisGame.T);
cells[2] = new Cell(0, 5, TetrisGame.T);
cells[3] = new Cell(1, 4, TetrisGame.T);
states = new State[4];
states[0]=new State(0,0,0,-1,0,1,1,0);//S0
states[1]=new State(0,0,-1,0,1,0,0,-1);//S1
states[2]=new State(0,0,0,1,0,-1,-1,0);//S2
states[3]=new State(0,0,1,0,-1,0,0,1);//S3
}
}
/**
* I形方块
*/
class I extends Tetromino {
public I() {
cells[0] = new Cell(0, 4, TetrisGame.I);
cells[1] = new Cell(0, 3, TetrisGame.I);
cells[2] = new Cell(0, 5, TetrisGame.I);
cells[3] = new Cell(0, 6, TetrisGame.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);
}
}
/**
* S形方块
*/
class S extends Tetromino {
public S() {
cells[0] = new Cell(1, 4, TetrisGame.S);
cells[1] = new Cell(1, 3, TetrisGame.S);
cells[2] = new Cell(0, 4, TetrisGame.S);
cells[3] = new Cell(0, 5, TetrisGame.S);
states = new State[2];
states[0] = new State(0,0,0,-1,-1,0,-1,1);
states[1] = new State(0,0,-1,0,0,1,1,1);
}
}
/**
* Z形方块
*/
class Z extends Tetromino {
public Z() {
cells[0] = new Cell(1, 4, TetrisGame.Z);
cells[1] = new Cell(0, 3, TetrisGame.Z);
cells[2] = new Cell(0, 4, TetrisGame.Z);
cells[3] = new Cell(1, 5, TetrisGame.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);
}
}
/**
* O形方块
*/
class O extends Tetromino {
public O() {
cells[0] = new Cell(0, 4, TetrisGame.O);
cells[1] = new Cell(0, 5, TetrisGame.O);
cells[2] = new Cell(1, 4, TetrisGame.O);
cells[3] = new Cell(1, 5, TetrisGame.O);
states = new State[2];
states[0] = new State(0,0,0,1,1,0,1,1);
states[1] = new State(0,0,0,1,1,0,1,1);
}
}
/**
* L形方块
*/
class L extends Tetromino {
public L() {
cells[0] = new Cell(0, 4, TetrisGame.L);
cells[1] = new Cell(0, 3, TetrisGame.L);
cells[2] = new Cell(0, 5, TetrisGame.L);
cells[3] = new Cell(1, 3, TetrisGame.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);
}
}
/**
* J形方块
*/
class J extends Tetromino {
public J() {
cells[0] = new Cell(0, 4, TetrisGame.J);
cells[1] = new Cell(0, 3, TetrisGame.J);
cells[2] = new Cell(0, 5, TetrisGame.J);
cells[3] = new Cell(1, 5, TetrisGame.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);
}
}
3、游戏主类
/**
* 俄罗斯方块
*/
public class TetrisGame extends JPanel {
//背景图片
private static BufferedImage background;
//暂停图片
private static BufferedImage pause;
//游戏结束图片
private static BufferedImage gameover;
public static BufferedImage T;
public static BufferedImage S;
public static BufferedImage I;
public static BufferedImage L;
public static BufferedImage J;
public static BufferedImage O;
public static BufferedImage Z;
private static int[] scoreTable = {0, 10, 50, 80, 200};//计分板
public static final int ROWS = 20;//背景墙行数
public static final int COLS = 10;//背景墙列数
public static final int CELL_SIZE = 26;//格子大小
public static final int FONT_COLOR = 0x667799;//字体颜色
public static final int FONT_SIZE = 24;//字体大小
public static final int RUNNING = 0;//运行
public static final int PAUSE = 1;//暂停
public static final int GAME_OVER = 2;//游戏结束
static {
try {
background = ImageIO.read(TetrisGame.class.getResource("/tetris.png"));
pause = ImageIO.read(TetrisGame.class.getResource("/pause.png"));
gameover = ImageIO.read(TetrisGame.class.getResource("/gameover.png"));
T = ImageIO.read(TetrisGame.class.getResource("/T.png"));
S = ImageIO.read(TetrisGame.class.getResource("/S.png"));
I = ImageIO.read(TetrisGame.class.getResource("/I.png"));
L = ImageIO.read(TetrisGame.class.getResource("/L.png"));
J = ImageIO.read(TetrisGame.class.getResource("/J.png"));
O = ImageIO.read(TetrisGame.class.getResource("/O.png"));
Z = ImageIO.read(TetrisGame.class.getResource("/Z.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
private int score;//分数
private int lines;//销毁的行数
private Cell[][] wall;//背景墙
private Tetromino tetromino;//正在下落的四块方格
private Tetromino nextOne;//下一个四格方块
private Timer timer;//定时器
private int intervel = 10;//下落时间间隔10毫秒
private int state;//游戏状态
private int speed;//下落速度
private int level;//难度级别
private int index;//下落计数器,当index % speed == 0时候下落一次
@Override
public void paint(Graphics g) {
g.drawImage(background, 0, 0, null);//画背景
g.translate(15, 15);//坐标系平移
paintWall(g);//画墙
paintTetromino(g);//画正在下落的四块方格
paintNextOne(g);//画下一个要下落的四块方块
paintScore(g);//绘制分数
paintState(g);//画游戏状态
}
/**
* 画墙
*
* @param g 画笔
*/
private void paintWall(Graphics g) {
for (int row = 0; row < wall.length; row++) {
Cell[] line = wall[row];//每一行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.drawRect(x, y, CELL_SIZE, CELL_SIZE);//空白部分绘制边框
} else {
g.drawImage(cell.getImage(), x, y, null);
}
}
}
}
/**
* 画正在下落的方块
*
* @param g 画笔
*/
private void paintTetromino(Graphics g) {
if (tetromino == null) {
return;
}
Cell[] cells = tetromino.cells;
for (int i = 0; i < cells.length; i++) {
//i = 1,2,3,4
Cell cell = cells[i];
int x = cell.getCol() * CELL_SIZE;
int y = cell.getRow() * CELL_SIZE;
g.drawImage(cell.getImage(), x, y, null);
}
}
/**
* 画下一个要下落的方块
*
* @param g 画笔
*/
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.getCol() + 10) * CELL_SIZE;
int y = (cell.getRow() + 1) * CELL_SIZE;
g.drawImage(cell.getImage(), x, y, null);
}
}
/**
* 画分数
*
* @param g 画笔
*/
private void paintScore(Graphics g) {
int x = 290;
int y = 160;
g.setColor(new Color(FONT_COLOR));//给画笔设置字体颜色
Font font = g.getFont();//取得画笔g当前字体
font = new Font(font.getName(), font.getStyle(), FONT_SIZE);//重新设置字体大小
g.setFont(font);//更改g的字体
g.drawString("SCORE:" + score, x, y);
y += 60;
g.drawString("LINES:" + lines, x, y);
y += 60;
g.drawString("LEVEL:" + level, x, y);
}
/**
* 画游戏状态
*
* @param g 画笔
*/
private void paintState(Graphics g) {
switch (state) {
case PAUSE:
g.drawImage(pause, -15, -15, null);
break;
case GAME_OVER:
g.drawImage(gameover, -15, -15, null);
break;
}
}
/**
* 判断是否出界
*/
private boolean outOfBounds() {
Cell[] cells = tetromino.cells;
for (int i = 0; i < cells.length; i++) {
Cell cell = cells[i];
int col = cell.getCol();
int row = cell.getRow();
if (row < 0 || row >= ROWS || col < 0 || col >= COLS) {
return true;
}
}
return false;
}
/**
* 判断正在下落的方块是否和墙上的方块重叠
*/
private boolean coincide() {
Cell[] cells = tetromino.cells;
for (int i = 0; i < cells.length; i++) {
Cell cell = cells[i];
int col = cell.getCol();
int row = cell.getRow();
//如果墙上的(row,col)位置不是null,那就重叠了
if (wall[row][col] != null) {
return true;
}
}
return false;
}
/**
* 右移
*/
private void moveRightAction() {
tetromino.moveRight();
if (outOfBounds() || coincide()) {
tetromino.moveLeft();
}
}
/**
* 左移
*/
private void moveLeftAction() {
tetromino.moveLeft();
if (outOfBounds() || coincide()) {
tetromino.moveRight();
}
}
/**
* 下落流程控制
*/
private void softDropAction() {
if (canDrop()) {
tetromino.softDrop();
} else {
landIntoWall();
int lines = destoryLines();
this.lines += lines;
this.score += scoreTable[lines];
if (isGameOver()) {
state = GAME_OVER;
} else {
tetromino = nextOne;
nextOne = Tetromino.randomOne();//重新赋值
}
}
}
/**
* 快速下落流程控制
*/
private void hardDropAction() {
while (canDrop()) {//一直循环执行下落动作
tetromino.softDrop();
}
landIntoWall();
int lines = destoryLines();
this.lines += lines;
this.score += scoreTable[lines];
if (isGameOver()) {
state = GAME_OVER;
} else {
tetromino = nextOne;
nextOne = Tetromino.randomOne();//重新赋值
}
}
/**
* 右旋转
*/
private void rotateRightAction() {
tetromino.rotateRight();
if (outOfBounds() || coincide()) {
tetromino.rotateLeft();
}
}
/**
* 左旋转
*/
private void rotateLeftAction() {
tetromino.rotateLeft();
if (outOfBounds() || coincide()) {
tetromino.rotateRight();
}
}
/**
* 判断是否能下落
*/
private boolean canDrop() {
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 == (ROWS - 1) || wall[row + 1][col] != null) {
return false;
}
}
return true;
}
/**
* 固定到墙上
*/
private void landIntoWall() {
Cell[] cells = tetromino.cells;
for (int i = 0; i < cells.length; i++) {
Cell cell = cells[i];
int row = cell.getRow();
int col = cell.getCol();
wall[row][col] = cell;
}
}
/**
* 销毁满行
*
* @return lines 销毁行的数量
*/
private int destoryLines() {
int lines = 0;//销毁行的数量
for (int row = 0; row < ROWS; row++) {//遍历行
if (fullCells(row)) {
deleteRow(row);
lines++;
}
}
return lines;
}
/**
* 删除行
*/
private void deleteRow(int row) {
for (int i = row; i >= 1; i--) {//i从满的那行开始
System.arraycopy(wall[i - 1], 0, wall[i], 0, COLS);//把i-1行复制到i行
}
Arrays.fill(wall[0], null);//把第一行全部填null值
}
/**
* 判断是否满行
*/
private boolean fullCells(int row) {
Cell[] cells = wall[row];//行
for (int i = 0; i < cells.length; i++) {
Cell cell = cells[i];//行中每格
if (cell == null) {
return false;
}
}
return true;
}
/**
* 判断游戏是否结束
*/
private boolean isGameOver() {
/*
* 下一个出场方块没有位置放了,就是游戏结束
* 即下一个出场方块每个格子行列对应的墙上如果有格子,就游戏结束
*/
Cell[] cells = nextOne.cells;
for (int i = 0; i < cells.length; i++) {
Cell cell = cells[i];
int row = cell.getRow();
int col = cell.getCol();
if (wall[row][col] != null) {
return true;
}
}
return false;
}
/**
* RUNNING状态下中操作
*/
private void processRunningKey(int key) {
switch (key) {
case KeyEvent.VK_Q:
System.exit(0);
break;//按q退出游戏
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;//按z左旋转
case KeyEvent.VK_P:
state = PAUSE;
break;//按p暂停
}
}
/**
* PAUSE状态下中操作
*/
private void processPauseKey(int key) {
switch (key) {
case KeyEvent.VK_Q:
System.exit(0);
break;//按q退出游戏
case KeyEvent.VK_C:
index = 1;
state = RUNNING;
break;//按c继续游戏
}
}
/**
* GAME_OVER状态下中操作
*/
private void processGameoverKey(int key) {
switch (key) {
case KeyEvent.VK_Q:
System.exit(0);
break;//按q退出游戏
case KeyEvent.VK_S://按s重新开始游戏
this.lines = 0;
this.score = 0;
this.wall = new Cell[ROWS][COLS];
this.tetromino = Tetromino.randomOne();
this.nextOne = Tetromino.randomOne();
this.state = RUNNING;
this.index = 0;
}
}
/**
* 游戏启动方法action:初始化
*/
public void action() {
wall = new Cell[ROWS][COLS];
tetromino = Tetromino.randomOne();//tetromino实例化
nextOne = Tetromino.randomOne();//nextOne
state = RUNNING;
/**
* 键盘监听事件
*/
KeyAdapter event = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {//键盘按下事件
int key = e.getKeyCode();
switch (state) {
case RUNNING:
processRunningKey(key);
break;
case PAUSE:
processPauseKey(key);
break;
case GAME_OVER:
processGameoverKey(key);
break;
}
repaint();
}
};
this.requestFocus();//请求此 Component 获取输入焦点。
this.addKeyListener(event);
timer = new Timer();
timer.schedule(new TimerTask() {//创建定时器任务
@Override
public void run() {
//下落速度控制逻辑
speed = 40 - (score / 1000);
speed = speed <= 1 ? 1 : speed;
level = 41 - speed;
if (index % speed == 0) {
if (state == RUNNING) {
softDropAction();
}
}
index++;
repaint();
}
}, intervel, intervel);
}
public static void main(String[] args) {
JFrame frame = new JFrame("俄罗斯方块");
TetrisGame tetrisGame = new TetrisGame();
frame.add(tetrisGame);
frame.setSize(550, 600);//设置窗口初始大小
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);//设置窗口初始位置
frame.setVisible(true);//设置窗体是否显示
tetrisGame.action();//游戏启动
}
}
编译项目:
启动项目:运行TetrisGame类的main()方法: