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

时间:2015-08-29 02:23:08

标签: java multithreading swing loops jpanel

所以我有一个我试图制作的游戏,在游戏循环中,我称之为Thread.sleep()。否则,我有代码在调整大小时保持窗口的宽高比。这很有效,除了在我调整大小时我得到奇怪的闪烁。我把问题缩小到了Thread.sleep(),当我把这一行拿出来时,我的程序运行正常,但是这会导致CPU飙升到如此高,以至于在我的Macbook上,Activity Monitor应用程序说我的游戏使用170 +%!现在这是有问题的,而且无论如何我都把睡眠线放在那里。我听说睡在事件调度线程上会导致这种效果,但是我在一个新线程中运行这个循环,所以我觉得我很好。你们知道会发生什么吗?这是源代码的一部分(你真的需要查看run()方法):

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子类中,运行此循环的代码(循环变量是上述类的实例):

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

    addKeyListener(this);
    addMouseListener(this);

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

如果你们能帮助我,我会喜欢它,我真的很难过。谢谢!

3 个答案:

答案 0 :(得分:0)

您应该使用SwingWorker而不是Thread来异步操作Swing组件。当我发现这个家伙时,我的生活发生了变化=)。 SwingWorker为您提供了一个方法“进程”,您可以使用它来逐步执行操作,并使用“完成”方法来完成处理,这两种方法都可以安全地处理事件派发线程。您应该在“doInBackground”上进行后台处理。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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