Java面板双缓冲

想知道是否有人能指出我正确的指挥,我已经开发了一个乒乓球游戏,它需要双重缓冲由于闪烁。 我在这里尝试了一些post尝试使它工作,但我仍然是一个摇摆awt足够的初学者,任何帮助将是惊人的谢谢。

public class PongPanel extends JPanel implements Runnable { private int screenWidth = 500; private int screenHeight = 300; private boolean isPaused = false; private boolean isGameOver = false; private int playToPoints = 10; private Padel player1,player2; private Ball ball; private Thread gameThread; private Image dbImage; private Graphics dbg; public PongPanel() { setPreferredSize(new Dimension(screenWidth,screenHeight)); setBackground(Color.BLACK); setDoubleBuffered(true); setFocusable(true); requestFocus(); player1 = new Padel(Position.LEFT,screenWidth,screenHeight); player2 = new Padel(Position.RIGHT,screenWidth,screenHeight); ball = new Ball(10,screenWidth/2,screenHeight/2,Color.WHITE); } public void addNotify(){ super.addNotify(); startGame(); } private void startGame(){ gameThread = new Thread(this); gameThread.start(); } @Override public void run() { while (!isGameOver) { dbImage = createImage(screenWidth,screenHeight); dbg = this.getGraphics(); if(!isPaused){ if(!gameOverCheck()){ updateGame(); paintComponents(dbg); } }else if(isPaused){ dbg.setColor(Color.ORANGE); dbg.setFont(new Font("serif",Font.BOLD,50)); dbg.drawString("Paused", screenWidth/2-82, screenHeight/2); } try { Thread.sleep(30); } catch (InterruptedException e) {e.printStackTrace();} } } private boolean gameOverCheck(){ if(player1.getScore() == playToPoints){ dbg.setColor(player1.getColour()); dbg.setFont(new Font("serif",Font.BOLD,50)); dbg.drawString("Player 1 Wins!", screenWidth/2 - 161, screenHeight/2); setGameOver(true); return true; }else if(player2.getScore() == playToPoints){ dbg.setColor(player2.getColour()); dbg.setFont(new Font("serif",Font.BOLD,50)); dbg.drawString("Player 2 Wins!", screenWidth/2 - 161, screenHeight/2); setGameOver(true); return true; } return false; } private void updateGame(){ ball.move(screenWidth,screenHeight,player1,player2); player1.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY()); player2.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY()); } @Override public void paintComponents(Graphics g) { super.paintComponents(g); dbg.setColor(Color.BLACK); dbg.fillRect(0, 0, screenWidth+20, screenHeight+20); dbg.setColor(Color.WHITE); dbg.drawLine(screenWidth/2, 0, screenWidth/2, screenHeight); dbg.setFont(new Font("serif",Font.BOLD,32)); dbg.drawString(player1.getScore()+"", screenWidth/2-40, screenHeight - 20); dbg.drawString(player2.getScore()+"", screenWidth/2+20, screenHeight - 20); ball.drawBall(dbg); player1.drawPadel(dbg); player2.drawPadel(dbg); } } 

这里有一个非常好的教程,它描述了如何使用BufferStrategy生成非闪烁动画。

