Java Swing游戏移动画面卡顿,请求排查问题原因
Java Swing游戏移动方块卡顿问题修复
问题现象
开发的Java Swing游戏中,白色方块移动时出现明显卡顿,代码逻辑简单,硬件性能足够,系统已更新至最新版本,有演示视频显示卡顿现象,活动监视器截图显示程序运行状态正常。
问题代码
GamePanel类
import javax.swing.*; import java.awt.*; public class GamePanel extends JPanel implements Runnable { // SCREEN SETTINGS final int originalTileSize = 16; //16x16 tile. final int scale = 3; final int tileSize = originalTileSize * scale; // 48x48 final int maxScreenCol = 16; final int maxScreenRow = 12; final int screenWidth = tileSize * maxScreenCol; //768 px. final int screenHeight = tileSize * maxScreenRow; // 576 px; //FPS int FPS = 60; KeyHandler keyH = new KeyHandler(); Thread gameThread; //set player's default position. int playerX = 100; int playerY = 100; int playerSpeed = 4; public GamePanel() { this.setPreferredSize(new Dimension(screenWidth, screenHeight)); this.setBackground(Color.BLACK); this.setDoubleBuffered(true); this.addKeyListener(keyH); this.setFocusable(true); } public void startGameThread() { gameThread = new Thread(this); gameThread.start(); } public void run() { double drawInterval = 1_000_000_000.0 / FPS; // 0.01666 seconds. double delta = 0; long currentTime; long timer = 0; int drawCount = 0; long lastTime = System.nanoTime(); while (gameThread != null) { currentTime = System.nanoTime(); delta += (currentTime - lastTime) / drawInterval; timer += (currentTime - lastTime); lastTime = currentTime; if (delta >= 1) { update(); repaint(); delta--; drawCount++; } if (timer >= 1_000_000_000) { System.out.println("FPS: " + drawCount); drawCount = 0; timer = 0; } } } public void update() { if (keyH.upPressed) { playerY -= playerSpeed; } else if (keyH.downPressed) { playerY += playerSpeed; } else if (keyH.leftPressed) { playerX -= playerSpeed; } else if (keyH.rightPressed) { playerX += playerSpeed; } } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; g2.setColor(Color.white); g2.fillRect(playerX, playerY, tileSize, tileSize); g2.dispose(); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> { JFrame window = new JFrame(); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setResizable(false); window.setTitle("2D Adventure"); GamePanel gamePanel = new GamePanel(); window.add(gamePanel); window.pack(); window.setVisible(true); gamePanel.startGameThread(); }); } }
KeyHandler类
import java.awt.event.KeyEvent; import java.awt.event.KeyListener; public class KeyHandler implements KeyListener { public boolean upPressed, downPressed, leftPressed, rightPressed; @Override public void keyTyped(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { int code = e.getKeyCode(); if (code == KeyEvent.VK_W) { upPressed = true; } if (code == KeyEvent.VK_S) { downPressed = true; } if (code == KeyEvent.VK_A) { leftPressed = true; } if (code == KeyEvent.VK_D) { rightPressed = true; } } @Override public void keyReleased(KeyEvent e) { int code = e.getKeyCode(); if (code == KeyEvent.VK_W) { upPressed = false; } if (code == KeyEvent.VK_S) { downPressed = false; } if (code == KeyEvent.VK_A) { leftPressed = false; } if (code == KeyEvent.VK_D) { rightPressed = false; } } }
活动监视器截图


卡顿原因及修复方案
- 错误释放绘图资源:
paintComponent中调用g2.dispose()会提前释放Swing提供的绘图上下文,导致渲染过程出现资源异常,引发卡顿。直接移除该行代码即可。 - 共享变量线程可见性缺失:
playerX和playerY在游戏线程(更新逻辑)与EDT事件调度线程(渲染逻辑)之间共享,未添加可见性修饰可能导致EDT无法及时获取最新坐标,出现视觉延迟。给这两个变量加上volatile修饰符,确保线程间变量可见性。 - 可选优化:替换KeyListener为KeyBindings:KeyListener对组件焦点依赖强,偶尔会出现按键响应不及时的情况,改用Swing的KeyBindings可以更稳定地处理键盘输入,进一步提升流畅度。
修改后重新运行游戏,方块移动流畅度会明显提升,卡顿现象消失,控制台输出的FPS值也会稳定在60左右。
内容的提问来源于stack exchange,提问作者Spektra Lami
相关产品推荐
相关产品推荐

