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

使用Swing开发游戏时出现轻微卡顿问题求助

Swing开发游戏时出现轻微卡顿问题求助

嘿,完全懂你这种“眼里容不下一点卡顿”的执念——这么简单的动画要是不丝滑,确实难受!咱们来拆解下你代码里的问题,把这个卡顿解决掉:

你的代码里导致卡顿的核心问题

  1. 无节制的循环占用CPU:你的run()方法里是while(true)死循环,没有任何帧率控制,线程会疯狂抢占CPU资源,导致操作系统的线程调度出现波动,进而让渲染节奏不稳定,出现细微卡顿。
  2. 计时精度不足:用Date().time获取时间的精度是毫秒级,对于60fps的动画来说,每帧间隔只有~16ms,毫秒级的计时误差很容易导致帧间隔不稳定,出现卡顿感。
  3. 错误的Graphics资源管理:在paintComponent里手动调用g2D.dispose()是不对的——这个Graphics上下文是Swing框架传递给你的,应该由框架自己管理释放,手动dispose可能会导致后续渲染出现异常。

针对性的修复方案

下面是修改后的关键代码部分,我会逐一说明改动点:

1. 替换高精度计时函数

把你的now()函数换成精度更高的纳秒级计时:

fun now() = System.nanoTime() / 1_000_000_000.0 // 转换为秒级的Double值

System.nanoTime()是专门用于短时间间隔计时的,精度远高于Date,能避免时间跳变和误差。

2. 添加帧率控制与稳定的循环逻辑

修改GamePanel的run()方法,加入固定帧率(比如60fps)的控制,让每帧的间隔稳定在~16ms左右:

private val targetFps = 60.0
private val targetFrameTime = 1.0 / targetFps // 每帧目标时间(秒)

override fun run() {
    setup()
    var lastTime = now()
    var accumulator = 0.0

    while (!Thread.currentThread().isInterrupted) {
        val currentTime = now()
        val deltaTime = currentTime - lastTime
        lastTime = currentTime

        // 累计时间,确保状态更新的步长稳定
        accumulator += deltaTime
        while (accumulator >= targetFrameTime) {
            updateState(targetFrameTime)
            accumulator -= targetFrameTime
        }

        // 渲染
        repaint()

        // 计算需要睡眠的时间,避免CPU空转
        val sleepTime = (targetFrameTime - (now() - currentTime)) * 1000
        if (sleepTime > 0) {
            try {
                Thread.sleep(sleepTime.toLong())
            } catch (e: InterruptedException) {
                Thread.currentThread().interrupt()
            }
        }
    }
}

这里用了固定时间步长的更新方式:不管渲染速度如何,状态更新的间隔是固定的,这样能保证游戏逻辑的稳定性,同时通过睡眠控制帧率,避免CPU被占满。

3. 修改updateState方法接收时间参数

这样可以让状态更新基于固定的时间步长,避免速度随帧率变化:

private fun updateState(deltaTime: Double) {
    // 后续添加游戏逻辑时,用deltaTime来计算状态变化,比如:
    // position += speed * deltaTime
}

4. 移除错误的g2D.dispose()调用

直接删掉paintComponent里的g2D.dispose(),让Swing自己管理这个Graphics资源:

override fun paintComponent(g: Graphics?) {
    super.paintComponent(g)
    val g2D = g as Graphics2D
    g2D.scale(0.5, 0.5)

    // A square that describes a spiral
    if (firstRendering != null) {
        val t = ((now() - firstRendering!!) * 100).toInt() // 这里调整了时间系数,让速度更合理
        g2D.color = Color.BLACK
        g2D.fillRect(
            1200 + (t * sin(t.toDouble() * PI / 720) / 5).toInt(),
            800 + (t * cos(t.toDouble() * PI / 720) / 5).toInt(),
            200,
            200
        )
    }
    // 删掉这一行:g2D.dispose()
}

5. 优化线程启动的方式

可以给线程设置合适的优先级,让游戏线程获得更高的调度优先级:

private val mainThread = Thread(null, this, "GameThread").apply {
    priority = Thread.NORM_PRIORITY + 1
}

为什么这些改动能解决卡顿?

  • 帧率控制让每帧的渲染间隔稳定,不会因为CPU跑太快导致渲染队列混乱;
  • 纳秒级计时消除了时间误差,保证动画的节奏均匀;
  • 正确的Graphics资源管理避免了渲染过程中的异常波动;
  • 线程睡眠减少了CPU占用,让操作系统的调度更平稳。

按照这些修改调整后,你的方块移动应该会变得丝滑很多,后续添加更多游戏逻辑时也能保持稳定的帧率~

备注:内容来源于stack exchange,提问作者Alphasaft

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 14:27:58