java Java面板双缓冲

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2063607/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 19:12:41  来源:igfitidea点击:

Java Panel Double Buffering

javaswingawtdoublebuffered

提问by user250643

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 suff 的初学者,任何帮助都会非常感谢。

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);
 }
}

回答by Adamski

There's a really good tutorial herewhich describes how to use BufferStrategyto produce non-flickering animation.

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

The important points are:

要点是:

  • Call setIgnoreRepaint(true)on the top-level Canvasto prevent AWT from repainting it, as you'll typically be doing this yourself within the animation loop.
  • Obtain the Graphics2Dobject from the BufferStrategy(rather than using the instance passed in via paintComponent(Graphics g).
  • 调用setIgnoreRepaint(true)顶层Canvas以防止 AWT 重新绘制它,因为您通常会在动画循环中自己执行此操作。
  • Graphics2DBufferStrategy(而不是使用通过传入的实例)获取对象paintComponent(Graphics g)

回答by helpermethod

A must-read about the painting mechanism in AWT and Swing

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

Painting in AWT and Swing

在 AWT 和 Swing 中绘画

回答by MadProgrammer

The basic problem is, you're violating the basic painting system of Swing. Swing uses a "passive rendering" algorithm, where paints are performed only when they need to be. You can make suggestions to the API about when something should be update via the repaintcall.

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

Based on your code, the basic problem is, you're calling paintComponentswith your own Graphicscontext, 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.

根据您的代码,基本问题是,您正在paintComponents使用自己的Graphics上下文进行调用,但是系统正在使用它的油漆油漆传递来破坏它,您正在与油漆系统作斗争而不是使用它。

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 stronglyrecommend 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();
        }
    }
}
  • Swing is NOT thread safe, you should NOT be updating the UI from outside the context of the Event Dispatching Thread
  • You should NEVER call any paintmethod directly. The system will perform this operation when it wants to update your component.
  • Swing 不是线程安全的,您不应该从事件调度线程的上下文之外更新 UI
  • 你永远不应该paint直接调用任何方法。当系统要更新您的组件时,系统会执行此操作。

I would strongly recommend having a read of:

我强烈建议阅读以下内容:

For example...

例如...

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

BufferedStrategy

Has already been suggested, BufferStrategyis a viable solution in cases where you want to take complete control off the painting system. It's more complex, but gets you around the oddities of the passive rendering system used by Swing

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

回答by dylnmc

I think you can just call super(true);first thing, and this just tells the JPanelthat it is double buffered... because one of JPanel's constructors is:

我认为你可以调用super(true);第一件事,这只是告诉JPanel它它是双缓冲的......因为其中一个JPanel的构造函数是:

public Jpanel(boolean isDoubleBuffered) {...}

I hope this helps someone even though it is nearly four years later.

我希望这对某人有所帮助,即使是在将近四年之后。