如何创建无闪烁 Java 游戏循环?
How do you create a flicker free Java game loop?
我一直在尝试创建一个 Java 游戏循环来显示一些简单的开始,但无论我尝试什么我都无法让它停止闪烁。我已经尝试使用谷歌搜索解决方案,但要么我的 google-fu 不完全符合要求,要么我做错了一些严重的事情。
我将这段代码发布在这里,希望我只是做了一些可以纠正的错误。在这一点上,我很想用另一种语言重新开始,上次我使用它时 SFML 很好。为可怕的代码道歉。
Main.java:
public class Main {
public static void main(String[] args) {
new GameFrame("Game");
}
}
GameFrame.java:
import java.awt.Color;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Toolkit;
import javax.swing.JFrame;
public class GameFrame extends JFrame {
protected GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
public GameFrame(String title) {
super(title);
init();
}
public void init() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
// Calculate the size of the window, taking into account the size of toolbars etc.
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(device.getDefaultConfiguration());
setSize(
(int) ((device.getDefaultConfiguration().getBounds().width - insets.left - insets.right) * 0.8),
(int) ((device.getDefaultConfiguration().getBounds().height - insets.top - insets.bottom) * 0.8)
);
// Centre and start maximised.
setLocationRelativeTo(null);
setExtendedState(MAXIMIZED_BOTH);
if(false) {
// Important otherwise you get an ugly border.
super.setVisible(false);
setUndecorated(true);
device.setFullScreenWindow(this);
} else {
setUndecorated(false);
device.setFullScreenWindow(null);
super.setVisible(true);
}
GamePanel panel = new GamePanel();
panel.setBackground(Color.BLACK);
new Thread(panel).start();
add(panel);
}
}
GamePanel.java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import javax.swing.JPanel;
public class GamePanel extends JPanel implements Runnable {
protected boolean running = false;
protected long lastTime;
protected int width = 0;
protected int height = 0;
protected Image image;
protected Graphics2D graphics;
protected double x = 0;
protected double y = 0;
protected void updateState(double delta) {
x = x + 0.00001 * delta;
y = y + 0.00001 * delta;
}
public void run() {
running = true;
lastTime = System.nanoTime();
while(width == 0 || height == 0) {
width = getBounds().width;
height = getBounds().height;
}
setDoubleBuffered(true);
while(running) {
long now = System.nanoTime();
long updateLength = now - lastTime;
double delta = updateLength / (10 ^ 9 / 60);
updateState(delta);
paintComponent(getGraphics());
lastTime = System.nanoTime();
try {Thread.sleep((now - lastTime + (10 ^ 9 / 60)) / (10 ^ 6));} catch (Exception e) {}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if(isVisible() && width > 0 && height > 0) {
setDoubleBuffered(true);
if(image == null) {
image = createImage(width, height);
graphics = (Graphics2D) image.getGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
graphics.setColor(Color.black);
graphics.fillRect(0, 0, width, height);
graphics.setColor(Color.WHITE);
graphics.fillRect(100, 20, 100, 100);
graphics.fillOval((int) x, (int) y, 30, 30);
g.drawImage(image, 0, 0, this);
}
}
}
您的程序片段在几个方面是不正确的:
Swing GUI 对象应该在 event dispatch thread.
上 仅 构建和操作
不要使用getGraphics()
;图形上下文 仅 在 paintComponent()
.
中有效
JPanel
默认是双缓冲的。
使用javax.swing.Timer
to pace the animation; a complete example is seen in this AnimationTest
的实例。
我一直在尝试创建一个 Java 游戏循环来显示一些简单的开始,但无论我尝试什么我都无法让它停止闪烁。我已经尝试使用谷歌搜索解决方案,但要么我的 google-fu 不完全符合要求,要么我做错了一些严重的事情。
我将这段代码发布在这里,希望我只是做了一些可以纠正的错误。在这一点上,我很想用另一种语言重新开始,上次我使用它时 SFML 很好。为可怕的代码道歉。
Main.java:
public class Main {
public static void main(String[] args) {
new GameFrame("Game");
}
}
GameFrame.java:
import java.awt.Color;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Toolkit;
import javax.swing.JFrame;
public class GameFrame extends JFrame {
protected GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
public GameFrame(String title) {
super(title);
init();
}
public void init() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
// Calculate the size of the window, taking into account the size of toolbars etc.
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(device.getDefaultConfiguration());
setSize(
(int) ((device.getDefaultConfiguration().getBounds().width - insets.left - insets.right) * 0.8),
(int) ((device.getDefaultConfiguration().getBounds().height - insets.top - insets.bottom) * 0.8)
);
// Centre and start maximised.
setLocationRelativeTo(null);
setExtendedState(MAXIMIZED_BOTH);
if(false) {
// Important otherwise you get an ugly border.
super.setVisible(false);
setUndecorated(true);
device.setFullScreenWindow(this);
} else {
setUndecorated(false);
device.setFullScreenWindow(null);
super.setVisible(true);
}
GamePanel panel = new GamePanel();
panel.setBackground(Color.BLACK);
new Thread(panel).start();
add(panel);
}
}
GamePanel.java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import javax.swing.JPanel;
public class GamePanel extends JPanel implements Runnable {
protected boolean running = false;
protected long lastTime;
protected int width = 0;
protected int height = 0;
protected Image image;
protected Graphics2D graphics;
protected double x = 0;
protected double y = 0;
protected void updateState(double delta) {
x = x + 0.00001 * delta;
y = y + 0.00001 * delta;
}
public void run() {
running = true;
lastTime = System.nanoTime();
while(width == 0 || height == 0) {
width = getBounds().width;
height = getBounds().height;
}
setDoubleBuffered(true);
while(running) {
long now = System.nanoTime();
long updateLength = now - lastTime;
double delta = updateLength / (10 ^ 9 / 60);
updateState(delta);
paintComponent(getGraphics());
lastTime = System.nanoTime();
try {Thread.sleep((now - lastTime + (10 ^ 9 / 60)) / (10 ^ 6));} catch (Exception e) {}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if(isVisible() && width > 0 && height > 0) {
setDoubleBuffered(true);
if(image == null) {
image = createImage(width, height);
graphics = (Graphics2D) image.getGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
graphics.setColor(Color.black);
graphics.fillRect(0, 0, width, height);
graphics.setColor(Color.WHITE);
graphics.fillRect(100, 20, 100, 100);
graphics.fillOval((int) x, (int) y, 30, 30);
g.drawImage(image, 0, 0, this);
}
}
}
您的程序片段在几个方面是不正确的:
Swing GUI 对象应该在 event dispatch thread.
上 仅 构建和操作
不要使用
getGraphics()
;图形上下文 仅 在paintComponent()
. 中有效
JPanel
默认是双缓冲的。使用
javax.swing.Timer
to pace the animation; a complete example is seen in thisAnimationTest
的实例。