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

如何解决Java中Flappy Bird类游戏Delta游戏循环的卡顿问题?

卡顿原因分析及解决方案

核心问题

  1. 线程违规操作UI:Swing的所有组件更新必须在**事件调度线程(EDT)**中执行,你的游戏循环在主线程直接调用ufo.fall()修改UI组件位置,这会导致线程冲突,引发卡顿甚至界面无响应。
  2. 无节制的死循环占用CPU:runLoop()是一个无限循环,没有任何线程休眠或让步操作,会持续占用CPU资源,随着时间推移,系统资源耗尽导致卡顿加剧。
  3. 缺少UI重绘触发:修改组件位置后,没有主动触发界面重绘,导致界面更新不及时。

修复后的代码

主类修改

import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Main implements KeyListener {

    static Player ufo = new Player(100, 100, 40, 50, Color.BLUE);
    private static long fps = 30;
    public static boolean jumpbool;  

    public static void main(String[] args) {
        // 确保UI初始化在EDT中执行
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();
            frame.setSize(800, 600); // 缩小窗口尺寸,避免过大导致渲染压力
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setResizable(true);
            frame.setLayout(null);

            JPanel panel = new JPanel();
            panel.setSize(800, 600);
            panel.setLayout(null);
            panel.setBackground(Color.darkGray);

            panel.add(ufo);
            frame.add(panel);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true); 
            frame.addKeyListener(new Main());
        });

        // 启动独立的游戏线程
        new Thread(Main::runLoop).start();
    }

    public static void runLoop() {
        double drawInterval = 1000000000.0 / fps;
        double delta = 0; 
        long lastTime = System.nanoTime();
        long currentTime;

        while (true) {
            currentTime = System.nanoTime();
            delta += (currentTime - lastTime) / drawInterval; 
            lastTime = currentTime;

            if (delta >= 1) {
                // 使用EDT更新UI
                SwingUtilities.invokeLater(() -> {
                    ufo.fall();
                    ufo.getParent().repaint(); // 触发父容器重绘
                });
                delta--;
            }

            // 让线程短暂休眠,释放CPU资源
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {}

    @Override
    public void keyPressed(KeyEvent e) {}

    @Override
    public void keyReleased(KeyEvent e) {}
}

Player类代码(保持原样即可)

import java.awt.Color;
import javax.swing.JLabel;

public class Player extends JLabel {

    int x,y,width,height;
    Color color; 

    Player(int x, int y, int width, int height, Color color){
        this.setBounds(x, y, width, height);
        this.setBackground(color);
        this.setOpaque(true);
    }

    public void fall() {
        this.setLocation(this.getX(), this.getY() + 7);
    }
}

关键修复点说明

  • UI操作移至EDT:用SwingUtilities.invokeLater()包裹所有UI更新代码,确保Swing组件在正确的线程中修改。
  • 独立游戏线程:将runLoop()放在单独的线程中执行,避免阻塞主线程(EDT)。
  • 添加线程休眠:在循环中加入Thread.sleep(1),让线程主动释放CPU,降低资源占用。
  • 触发重绘:修改位置后调用getParent().repaint()强制界面更新,确保组件位置变化及时显示。
  • 缩小窗口尺寸:原窗口4000x2000过大,会增加渲染负担,调整为合理尺寸减少卡顿。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 16:01:09