使用Swing开发游戏时出现轻微卡顿问题求助
Swing开发游戏时出现轻微卡顿问题求助
嘿,完全懂你这种“眼里容不下一点卡顿”的执念——这么简单的动画要是不丝滑,确实难受!咱们来拆解下你代码里的问题,把这个卡顿解决掉:
你的代码里导致卡顿的核心问题
- 无节制的循环占用CPU:你的
run()方法里是while(true)死循环,没有任何帧率控制,线程会疯狂抢占CPU资源,导致操作系统的线程调度出现波动,进而让渲染节奏不稳定,出现细微卡顿。 - 计时精度不足:用
Date().time获取时间的精度是毫秒级,对于60fps的动画来说,每帧间隔只有~16ms,毫秒级的计时误差很容易导致帧间隔不稳定,出现卡顿感。 - 错误的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
相关产品推荐
相关产品推荐

