package tetris; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.Arrays; import java.util.Random; public class GamePanel extends JPanel { private static final int BLOCK_SIZE = 30; private static final int ROWS = 20, COLS = 10; private final int[][] board = new int[ROWS][COLS]; private Block currentBlock, nextBlock; private Timer timer; private int fallSpeed = 500, score = 0; private final NextBlockPanel nextBlockPanel; private final ResourceManager resManager = ResourceManager.getInstance(); public GamePanel(NextBlockPanel nextBlockPanel) { this.nextBlockPanel = nextBlockPanel; setPreferredSize(new Dimension(COLS * BLOCK_SIZE, ROWS * BLOCK_SIZE)); setBackground(Color.BLACK); initControls(); initTimer(); newGame(); addComponentListener(new ComponentAdapter() { @Override public void componentShown(ComponentEvent e) { requestFocusInWindow(); } }); } private void initControls() { addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_SPACE: togglePause(); break; case KeyEvent.VK_LEFT: if (!isPaused && !isGameOver) handleMove(-1, 0); break; case KeyEvent.VK_RIGHT: if (!isPaused && !isGameOver) handleMove(1, 0); break; case KeyEvent.VK_DOWN: if (!isPaused && !isGameOver) hardDrop(); break; case KeyEvent.VK_UP: if (!isPaused && !isGameOver) rotateBlock(); break; } repaint(); } }); } private void handleMove(int dx, int dy) { if (canMove(dx, dy)) { if (dx == -1) currentBlock.moveLeft(); else if (dx == 1) currentBlock.moveRight(); } } public void togglePause() { isPaused = !isPaused; if (timer != null) { if (isPaused) { timer.stop(); } else { timer.setDelay(fallSpeed); timer.start(); } } Toolkit.getDefaultToolkit().beep(); requestFocusInWindow(); repaint(); } private boolean canMove(int dx, int dy) { int[][] shape = currentBlock.getShape(); for (int i = 0; i < shape.length; i++) { for (int j = 0; j < shape[i].length; j++) { if (shape[i][j] == 1) { int newX = currentBlock.getX() + j + dx; int newY = currentBlock.getY() + i + dy; if (newX < 0 || newX >= COLS || newY >= ROWS || (newY >= 0 && board[newY][newX] != 0)) return false; } } } return true; } private void rotateBlock() { Block temp = new Block(currentBlock.getType()); temp.moveTo(currentBlock.getX(), currentBlock.getY()); temp.rotate(); if (canRotate(temp)) currentBlock.rotate(); } private boolean canRotate(Block block) { int[][] shape = block.getShape(); for (int i = 0; i < shape.length; i++) { for (int j = 0; j < shape[i].length; j++) { if (shape[i][j] == 1) { int x = block.getX() + j; int y = block.getY() + i; if (x < 0 || x >= COLS || y >= ROWS || (y >= 0 && board[y][x] != 0)) return false; } } } return true; } private void hardDrop() { while (canMove(0, 1)) currentBlock.moveDown(); mergeBlock(); clearLines(); spawnNewBlock(); repaint(); } private void mergeBlock() { int[][] shape = currentBlock.getShape(); for (int i = 0; i < shape.length; i++) { for (int j = 0; j < shape[i].length; j++) { if (shape[i][j] == 1) { int y = currentBlock.getY() + i; int x = currentBlock.getX() + j; if (y < 0) gameOver(); else board[y][x] = currentBlock.getType() + 1; } } } } private static final int[] SCORE_TABLE = {100, 300, 500, 800}; private void clearLines() { int linesCleared = 0; for (int i = ROWS - 1; i >= 0; i--) { if (isLineFull(i)) { if (i > 0) { System.arraycopy(board, 0, board, 1, i); } Arrays.fill(board[0], 0); linesCleared++; i++; } } if (linesCleared > 0) { resManager.playClearSound(); // 添加消除音效 score += (linesCleared < 4) ? SCORE_TABLE[linesCleared - 1] : SCORE_TABLE[3]; Toolkit.getDefaultToolkit().beep(); nextBlockPanel.updateScore(); } } private boolean isLineFull(int row) { for (int cell : board[row]) if (cell == 0) return false; return true; } private void spawnNewBlock() { currentBlock = (nextBlock != null) ? nextBlock : new Block(new Random().nextInt(7)); nextBlock = new Block(new Random().nextInt(7)); nextBlockPanel.setNextBlock(nextBlock); if (!canSpawn()) gameOver(); } private boolean canSpawn() { if (currentBlock.getY() < 0) return false; int[][] shape = currentBlock.getShape(); for (int i = 0; i < shape.length; i++) { for (int j = 0; j < shape[i].length; j++) { if (shape[i][j] == 1) { int y = currentBlock.getY() + i; int x = currentBlock.getX() + j; if (x < 0 || x >= COLS || y >= ROWS || y < 0 || board[y][x] != 0) return false; } } } return true; } public void newGame() { for (int[] row : board) Arrays.fill(row, 0); score = 0; spawnNewBlock(); isGameOver = isPaused = false; if (timer != null) { timer.start(); } requestFocusInWindow(); } private void initTimer() { timer = new Timer(fallSpeed, e -> { if (!isPaused && !isGameOver) { if (canMove(0, 1)) { currentBlock.moveDown(); } else { mergeBlock(); clearLines(); spawnNewBlock(); } repaint(); } }); } public void setFallSpeed(int speed) { fallSpeed = speed; if (timer != null) { timer.setDelay(speed); if (!isPaused) { timer.restart(); } } } public int getScore() { return score; } private void gameOver() { isGameOver = true; timer.stop(); JOptionPane.showMessageDialog(this, "得分: " + score); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // 绘制游戏背景图 Image gameBg = resManager.getGameBackground(); if (gameBg != null) { g.drawImage(gameBg, 0, 0, getWidth(), getHeight(), this); } // 绘制固定方块 for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { if (board[i][j] != 0) { int type = board[i][j] - 1; Image blockImage = resManager.getBlockImage(type); if (blockImage != null) { g.drawImage(blockImage, j * BLOCK_SIZE, i * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, this); } } } } // 绘制当前方块 if (currentBlock != null) { int[][] shape = currentBlock.getShape(); for (int i = 0; i < shape.length; i++) { for (int j = 0; j < shape[i].length; j++) { if (shape[i][j] == 1) { int x = (currentBlock.getX() + j) * BLOCK_SIZE; int y = (currentBlock.getY() + i) * BLOCK_SIZE; if (y < getHeight()) { int type = currentBlock.getType(); Image blockImage = resManager.getBlockImage(type); if (blockImage != null) { g.drawImage(blockImage, x, y, BLOCK_SIZE, BLOCK_SIZE, this); } } } } } } } private boolean isPaused = false, isGameOver = false; }