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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:13:50