Java中Delta Time导致运动卡顿问题排查
解决Swing中Delta Time计算导致的圆形移动卡顿问题
嘿,我看到你在尝试用delta time实现圆形在不同帧率下的平滑移动,但遇到了卡顿问题。咱们来一步步拆解你的代码里的问题,然后修正它:
你的代码里的核心问题
1. Delta Time的初始值错误
你初始化lastTime = 0,第一次计算deltaTime的时候,会得到一个超大的初始值(因为System.nanoTime()返回的是从某个固定时间点开始的纳秒数,和0的差值会非常大),这会导致圆形第一帧就直接跳出去,后续的移动也会因为这个初始异常值变得不稳定。
2. 错误的线程使用方式
Swing是单线程模型,所有UI相关的操作(包括渲染和组件更新)都必须在**事件调度线程(EDT)**中执行。但你在main线程里写了一个无限循环调用frame.repaint(),这会:
- 占用大量CPU资源,导致EDT无法及时处理渲染请求
repaint()是异步请求,频繁调用会导致渲染队列积压,出现明显卡顿
3. Delta Time的计算写法不够清晰
虽然你的表达式(lastTime - (lastTime = System.nanoTime())) / -1000000.0数学上是对的,但这种赋值嵌套的写法可读性极差,很容易出错,也不利于后续调试。
修正后的解决方案
我们需要用正确的线程方式来处理游戏循环,同时修复delta time的计算逻辑。这里推荐使用javax.swing.Timer来实现定时更新,它会自动在EDT中执行任务,完美适配Swing的线程模型:
修正后的主类
import javax.swing.JFrame; import javax.swing.Timer; public class Main { public static long lastTime; public static double deltaTime; public static void main(String[] args) { // 初始化lastTime为当前时间,避免初始超大delta值 lastTime = System.nanoTime(); // 设置窗口 JFrame frame = new JFrame("Test"); frame.setSize(600, 400); PanelP panel = new PanelP(); frame.add(panel); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 加上关闭窗口退出程序的逻辑 frame.setVisible(true); // 使用Swing Timer实现游戏循环,Timer会自动在EDT中执行任务 Timer gameTimer = new Timer(0, e -> { // 计算Delta Time(转换为毫秒单位) long currentTime = System.nanoTime(); deltaTime = (currentTime - lastTime) / 1000000.0; lastTime = currentTime; // 更新圆形位置 panel.updatePosition(); // 请求重绘 frame.repaint(); }); gameTimer.start(); } }
修正后面板类
import java.awt.Graphics; import javax.swing.JPanel; public class PanelP extends JPanel { private float x = 0; // 初始化圆形的初始X位置 public PanelP() { super(); } // 单独的更新方法,分离位置更新与渲染逻辑,代码结构更清晰 public void updatePosition() { // 调整为每秒移动100像素的速度(0.1f * deltaTime,因为deltaTime是毫秒) x += 0.1f * (float) Main.deltaTime; // 让圆形超出屏幕后回到左侧,方便持续测试 if (x > getWidth()) { x = -30; } } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // 必须调用super,否则会留下之前的绘制痕迹 // 绘制圆形 g.drawOval(Math.round(x), 50, 30, 30); } }
关键修改点说明
- 线程安全:用
Swing Timer替代main线程的无限循环,确保所有UI操作都在EDT中执行,彻底解决线程冲突导致的卡顿 - Delta Time初始化:一开始就把
lastTime设为当前时间,避免第一帧出现异常大的delta值 - 分离更新和渲染:把位置更新逻辑放到单独的
updatePosition()方法里,符合游戏开发的逻辑分离思想,便于维护 - 修复绘制痕迹:在
paintComponent中调用super.paintComponent(g),每次重绘时先清空面板,避免圆形移动后留下拖影
这样修改后,圆形就能在不同帧率下平滑移动,不会出现卡顿了!
内容的提问来源于stack exchange,提问作者bar9833625
相关产品推荐
相关产品推荐

