Java Thread.sleep() 导致 JFrame 在调整大小时闪烁

Java Thread.sleep() causing JFrame to flicker when resized

所以我有一个我正在尝试制作的游戏,在游戏循环中,我调用 Thread.sleep()。在其他地方,我有代码在调整大小时保持 window 的纵横比。这很好用,只是在调整大小时出现奇怪的闪烁。我已将问题缩小到 Thread.sleep(),当我删除这一行时,我的程序按预期工作,但这会导致 CPU 飙升到如此之高,以至于在我的 Macbook 上,Activity 监控应用程序说我的游戏使用了 170+%!现在这是有问题的,这正是我无论如何都要把睡眠线放在那里的原因。我听说在事件派发线程上休眠会导致这种效果,但我 运行 在新线程中执行此循环,所以我认为我很好。你们知道会发生什么吗?这是源代码的一部分(你真的需要看看 运行() 方法):

package jeffrey_ryan.game2d;

public class GameLoop implements Runnable {
    private boolean running = false;
    private boolean paused = false;
    private float gameHertz = 30.0f;
    private long timeBetweenUpdates = (long) (1_000_000_000 / gameHertz);
    private int maxUpdates = 5;
    private LoopListener loopListener;

    public void run() {
        long lastUpdateTime = System.nanoTime();
        running = true;

        while (running) {
            long now = System.nanoTime();
            if (!paused) {
                int updates = 0;

                while (now - lastUpdateTime >= timeBetweenUpdates && updates < maxUpdates) {
                    if (loopListener != null) {
                        loopListener.update((double) timeBetweenUpdates / 1_000_000_000);
                    }

                    lastUpdateTime += timeBetweenUpdates;
                    updates++;
                }

                if (loopListener != null) {
                    float interpolation = Math.min(1.0f, (float) (now - lastUpdateTime) / timeBetweenUpdates);
                    loopListener.render(interpolation);
                }

                long timeRemaining = (timeBetweenUpdates - (now - lastUpdateTime)) / 1_000_000;

                try {
                    Thread.sleep(Math.max(timeRemaining - 5, 0)); // HERE'S THE OFFENDING LINE ******************
                }
                catch (InterruptedException ie) {
                    ie.printStackTrace();
                }
            }
            else {
                try {
                    Thread.sleep(25);
                }
                catch (InterruptedException ie) {
                    ie.printStackTrace();
                }
            }
        }
    }

    public void start() {
        running = true;
    }

    public void stop() {
        running = false;
    }

    public void pause() {
        paused = true;
    }

    public void play() {
        paused = false;
    }

    public float getSpeed() {
        return gameHertz;
    }

    public void setSpeed(float hertz) {
        gameHertz = hertz;
        timeBetweenUpdates = (long) (1_000_000_000 / gameHertz);
    }

    public int getMaxUpdates() {
        return maxUpdates;
    }

    public void setMaxUpdates(int updates) { 
        maxUpdates = updates;
    }

    public void setLoopListener(LoopListener listener) {
        loopListener = listener;
    }
}

在我的 JPanel 子class 中,这里是 运行 这个循环的代码(其中循环变量是上述 class 的一个实例):

@Override
public void addNotify() {
    super.addNotify();

    addKeyListener(this);
    addMouseListener(this);

    Thread thread = new Thread(loop, "GameLoop");
    thread.start();
}

如果你们能帮助我,我会很高兴,我真的很难过。谢谢!

您应该使用 SwingWorker 而不是线程来异步操作 Swing 组件。当我发现这个人时,我的生活改变了 =)。 SwingWorker 为您提供了一个方法 "process" ,您可以使用它来逐渐执行操作,并提供一个 "done" 方法来完成您的处理,这两种方法都可以安全地处理事件调度线程。您应该在 "doInBackground".

上进行的后台进程

调用 'Thread.sleep(n)' 会导致整个线程变得无响应,如果此线程绑定到您的 JFrame 线程,那么该线程也会变得无响应并导致整个框架和组件冻结并停止响应 - 可能闪烁的原因。所以确保睡眠是在游戏循环中而不是在帧上,一种方法是在初始化时创建两个线程,一个用于帧,另一个用于逻辑,然后让游戏循环处理输入和输出,而显示线程只是显示(我相信这是大多数游戏引擎的工作方式)。还要确保任何线程都没有链接,否则休眠线程会影响显示线程。

我找到了问题的答案。调用循环的 class 是一个 JPanel,在调整大小时没有重新绘制,只有在循环告诉它重新绘制时才会重新绘制,这导致了 JPanel 也没有绘制的某些时期。我通过重写 paintComponent 解决了这个问题。