You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

2048游戏GUI开发问题:actionPerformed函数无法触发

问题分析与解决方案

核心问题

  1. EDT线程被阻塞:actionPerformed方法中调用queue.take()是阻塞式操作,会卡住Swing的事件派发线程(EDT),导致Timer后续事件无法触发,UI也无法更新,这是actionPerformed从未执行或执行后无响应的根本原因。
  2. 焦点未正确获取:即使设置了setFocusable(true),面板可能没有实际获得键盘焦点,导致KeyListener无法接收按键事件。
  3. Timer使用逻辑错误:用Timer配合阻塞队列等待用户输入是不合理的,Timer的作用应该是定时更新UI,而非阻塞等待操作。

修复步骤

  • 移除Timer中的阻塞逻辑,直接在按键事件处理后执行方块移动、生成新方块和重绘操作
  • 在startGame方法中添加requestFocusInWindow(),确保面板获得键盘焦点
  • 简化逻辑,不需要用BlockingQueue来传递操作信号,直接在按键事件中触发游戏逻辑

修改后的完整代码

GamePanel.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;

public class GamePanel extends JPanel {
    static final int SCREEN_WIDTH = 600;
    static final int SCREEN_HEIGHT = 600;
    Random random;
    Board grid;
    boolean running = false;

    GamePanel() {
        random = new Random();
        this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
        this.setBackground(Color.white);
        this.setFocusable(true);
        this.addKeyListener(new MyKeyAdapter());
        startGame();
    }

    public void startGame() {
        grid = new Board();
        grid.newTile();
        grid.newTile();
        running = true;
        requestFocusInWindow(); // 确保面板获得焦点
        System.out.println("game started");
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        int tileSize = SCREEN_WIDTH / this.grid.SIZE;

        // 绘制网格线
        for (int row = 0; row < this.grid.SIZE; row++) {
            for (int col = 0; col < this.grid.SIZE; col++) {
                int x = col * tileSize;
                int y = row * tileSize;
                g2d.drawRect(x, y, tileSize, tileSize);
            }
        }

        // 绘制方块
        for (int row = 0; row < this.grid.SIZE; row++) {
            for (int col = 0; col < this.grid.SIZE; col++) {
                int x = col * tileSize;
                int y = row * tileSize;
                Tile tile = grid.board[row][col];
                if (tile != null) {
                    g2d.setColor(tile.getColor());
                    g2d.fillRect(x, y, tileSize, tileSize);
                    g2d.setColor(Color.BLACK);
                    // 调整文字位置,使其居中
                    FontMetrics fm = g2d.getFontMetrics();
                    int textX = x + (tileSize - fm.stringWidth(String.valueOf(tile.getValue()))) / 2;
                    int textY = y + (tileSize + fm.getAscent()) / 2;
                    g2d.drawString(String.valueOf(tile.getValue()), textX, textY);
                }
            }
        }
    }

    public class MyKeyAdapter extends KeyAdapter {
        @Override
        public void keyPressed(KeyEvent e) {
            if (!running) return;

            boolean moved = false;
            switch (e.getKeyCode()) {
                case KeyEvent.VK_LEFT:
                    grid.setDirection('L');
                    moved = grid.move();
                    System.out.println("Left key pressed");
                    break;
                case KeyEvent.VK_RIGHT:
                    grid.setDirection('R');
                    moved = grid.move();
                    break;
                case KeyEvent.VK_UP:
                    grid.setDirection('U');
                    moved = grid.move();
                    break;
                case KeyEvent.VK_DOWN:
                    grid.setDirection('D');
                    moved = grid.move();
                    break;
            }

            // 只有方块实际移动了才生成新方块
            if (moved) {
                grid.newTile();
                // 检查游戏是否结束
                if (!grid.gridHasSpace() && !grid.canMerge()) {
                    running = false;
                    JOptionPane.showMessageDialog(null, "Game Over!");
                }
                repaint();
            }
        }
    }
}

GameFrame.java

import javax.swing.JFrame;

public class GameFrame extends JFrame {
    GameFrame() {
        this.add(new GamePanel());
        this.setTitle("Game2048");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setResizable(false);
        this.pack();
        this.setVisible(true);
        this.setLocationRelativeTo(null);
    }
}

Game2048.java

import javax.swing.SwingUtilities;

public class Game2048 {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new GameFrame());
    }
}

额外说明

  • 假设你的Board类的move()方法返回布尔值,表示方块是否实际发生了移动,这样可以避免在没有移动时生成新方块,符合2048的游戏规则
  • 需要在Board类中实现canMerge()方法,用于判断游戏是否真的结束(当没有空格且无法合并时才结束)
  • 调整了文字绘制的位置,使其在方块中居中显示

内容的提问来源于stack exchange,提问作者Maria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 11:31:08