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 解决了这个问题。
所以我有一个我正在尝试制作的游戏,在游戏循环中,我调用 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 解决了这个问题。