BufferStrategy 不解决闪烁
BufferStrategy don't solve flickering
我正在尝试了解 BufferStrategy
的工作原理。我制作了一个简单的应用程序,它以每秒 60 帧的速度一遍又一遍地绘制一些 Sprite
对象。我可以在 canvas 上看到图像,但由于某种原因它们在闪烁。你能告诉我为什么吗?如果您不想阅读所有代码,只需关注 Paint 方法和主游戏循环。
public abstract class Frame extends JFrame implements KeyListener {
private static final long serialVersionUID = 1L;
//------------------------Variables------------------------//
private boolean initialized = false;
private boolean fullScreen = false;
public boolean running = true;
private GraphicsDevice vc;
private BufferStrategy strategy;
private Graphics2D g2d;
private int timer = 0;
//------------------------Variables------------------------//
public final void __init__() {
this.addKeyListener(this); //Adding key listener.
this.setVisible(true);
this.setIgnoreRepaint(true);
this.createBufferStrategy(2);
this.strategy = this.getBufferStrategy();
this.setResizable(false);
this.initialized = true; //Initialized.
}
//Create a window.
public final void set_mode(int width, int height, boolean fullScreen) throws NotInitializedException {
//Frame not initialized.
if (!this.initialized) {
throw new NotInitializedException("Frame not initialized!");
} else {
//--------------------Variables--------------------//
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
//--------------------Variables--------------------//
//Setting vc equal to the default graphics device of the system.
this.vc = env.getDefaultScreenDevice();
//Full Screen.
if (fullScreen) {
this.fullScreen = fullScreen;
//Creating the display mode.
DisplayMode mode = new DisplayMode(width, height, 32, DisplayMode.REFRESH_RATE_UNKNOWN);
//If display settings are allow to change display mode.
if (this.vc.isDisplayChangeSupported()) {
this.vc.setDisplayMode(mode); //Change to the new mode.
}
//Set the screen to full screen.
this.vc.setFullScreenWindow(this);
} //Not full screen.
else {
this.setSize(width, height);
this.addWindowListener(new WindowHandler(this));
}
}
}
//This mehod is been called from Sprite.draw() method.
public void paint(Sprite sprite) {
do {
do {
this.g2d = (Graphics2D) this.strategy.getDrawGraphics();
g2d.drawImage(sprite.getImage(), sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight(), null);
this.g2d.dispose();
} while (this.strategy.contentsRestored());
this.strategy.show();
} while (this.strategy.contentsLost());
}
public final int tick(int fps) {
int ms = 1000 / fps;
timer += 1;
//Try to sleep.
try {
Thread.sleep(ms);
} //Thread interrupted.
catch (Exception e) {
System.out.println(e.getMessage());
System.exit(0);
}
return timer;
}
public class MyApp extends Frame {
public static String BG_PATH = "C:/Users/admin/Desktop/game/bg.jpg";
public static String PL_PATH = "C:/Users/admin/Desktop/game/player.png";
public static String EN_PATH = "C:/Users/admin/Desktop/game/enemy.png";
private int speed = 20;
private boolean left = false;
private boolean right = false;
@Override
public void keyPressed(KeyEvent arg0) {
// TODO Auto-generated method stub
if (arg0.getKeyCode() == KeyEvent.VK_LEFT) {
this.left = true;
} else if (arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
this.right = true;
} else if (arg0.getKeyCode() == KeyEvent.VK_ESCAPE) {
this.close();
}
}
@Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
if (arg0.getKeyCode() == KeyEvent.VK_LEFT) {
this.left = false;
} else if (arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
this.right = false;
}
}
@Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void onWindowClose() {
// TODO Auto-generated method stub
}
//This method starts the game.
public void startApp() {
this.__init__(); //initialize the frame.
Sprite bg = new Sprite(this, Picture.load(BG_PATH), "bg"); //Create a new sprite obj
this.set_mode(bg.getWidth() - 500, bg.getHeight() - 100, false); //Create the window.
Sprite player = new Sprite(this, Picture.load(PL_PATH), "player");
player.setX(bg.getWidth() / 3);
player.setY(bg.getHeight() / 2);
//Game Main Loop
while (this.running) {
bg.draw();
player.draw();
player.moveHorizontal(left, right, speed); //Dont worry about this line.
this.tick(50);
}
}
}
}
你有几个我可以清楚地发现的问题。
首先,您必须了解 Swing/Awt 中的绘图并不以速度着称,实际上恰恰相反。事实是,即使您告诉您的游戏以 60fps 的速度 运行,它也可能无法做到。因此闪烁。从本质上讲,您的应用程序处于 "drawing-data race" 范围内,并且总是稍稍落后。快速尝试一些东西;将 Thead.Sleep() 设置为 10 或 30。我觉得这可能会完全解决您的问题。
如果不行,再考虑第二个问题。您在 player.Draw(); 内调用 this.strategy.show();
;功能,当它需要成为您在绘图调用中做的最后一件事时。换句话说:
//Game Main Loop
while (this.running) {
bg.draw(); // DON'T SWAP BUFFERS!
player.draw(); // DON'T SWAP BUFFERS!
// draw other entities
player.moveHorizontal(left, right, speed); //Dont worry about this line.
this.tick(50);
this.strategy.show(); // This needs to be the very last thing you do. You're swapping buffers here, which only needs to be done once per frame.
}
我猜你也在 bg.Draw(); 期间交换缓冲区;功能也一样,这实际上就是您的屏幕闪烁的原因。 所以这是两件事。尝试将每秒帧数降低到 Java 实际可以处理的水平,并且在绘图例程结束之前不要交换缓冲区。
其他一些建议:
使用直接变量访问而不是 getter 和 setter。调用 "player.getX()" 时有开销。当你可以打电话给 "player.x".
Java2D游戏开发没有前途。 Swing/AWT(你正在使用的)完全死了。如果你想制作游戏并认真对待它,请花时间学习 OpenGL(在你的情况下,它将是 Lwjgl 作为包装器)。
我正在尝试了解 BufferStrategy
的工作原理。我制作了一个简单的应用程序,它以每秒 60 帧的速度一遍又一遍地绘制一些 Sprite
对象。我可以在 canvas 上看到图像,但由于某种原因它们在闪烁。你能告诉我为什么吗?如果您不想阅读所有代码,只需关注 Paint 方法和主游戏循环。
public abstract class Frame extends JFrame implements KeyListener {
private static final long serialVersionUID = 1L;
//------------------------Variables------------------------//
private boolean initialized = false;
private boolean fullScreen = false;
public boolean running = true;
private GraphicsDevice vc;
private BufferStrategy strategy;
private Graphics2D g2d;
private int timer = 0;
//------------------------Variables------------------------//
public final void __init__() {
this.addKeyListener(this); //Adding key listener.
this.setVisible(true);
this.setIgnoreRepaint(true);
this.createBufferStrategy(2);
this.strategy = this.getBufferStrategy();
this.setResizable(false);
this.initialized = true; //Initialized.
}
//Create a window.
public final void set_mode(int width, int height, boolean fullScreen) throws NotInitializedException {
//Frame not initialized.
if (!this.initialized) {
throw new NotInitializedException("Frame not initialized!");
} else {
//--------------------Variables--------------------//
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
//--------------------Variables--------------------//
//Setting vc equal to the default graphics device of the system.
this.vc = env.getDefaultScreenDevice();
//Full Screen.
if (fullScreen) {
this.fullScreen = fullScreen;
//Creating the display mode.
DisplayMode mode = new DisplayMode(width, height, 32, DisplayMode.REFRESH_RATE_UNKNOWN);
//If display settings are allow to change display mode.
if (this.vc.isDisplayChangeSupported()) {
this.vc.setDisplayMode(mode); //Change to the new mode.
}
//Set the screen to full screen.
this.vc.setFullScreenWindow(this);
} //Not full screen.
else {
this.setSize(width, height);
this.addWindowListener(new WindowHandler(this));
}
}
}
//This mehod is been called from Sprite.draw() method.
public void paint(Sprite sprite) {
do {
do {
this.g2d = (Graphics2D) this.strategy.getDrawGraphics();
g2d.drawImage(sprite.getImage(), sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight(), null);
this.g2d.dispose();
} while (this.strategy.contentsRestored());
this.strategy.show();
} while (this.strategy.contentsLost());
}
public final int tick(int fps) {
int ms = 1000 / fps;
timer += 1;
//Try to sleep.
try {
Thread.sleep(ms);
} //Thread interrupted.
catch (Exception e) {
System.out.println(e.getMessage());
System.exit(0);
}
return timer;
}
public class MyApp extends Frame {
public static String BG_PATH = "C:/Users/admin/Desktop/game/bg.jpg";
public static String PL_PATH = "C:/Users/admin/Desktop/game/player.png";
public static String EN_PATH = "C:/Users/admin/Desktop/game/enemy.png";
private int speed = 20;
private boolean left = false;
private boolean right = false;
@Override
public void keyPressed(KeyEvent arg0) {
// TODO Auto-generated method stub
if (arg0.getKeyCode() == KeyEvent.VK_LEFT) {
this.left = true;
} else if (arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
this.right = true;
} else if (arg0.getKeyCode() == KeyEvent.VK_ESCAPE) {
this.close();
}
}
@Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
if (arg0.getKeyCode() == KeyEvent.VK_LEFT) {
this.left = false;
} else if (arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
this.right = false;
}
}
@Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void onWindowClose() {
// TODO Auto-generated method stub
}
//This method starts the game.
public void startApp() {
this.__init__(); //initialize the frame.
Sprite bg = new Sprite(this, Picture.load(BG_PATH), "bg"); //Create a new sprite obj
this.set_mode(bg.getWidth() - 500, bg.getHeight() - 100, false); //Create the window.
Sprite player = new Sprite(this, Picture.load(PL_PATH), "player");
player.setX(bg.getWidth() / 3);
player.setY(bg.getHeight() / 2);
//Game Main Loop
while (this.running) {
bg.draw();
player.draw();
player.moveHorizontal(left, right, speed); //Dont worry about this line.
this.tick(50);
}
}
}
}
你有几个我可以清楚地发现的问题。
首先,您必须了解 Swing/Awt 中的绘图并不以速度着称,实际上恰恰相反。事实是,即使您告诉您的游戏以 60fps 的速度 运行,它也可能无法做到。因此闪烁。从本质上讲,您的应用程序处于 "drawing-data race" 范围内,并且总是稍稍落后。快速尝试一些东西;将 Thead.Sleep() 设置为 10 或 30。我觉得这可能会完全解决您的问题。
如果不行,再考虑第二个问题。您在 player.Draw(); 内调用 this.strategy.show();
;功能,当它需要成为您在绘图调用中做的最后一件事时。换句话说:
//Game Main Loop
while (this.running) {
bg.draw(); // DON'T SWAP BUFFERS!
player.draw(); // DON'T SWAP BUFFERS!
// draw other entities
player.moveHorizontal(left, right, speed); //Dont worry about this line.
this.tick(50);
this.strategy.show(); // This needs to be the very last thing you do. You're swapping buffers here, which only needs to be done once per frame.
}
我猜你也在 bg.Draw(); 期间交换缓冲区;功能也一样,这实际上就是您的屏幕闪烁的原因。 所以这是两件事。尝试将每秒帧数降低到 Java 实际可以处理的水平,并且在绘图例程结束之前不要交换缓冲区。
其他一些建议:
使用直接变量访问而不是 getter 和 setter。调用 "player.getX()" 时有开销。当你可以打电话给 "player.x".
Java2D游戏开发没有前途。 Swing/AWT(你正在使用的)完全死了。如果你想制作游戏并认真对待它,请花时间学习 OpenGL(在你的情况下,它将是 Lwjgl 作为包装器)。