线程休眠后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
相关产品推荐
相关产品推荐

