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

线程休眠后JFrame中游戏对象位置偏移问题求助

问题分析与解决思路

兄弟,你这个问题的核心坑在于你把线程休眠放在了Swing的事件调度线程(EDT)里!

Swing的所有UI更新和事件处理都是在EDT里跑的,你在Handler.tick()里调用Thread.sleep(1000),直接把EDT卡死了。这1秒内,UI完全没法刷新,而你的游戏对象的逻辑可能在休眠结束后,一次性执行了本该在这1秒里逐步完成的移动操作,所以醒来后位置直接跳变,看起来就像瞬移了一样。

解决步骤

1. 把游戏逻辑和UI线程彻底分离

别让游戏逻辑(比如tick()方法)占用EDT,单独开一个后台线程来跑游戏循环。这样休眠的时候,只会暂停游戏逻辑,不会卡住UI渲染。

2. 用规范的游戏循环控制速率

直接用Thread.sleep()控制帧率不稳定,建议用基于时间差的循环来保证固定的tick次数,避免逻辑跳变。

3. 确保UI渲染在EDT执行

所有Swing组件的更新必须在EDT里做,用SwingUtilities.invokeLater()把渲染操作丢给EDT处理。

修改后的代码示例

重构后的Handler类

import javax.swing.SwingUtilities;
import java.util.LinkedList;
import java.awt.Graphics;

public class Handler {
    private LinkedList<GameObject> object = new LinkedList<>();
    private volatile boolean running = true;
    private Thread gameThread;
    private GamePanel yourGamePanel; // 替换为你的游戏面板实例

    public Handler(GamePanel gamePanel) {
        this.yourGamePanel = gamePanel;
        // 启动游戏逻辑线程
        gameThread = new Thread(this::gameLoop);
        gameThread.start();
    }

    // 规范的游戏循环,控制tick速率
    private void gameLoop() {
        long lastTime = System.nanoTime();
        final double nsPerTick = 1_000_000_000.0 / 60; // 每秒60次tick
        double delta = 0;

        while (running) {
            long now = System.nanoTime();
            delta += (now - lastTime) / nsPerTick;
            lastTime = now;

            // 累积足够时间就执行一次tick
            while (delta >= 1) {
                try {
                    tick();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                delta--;
            }

            // 把渲染操作丢给EDT执行,保证线程安全
            SwingUtilities.invokeLater(this::triggerRender);
        }
    }

    private void tick() throws InterruptedException {
        for (int i = 0; i < object.size(); i++) {
            GameObject tempObject = object.get(i);
            tempObject.tick();
            if (tempObject.x == 100) {
                // 这里休眠的是游戏线程,不是EDT,UI不会卡
                Thread.sleep(1000);
            }
        }
    }

    // 触发UI组件重绘
    private void triggerRender() {
        yourGamePanel.repaint();
    }

    // 这个render方法要在自定义面板的paintComponent里调用
    public void render(Graphics g) {
        for (int i = 0; i < object.size(); i++) {
            GameObject tempObject = object.get(i);
            tempObject.render(g);
        }
    }

    // 停止游戏线程的方法
    public void stopGame() {
        running = false;
        gameThread.interrupt();
    }
}

自定义游戏面板(GamePanel)

import javax.swing.JPanel;
import java.awt.Graphics;

public class GamePanel extends JPanel {
    private Handler handler;

    public GamePanel(Handler handler) {
        this.handler = handler;
        setDoubleBuffered(true); // 开启双缓冲,解决画面闪烁问题
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // 在这里调用Handler的render方法,完成游戏对象绘制
        handler.render(g);
    }
}

关键注意点

  • volatile关键字修饰running:保证后台线程能及时感知到停止信号。
  • 双缓冲:setDoubleBuffered(true)能避免游戏画面闪烁。
  • 绝对不要在EDT里做耗时操作:比如sleep、复杂计算,否则UI会直接卡死,各种奇怪的渲染bug都会找上门。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:27:28