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

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;
        }
    }
}

活动监视器截图

活动监视器截图1
活动监视器截图2

卡顿原因及修复方案

  1. 错误释放绘图资源:paintComponent中调用g2.dispose()会提前释放Swing提供的绘图上下文,导致渲染过程出现资源异常,引发卡顿。直接移除该行代码即可。
  2. 共享变量线程可见性缺失:playerX和playerY在游戏线程(更新逻辑)与EDT事件调度线程(渲染逻辑)之间共享,未添加可见性修饰可能导致EDT无法及时获取最新坐标,出现视觉延迟。给这两个变量加上volatile修饰符,确保线程间变量可见性。
  3. 可选优化:替换KeyListener为KeyBindings:KeyListener对组件焦点依赖强,偶尔会出现按键响应不及时的情况,改用Swing的KeyBindings可以更稳定地处理键盘输入,进一步提升流畅度。

修改后重新运行游戏,方块移动流畅度会明显提升,卡顿现象消失,控制台输出的FPS值也会稳定在60左右。

内容的提问来源于stack exchange,提问作者Spektra Lami

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 06:12:08