重点是:

  • 在顶级Canvas上调用setIgnoreRepaint(true)以防止AWT重新绘制它,因为您通常会在动画循环中自己执行此操作。
  • BufferStrategy获取Graphics2D对象(而不是使用通过paintComponent(Graphics g)传入的实例。

关于AWT和Swing中绘画机制的必读书

在AWT和Swing中绘画

基本问题是,你违反了Swing的基本绘画系统。 Swing使用“被动渲染”算法,只有在需要时才执行绘制。 您可以向API提出有关何时应通过repaint调用更新内容的建议。

基于你的代码,基本的问题是,你用你自己的Graphics上下文调用paintComponents ,但是系统然后用它的paint paint pass来捣毁它,你正在对着paint系统而不是使用它。

如果正确完成,Swing组件已经是双缓冲的,因此除了实际使用API​​ /系统之外,您不需要做任何“额外”操作。

强烈建议您查看:

  • 执行自定义绘画
  • 在AWT和Swing中绘画

更好地了解Swing中绘画的工作原理。

那么,让我们从…开始吧

 @Override public void run() { while (!isGameOver) { dbImage = createImage(screenWidth, screenHeight); dbg = this.getGraphics(); if (!isPaused) { if (!gameOverCheck()) { updateGame(); paintComponents(dbg); } } else if (isPaused) { dbg.setColor(Color.ORANGE); dbg.setFont(new Font("serif", Font.BOLD, 50)); dbg.drawString("Paused", screenWidth / 2 - 82, screenHeight / 2); } try { Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } } } 
  • Swing不是线程安全的,您不应该在Event Dispatching Thread的上下文之外更新UI
  • 你永远不应该直接调用任何paint方法。 系统将在要更新组件时执行此操作。

我强烈建议阅读:

  • Swing中的并发性
  • 如何使用Swing Timers获得可能的解决方案

例如…

 import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.text.Position; public class PongPanel extends JPanel implements Runnable { private int screenWidth = 500; private int screenHeight = 300; private boolean isPaused = false; private boolean isGameOver = false; private int playToPoints = 10; private Padel player1, player2; private Ball ball; private Timer gameThread; public PongPanel() { setPreferredSize(new Dimension(screenWidth, screenHeight)); setBackground(Color.BLACK); setDoubleBuffered(true); setFocusable(true); requestFocus(); player1 = new Padel(Position.LEFT, screenWidth, screenHeight); player2 = new Padel(Position.RIGHT, screenWidth, screenHeight); ball = new Ball(10, screenWidth / 2, screenHeight / 2, Color.WHITE); } public void addNotify() { super.addNotify(); startGame(); } private void startGame() { gameThread = new Timer(30, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateGame(); gameOverCheck(); if (isGameOver) { repaint(); return; } if (!isPaused) { if (!gameOverCheck()) { updateGame(); } } repaint(); } }); gameThread.start(); } private boolean gameOverCheck() { if (player1.getScore() == playToPoints) { setGameOver(true); return true; } else if (player2.getScore() == playToPoints) { setGameOver(true); return true; } return false; } private void updateGame() { ball.move(screenWidth, screenHeight, player1, player2); player1.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY()); player2.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY()); } @Override protected void paintComponent(Graphics g) { super.paintComponents(g); Graphics2D g2d = (Graphics2D) g.create(); g2d.setColor(Color.BLACK); g2d.fillRect(0, 0, screenWidth + 20, screenHeight + 20); g2d.setColor(Color.WHITE); g2d.drawLine(screenWidth / 2, 0, screenWidth / 2, screenHeight); g2d.setFont(new Font("serif", Font.BOLD, 32)); g2d.drawString(player1.getScore() + "", screenWidth / 2 - 40, screenHeight - 20); g2d.drawString(player2.getScore() + "", screenWidth / 2 + 20, screenHeight - 20); ball.drawBall(g2d); player1.drawPadel(g2d); player2.drawPadel(g2d); if (isGameOver) { if (player1.getScore() == playToPoints) { g2d.setColor(player1.getColour()); g2d.setFont(new Font("serif", Font.BOLD, 50)); g2d.drawString("Player 1 Wins!", screenWidth / 2 - 161, screenHeight / 2); } else if (player2.getScore() == playToPoints) { g2d.setColor(player2.getColour()); g2d.setFont(new Font("serif", Font.BOLD, 50)); g2d.drawString("Player 2 Wins!", screenWidth / 2 - 161, screenHeight / 2); } } else if (isPaused) { g2d.setColor(Color.ORANGE); g2d.setFont(new Font("serif", Font.BOLD, 50)); g2d.drawString("Paused", screenWidth / 2 - 82, screenHeight / 2); } g2d.dispose(); } } 

BufferedStrategy

已经有人建议,如果您希望完全控制喷涂系统, BufferStrategy是一个可行的解决方案。 它更复杂,但可以让你了解Swing使用的被动渲染系统的奇特之处

我想你可以称之为super(true); 第一件事,这只是告诉JPanel它是双缓冲的……因为JPanel的构造函数之一是:

 public Jpanel(boolean isDoubleBuffered) {...} 

我希望这可以帮助某人,即使它已经将近四年了。