1. 基础框架
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Random;
public class Tetris extends JPanel implements ActionListener {
private final int BoardWidth = 10;
private final int BoardHeight = 20;
private Timer timer;
private boolean isFallingFinished = false;
private boolean isStarted = false;
private boolean isPaused = false;
private int numLinesRemoved = 0;
private int score = 0;
private int curX = BoardWidth / 2; // 假设初始位置在中间
private int curY = 0;
private JLabel statusBar;
private Shape currentPiece;
private Tetrominoes[][] board;
public Tetris() {
setFocusable(true);
board = new Tetrominoes[BoardHeight][BoardWidth];
for (int i = 0; i < BoardHeight; i++) {
for (int j = 0; j < BoardWidth; j++) {
board[i][j] = Tetrominoes.NoShape;
}
}
statusBar = new JLabel("Score: 0, Lines: 0", SwingConstants.CENTER);
timer = new Timer(500, this); // 初始时间间隔设为500毫秒
addKeyListener(new TAdapter());
}
public void startGame() {
isStarted = true;
isPaused = false;
timer.start();
newPiece();
}
public void pauseGame() {
isPaused = !isPaused;
if (isPaused) {
timer.stop();
} else {
timer.start();
}
}
public void stopGame() {
isStarted = false;
isPaused = false;
timer.stop();
currentPiece = null;
}
public JLabel getStatusBar() {
return statusBar;
}
private void newPiece() {
Random random = new Random();
int shapeIndex = random.nextInt(Tetrominoes.values().length - 1); // 排除 NoShape
currentPiece = new Shape(Tetrominoes.values()[shapeIndex], curX, curY);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == timer) {
if (isFallingFinished) {
isFallingFinished = false;
placePiece();
}
oneLineDown();
}
}
private void oneLineDown() {
if (currentPiece != null && !isPaused) {
int newY = currentPiece.getY() + 1;
if (!tryMove(currentPiece, currentPiece.getX(), newY)) {
isFallingFinished = true;
currentPiece = null;
placePiece();
}
}
}
private boolean tryMove(Shape piece, int newX, int newY) {
// 这里需要实现移动逻辑,检查是否越界或碰撞
return true;
}
private void placePiece() {
// 这里需要实现放置当前方块到游戏板的逻辑
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Tetris");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 400);
frame.add(new Tetris());
frame.setVisible(true);
});
}
// 内部类和其它方法将在后面提供
}
2. 枚举定义和 Shape 类
public enum Tetrominoes {
NoShape,
I, J, L, O, S, T, Z
}
public class Shape {
private Tetrominoes shape;
private int x, y;
private int[][] coordinates;
public Shape(Tetrominoes shape, int x, int y) {
this.shape = shape;
this.x = x;
this.y = y;
this.coordinates = getCoordinatesForShape(shape);
}
private int[][] getCoordinatesForShape(Tetrominoes shape) {
// 根据形状返回方块的坐标,这里需要具体实现
return new int[4][2]; // 假设所有形状都是 4x1 的方块
}
public Tetrominoes getShape() {
return shape;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
// 旋转逻辑将在后面提供
}
3. Shape 类的旋转逻辑
public class Shape {
// ... 现有代码 ...
public void rotate() {
int[][] newCoordinates = new int[2][4];
for (int i = 0; i < 4; i++) {
int x = coordinates[i][1]; // 交换 x 和 y 的值
int y = coordinates[i][0];
newCoordinates[1 - x][y] = 1; // 沿对角线翻转
}
coordinates = newCoordinates;
}
// 检查旋转后的形状是否在游戏区域内
public boolean rotate(Tetrominoes[][] gameBoard, int newX, int newY) {
int[][] newCoordinates = new int[4][2];
for (int i = 0; i < 4; i++) {
int x = -coordinates[i][1]; // 逆时针旋转90度
int y = coordinates[i][0];
newCoordinates[y + newX][x + newY] = 1;
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (newCoordinates[i][j] == 1 && (newY + i < 0 || newY + i >= BoardHeight || newX + j < 0 || newX + j >= BoardWidth || gameBoard[newY + i][newX + j] != Tetrominoes.NoShape)) {
return false;
}
}
}
// 如果旋转有效,更新坐标
this.coordinates = newCoordinates;
this.x = newX;
this.y = newY;
return true;
}
}
4. TAdapter 类键盘监听逻辑
class TAdapter extends KeyAdapter {
public void keyPressed(KeyEvent e) {
if (!isStarted || currentPiece == null) {
return;
}
int keycode = e.getKeyCode();
switch (keycode) {
case KeyEvent.VK_LEFT:
if (tryMove(currentPiece, currentPiece.getX() - 1, currentPiece.getY())) {
currentPiece.setX(currentPiece.getX() - 1);
}
break;
case KeyEvent.VK_RIGHT:
if (tryMove(currentPiece, currentPiece.getX() + 1, currentPiece.getY())) {
currentPiece.setX(currentPiece.getX() + 1);
}
break;
case KeyEvent.VK_DOWN:
if (tryMove(currentPiece, currentPiece.getX(), currentPiece.getY() + 1)) {
currentPiece.setY(currentPiece.getY() + 1);
}
break;
case KeyEvent.VK_UP:
if (currentPiece.rotate(board, currentPiece.getX(), currentPiece.getY())) {
// 旋转成功
} else {
// 旋转无效,可能需要撤销旋转或处理碰撞
}
break;
case KeyEvent.VK_SPACE:
dropDown();
break;
case KeyEvent.VK_P:
pauseGame();
break;
}
}
}
5. 移动和放置逻辑
public boolean tryMove(Shape piece, int newX, int newY) {
for (int i = 0; i < piece.coordinates.length; i++) {
for (int j = 0; j < piece.coordinates[i].length; j++) {
if (piece.coordinates[i][j] == 1) {
int testY = newY + i;
int testX = newX + j;
if (testY >= BoardHeight || testX < 0 || testX >= BoardWidth || board[testY][testX] != Tetrominoes.NoShape) {
return false;
}
}
}
}
piece.setX(newX);
piece.setY(newY);
return true;
}
private void dropDown() {
while (tryMove(currentPiece, currentPiece.getX(), currentPiece.getY() + 1)) {
currentPiece.setY(currentPiece.getY() + 1);
}
isFallingFinished = true;
placePiece();
}
private void placePiece() {
for (int i = 0; i < currentPiece.coordinates.length; i++) {
for (int j = 0; j < currentPiece.coordinates[i].length; j++) {
if (currentPiece.coordinates[i][j] == 1) {
board[currentPiece.getY() + i][currentPiece.getX() + j] = currentPiece.getShape();
}
}
}
removeFullLines();
currentPiece = newPiece();
}
6. 绘制游戏界面的方法
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// 绘制背景
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
// 绘制游戏板
for (int i = 0; i < BoardHeight; i++) {
for (int j = 0; j < BoardWidth; j++) {
g.setColor(getDrawColor(board[i][j]));
g.fillRect(j * 30, i * 30, 30, 30);
}
}
// 绘制当前活动方块
if (currentPiece != null) {
drawShape(g, currentPiece);
}
// 绘制状态栏
statusBar.setText("Score: " + score + ", Lines: " + numLinesRemoved);
statusBar.setBounds(0, getHeight() - 50, getWidth(), 50);
statusBar.paintImmediately(statusBar.getBounds());
}
private void drawShape(Graphics g, Shape shape) {
for (int i = 0; i < shape.coordinates.length; i++) {
for (int j = 0; j < shape.coordinates[i].length; j++) {
if (shape.coordinates[i][j] == 1) {
g.setColor(getDrawColor(shape.getShape()));
g.fillRect((shape.getX() + j) * 30, (shape.getY() + i) * 30, 30, 30);
}
}
}
}
private Color getDrawColor(Tetrominoes shape) {
switch (shape) {
case I:
return Color.CYAN;
case J:
return Color.BLUE;
case L:
return Color.ORANGE;
case O:
return Color.GREEN;
case S:
return Color.YELLOW;
case T:
return Color.PURPLE;
case Z:
return Color.RED;
default:
return Color.WHITE;
}
}
7. 消除行的逻辑
private void removeFullLines() {
boolean[] linesToRemove = new boolean[BoardHeight];
for (int y = 0; y < BoardHeight; y++) {
linesToRemove[y] = true;
for (int x = 0; x < BoardWidth; x++) {
if (board[y][x] == Tetrominoes.NoShape) {
linesToRemove[y] = false;
break;
}
}
}
// 记录消除的行数
int linesCleared = 0;
for (boolean lineToRemove : linesToRemove) {
if (lineToRemove) {
linesCleared++;
numLinesRemoved++;
// 清除当前行
for (int x = 0; x < BoardWidth; x++) {
board[y][x] = Tetrominoes.NoShape;
}
// 将上面的行下移
while (y > 0) {
for (int x = 0; x < BoardWidth; x++) {
board[y][x] = board[y - 1][x];
}
y--;
}
// 更新最上面的行为空
for (int x = 0; x < BoardWidth; x++) {
board[0][x] = Tetrominoes.NoShape;
}
y = BoardHeight - 1; // 重置 y 坐标
}
}
// 更新分数
updateScore(linesCleared);
}
private void updateScore(int linesCleared) {
// 简单的计分逻辑,您可以根据需要进行调整
score = score + (100 * linesCleared) + (10 * (int) Math.pow(1.5, linesCleared - 1));
statusBar.setText("Score: " + score + ", Lines: " + numLinesRemoved);
}
9. actionPerformed 方法
@Override
public void actionPerformed(ActionEvent e) {
if (!isPaused && isStarted) {
if (isFallingFinished) {
isFallingFinished = false;
placePiece();
if (checkGameOver()) {
stopGame();
JOptionPane.showMessageDialog(this, "Game Over! Your score: " + score, "Game Over", JOptionPane.PLAIN_MESSAGE);
}
} else {
oneLineDown();
}
}
}
10. 完善 Shape 类的构造器和旋转逻辑
public class Shape {
// ... 现有代码 ...
public Shape(Tetrominoes shape, int x, int y) {
this.shape = shape;
this.x = x;
this.y = y;
this.coordinates = shape.getCoordinates();
}
// 旋转形状
public void rotate() {
int[][] rotated = new int[2][4];
for (int i = 0; i < 4; i++) {
rotated[1 - coordinates[i][0]][coordinates[i][1]] = 1;
}
coordinates = rotated;
}
// ... 其余代码 ...
}