设计方案详细介绍
1. 架构设计与分层
本项目采用了简单的 MVC(模型-视图-控制器)设计思想,将整个游戏的状态、显示与控制逻辑整合在同一个类(Board 类)中。
- 模型层:
棋盘数据使用一维数组存储(类型为Tetrominoes[]
),每个元素表示一个单元格的方块类型。
类Shape
封装了俄罗斯方块的形状、坐标数据及旋转逻辑。 - 视图层:
Board 类继承自JPanel
,重写了paintComponent
方法用于绘制背景、固定的方块和当前下落的方块。 - 控制层:
采用 Swing 的定时器(Timer)定时更新游戏状态(让方块下落);同时利用 Swing 的 Key Bindings(命令模式)处理用户的按键操作,使代码更为解耦和符合 Swing 编程规范。
2. 关键设计模式
-
命令模式(Command Pattern)
采用 Swing Key Bindings 将按键(如“LEFT”、“RIGHT”、“DOWN”、“UP”、“SPACE”)与具体操作 Action 绑定。
这种方式比传统 KeyListener 更灵活,并且能够避免焦点问题,使得用户交互响应更加准确。 -
工厂模式(Factory Pattern)
在Shape
类中,通过setRandomShape
方法随机生成不同形状的方块。虽然本例中为简单实现,但这种设计易于扩展,如日后增加新的方块形状时只需扩充坐标模板即可。 -
单一职责原则(Single Responsibility Principle)
每个类均只负责特定功能:Tetris
类负责窗口初始化;Board
类负责游戏逻辑、绘图和用户交互;Shape
类专注于方块数据和旋转操作;- 枚举
Tetrominoes
用于描述各种固定的方块类型。
3. 代码优化与扩展性
- 详细注释与文档
所有公共方法均提供了详细的注释,方便日后维护和扩展。 - 简化数组操作
封装了计算数组下标的index(x, y)
方法,利用Arrays.fill
简化棋盘清空操作,使用System.arraycopy
进行数组复制,提升代码可读性和性能。 - 灵活的键盘绑定
通过 Key Bindings 实现的命令模式使得修改键位、添加新的交互动作十分方便,不需要更改底层逻辑。
4. 后续扩展建议
- 积分与关卡
可增加积分系统,统计消除行数,根据积分变化调整下落速度,实现关卡升级。 - 音效与动画
添加背景音乐和音效效果,增强游戏体验;可采用 Swing Timer 或线程实现简单动画。 - 独立的 MVC 分离
当游戏功能进一步复杂时,可以将模型、视图与控制器进一步分离,实现更清晰的分层结构。
总之,本设计在保证简单易懂的基础上采用了较多的设计模式和代码优化方法,为后续扩展(如增加游戏难度、积分统计、更多用户交互等)提供了良好的基础。
package ethereal.palace.game;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Random;
/**
* Tetris 游戏主窗口类
* 本类继承自 JFrame,用于创建游戏主窗口,并将游戏板(Board 对象)添加到窗口中。
* @author kong
*/
public class Tetris extends JFrame {
/**
* 构造函数,初始化用户界面
*/
public Tetris() {
initUI();
}
/**
* 初始化用户界面
* <p>
* 本方法创建一个游戏板对象,将其添加到窗口中,并设置窗口标题、关闭操作、大小及显示位置。
*/
private void initUI() {
Board board = new Board();
add(board);
setTitle("俄罗斯方块");
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
}
/**
* 主方法,程序入口
* 使用 EventQueue.invokeLater 确保线程安全地创建和显示主窗口。
*
* @param args 命令行参数(未使用)
*/
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
Tetris game = new Tetris();
game.setVisible(true);
});
}
}
/**
* 游戏板类
* 本类集模型、视图与控制于一身,负责处理游戏逻辑(如方块下落、碰撞检测、消除满行)、绘制棋盘和当前方块,
* 同时通过 Swing 的 Key Bindings 实现用户交互(移动、旋转、直接下落)。
*/
class Board extends JPanel implements ActionListener {
/**
* 棋盘宽度(列数)
*/
private final int BOARD_WIDTH = 10;
/**
* 棋盘高度(行数)
*/
private final int BOARD_HEIGHT = 20;
/**
* 单个单元格的像素大小
*/
private final int CELL_SIZE = 30;
/**
* 游戏定时器,每隔一定时间触发一次方块下落
*/
private final Timer timer;
/**
* 标记当前方块是否已触底
*/
private boolean isFallingFinished = false;
/**
* 当前方块的水平位置(列索引)
*/
private int currentX = 0;
/**
* 当前方块的垂直位置(行索引)
*/
private int currentY = 0;
/**
* 当前正在下落的方块对象
*/
private Shape currentPiece;
/**
* 棋盘数组,使用一维数组按行存储,每个元素表示该单元格的方块类型
*/
private final Tetrominoes[] board;
/**
* 构造函数,初始化棋盘、背景、键盘绑定、定时器及生成第一个方块
*/
public Board() {
setPreferredSize(new Dimension(BOARD_WIDTH * CELL_SIZE, BOARD_HEIGHT * CELL_SIZE));
setBackground(Color.BLACK);
board = new Tetrominoes[BOARD_WIDTH * BOARD_HEIGHT];
clearBoard();
currentPiece = new Shape();
initKeyBindings();
timer = new Timer(400, this);
timer.start();
newPiece();
}
/**
* 初始化键盘命令绑定
* 本方法使用 Swing 的 Key Bindings(命令模式)将指定按键与对应动作关联,
* 包括向左移动、向右移动、下移、旋转以及直接下落。
*/
private void initKeyBindings() {
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke("LEFT"), "moveLeft");
im.put(KeyStroke.getKeyStroke("RIGHT"), "moveRight");
im.put(KeyStroke.getKeyStroke("DOWN"), "moveDown");
im.put(KeyStroke.getKeyStroke("UP"), "rotate");
im.put(KeyStroke