Двойная буферизация панели Java - PullRequest
6 голосов
/ 14 января 2010

задавался вопросом, может ли кто-нибудь указать мне правильное направление, я разработал игру понг, и она нуждается в двойной буферизации из-за мерцания. Iv попробовал кое-что из поста здесь, чтобы попытаться заставить его работать, но я все еще новичок с достаточным колебанием, любая помощь будет удивительной, спасибо.

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

Ответы [ 4 ]

6 голосов
/ 14 января 2010

Здесь действительно хороший учебник здесь , в котором описано, как использовать BufferStrategy для создания немерцающей анимации.

Важными моментами являются:

  • Вызовите setIgnoreRepaint(true) на верхнем уровне Canvas, чтобы предотвратить перекрашивание AWT, как вы обычно делаете это самостоятельно в цикле анимации.
  • Получить объект Graphics2D из BufferStrategy (вместо использования экземпляра, переданного через paintComponent(Graphics g).
3 голосов
/ 14 января 2010

Обязательно прочитайте о механизме окраски в AWT и Swing

Покраска в AWT и Swing

2 голосов
/ 17 мая 2018

Основная проблема в том, что вы нарушаете базовую систему рисования Swing.Swing использует алгоритм «пассивного рендеринга», где рисование выполняется только тогда, когда это необходимо.Вы можете вносить предложения в API о том, когда что-то должно быть обновлено с помощью вызова repaint.

Основываясь на вашем коде, основная проблема заключается в том, что вы звоните paintComponents с вашим собственным Graphics контекстом, но система затем удаляет его с помощью прохода краски, вы боретесь с системой рисования, а не работаете с ней.

Если все сделано правильно, компоненты Swing уже имеют двойную буферизацию, поэтому вам не нужночтобы сделать что-то «лишнее», кроме реальной работы с API / системой.

Я настоятельно рекомендую взглянуть на:

, чтобы лучше понять, как рисование работает в 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 НЕ является потокобезопасным, вы НЕ должны обновлять пользовательский интерфейс вне контекста нити диспетчеризации событий
  • Вы не должны НИКОГДАвызовите любой метод paint напрямую.Система выполнит эту операцию, когда захочет обновить ваш компонент.

Я настоятельно рекомендую прочитать:

Например ...

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

0 голосов
/ 24 июня 2014

Я думаю, вы можете просто позвонить super(true); Во-первых, это просто говорит JPanel, что он с двойной буферизацией ... потому что один из конструкторов JPanel:

public Jpanel(boolean isDoubleBuffered) {...}

Надеюсь, это кому-нибудь поможет, хотя прошло почти четыре года.

...