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

Java Swing中InvokeLater无法立即触发repaint问题求助

问题分析与解决方案

你的核心问题在于Swing重绘机制的特性和不合理的游戏循环设计:

  1. Swing的repaint()并非立即执行绘制,而是向EDT(事件调度线程)发送重绘请求。如果短时间内发送大量重复请求,EDT会自动合并这些请求,只执行一次绘制,这就导致paintComponent被调用的次数远低于你设置的120FPS。
  2. 当鼠标移动或按键时,EDT会优先处理这些输入事件,此时会顺带处理积压的重绘请求,所以你会看到渲染变得流畅。

修复步骤

1. 优化游戏循环,避免忙等并给EDT留处理时间

你的当前循环是无休眠的忙等,占用大量CPU资源的同时,让EDT没有足够时间处理重绘请求。修改循环逻辑,加入休眠:

@Override
public void run() {
    double timePerFrame = 1000000000.0 / FPS_SET;
    long lastFrame = System.nanoTime();
    long now;
    long sleepTime;

    while (true) {
        now = System.nanoTime();
        if (now - lastFrame >= timePerFrame) {
            // 先执行游戏逻辑更新(和渲染分离)
            updateGameState();
            // 发送重绘请求
            _reDraw();
            lastFrame = now;
        } else {
            // 计算剩余时间,休眠减少CPU占用,给EDT处理重绘的机会
            sleepTime = (long) ((timePerFrame - (now - lastFrame)) / 1000000);
            if (sleepTime > 0) {
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

// 单独的游戏逻辑更新方法
private void updateGameState() {
    // 这里写角色移动、碰撞检测、动画帧更新等逻辑
}

2. 正确实现paintComponent方法

必须调用super.paintComponent(g),这是Swing组件绘制的基础,负责清理上一帧的绘制内容,否则会出现画面残留或绘制异常:

public class GamePanel extends JPanel {
    // ...
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); // 不可省略,清理背景
        // 你的绘制逻辑
        renderGame(g);
    }

    private void renderGame(Graphics g) {
        // 绘制角色、场景等内容
    }
}

3. 可选:用Swing Timer简化实现(适合轻量游戏)

如果游戏逻辑不复杂,可以直接使用Swing的Timer来驱动更新和渲染,它本身运行在EDT上,无需手动处理线程同步:

public class Game {
    private GamePanel gamePanel;
    private final int FPS_SET = 120;
    private Timer gameTimer;

    public Game() {
        gamePanel = new GamePanel();
        gamePanel.requestFocus();

        // 计算每帧间隔毫秒数
        int frameDelay = 1000 / FPS_SET;
        gameTimer = new Timer(frameDelay, e -> {
            updateGameState();
            gamePanel.repaint();
        });
        gameTimer.start();
    }

    private void updateGameState() {
        // 游戏状态更新逻辑
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 20:23:11