[英]Java Panel Double Buffering
wondered if anyone could point me in the right directon, i have developed a pong game and it needs double buffering due to flickering. 想知道是否有人能指出我正确的指挥,我已经开发了一个乒乓球游戏,它需要双重缓冲由于闪烁。 Iv tryed some of the post on here to try and make it work, but im still a beginner with the swing awt suff, any help would be amazing thanks.
我在这里尝试了一些帖子尝试使它工作,但我仍然是一个摇摆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);
}
}
There's a really good tutorial here which describes how to use BufferStrategy to produce non-flickering animation. 这里有一个非常好的教程,它描述了如何使用BufferStrategy生成非闪烁动画。
The important points are: 重点是:
setIgnoreRepaint(true)
on the top-level Canvas
to prevent AWT from repainting it, as you'll typically be doing this yourself within the animation loop. Canvas
上调用setIgnoreRepaint(true)
以防止AWT重新绘制它,因为您通常会在动画循环中自己执行此操作。 Graphics2D
object from the BufferStrategy
(rather than using the instance passed in via paintComponent(Graphics g)
. BufferStrategy
获取Graphics2D
对象(而不是使用通过paintComponent(Graphics g)
传递的实例paintComponent(Graphics g)
。 A must-read about the painting mechanism in AWT and Swing 关于AWT和Swing中绘画机制的必读书
The basic problem is, you're violating the basic painting system of Swing. 基本问题是,你违反了Swing的基本绘画系统。 Swing uses a "passive rendering" algorithm, where paints are performed only when they need to be.
Swing使用“被动渲染”算法,只有在需要时才执行绘制。 You can make suggestions to the API about when something should be update via the
repaint
call. 您可以向API提出有关何时应通过
repaint
调用更新内容的建议。
Based on your code, the basic problem is, you're calling paintComponents
with your own Graphics
context, but the system is is then trashing it with it's paint paint pass, you are fighting the paint system rather then working with it. 基于你的代码,基本的问题是,你正在使用自己的
Graphics
上下文调用paintComponents
,但是系统正在使用它的paint paint pass废弃它,你正在与绘制系统作斗争而不是使用它。
If done correctly, Swing components are already double buffered, so you don't need to do anything "extra", other then actual work with the API/system. 如果正确完成,Swing组件已经是双缓冲的,因此除了实际使用API /系统之外,您不需要做任何“额外”操作。
I strongly recommend having a look at: 我强烈建议您查看:
to get a better understanding of how painting works in Swing. 更好地了解Swing中绘画的工作原理。
So, let's start with... 那么,让我们从......开始吧
@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();
}
}
}
paint
method directly. paint
方法。 The system will perform this operation when it wants to update your component. I would strongly recommend having a read of: 我强烈建议阅读:
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
Has already been suggested, BufferStrategy
is a viable solution in cases where you want to take complete control off the painting system. 已经有人建议,如果您希望完全控制喷涂系统,
BufferStrategy
是一个可行的解决方案。 It's more complex, but gets you around the oddities of the passive rendering system used by Swing 它更复杂,但可以让你了解Swing使用的被动渲染系统的奇怪之处
I think you can just call super(true);
我想你可以称之为
super(true);
first thing, and this just tells the JPanel
that it is double buffered... because one of JPanel
's constructors is: 第一件事,这只是告诉
JPanel
它是双缓冲的...因为JPanel
的构造函数之一是:
public Jpanel(boolean isDoubleBuffered) {...}
I hope this helps someone even though it is nearly four years later. 我希望这可以帮助某人,即使它已经将近四年了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.