Как исправить плохой двойной буфер
Я пытался следовать учебнику по двойной буферизации, и я действительно не знаю, что я сделал неправильно. Это работает до того, как я сделал урок, но время от времени мерцание есть. У меня есть два файла Game и gameLoop
Игра:
import java.awt.Graphics;
public class Game extends gameLoop
{
public void init()
{
setSize(854,480);
Thread th = new Thread(this);
th.start();
offscreen = createImage(854,480);
d = offscreen.getGraphics();
}
public void paint(Graphics g)
{
d.clearRect(0, 0, 854, 480);
d.drawImage(disk, x, y, this);
g.drawImage(offscreen , 0, 0, this);
}
public void Update(Graphics gfx)
{
paint(gfx);
}
}
gameLoop
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class gameLoop extends Applet implements Runnable, MouseListener, MouseMotionListener
{
public int x, y, counter, mouseX, mouseY;
public Image offscreen;
public Graphics d;
public boolean up, down, left, right, pressed;
public BufferedImage disk1, disk2, disk3, disk4, disk;
public int ballSpeedX = -6;
public int ballSpeedY = -3;
public void run()
{
x = 400;
y = 200;
try {
disk1 = ImageIO.read(new File("disk1.png"));
disk2 = ImageIO.read(new File("disk2.png"));
disk3 = ImageIO.read(new File("disk3.png"));
disk4 = ImageIO.read(new File("disk4.png"));
} catch (IOException e1) {
e1.printStackTrace();
}
while(true)
{
if(x >= (854 - 150))
{
ballSpeedX = ballSpeedX * -1;
}
if(y >= (480 - 140))
{
ballSpeedY = ballSpeedY * -1;
}
if(y < (0 - 10))
{
ballSpeedY = ballSpeedY * -1;
}
if(x < (0- 10))
{
ballSpeedX = ballSpeedX * -1;
}
x = x + ballSpeedX;
y = y + ballSpeedY;
counter ++;
if(counter >= 4)
counter = 0;
if(counter == 0)
disk = disk1;
if(counter == 1)
disk = disk2;
if(counter == 2)
disk = disk3;
if(counter == 3)
disk = disk4;
System.out.println(counter);
repaint();
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseMoved(MouseEvent m) {}
public void mousePressed(MouseEvent m)
{
}
public void mouseReleased(MouseEvent m)
{
pressed = false;
}
public void mouseDragged(MouseEvent e) {
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
mouseX = (int)b.getX();
mouseY = (int)b.getY();
ballSpeedX = mouseX;
ballSpeedY = mouseY;
}
}
2 ответа
Существует целый ряд различных типов "двойных буферов". Основным является простое изображение вне экрана, которое вы обновляете, и вместо этого оно отображается на экране. Если все сделано правильно, рисование изображения часто происходит быстрее, чем рисование графики на экране.
Другой тип - перелистывание страниц. То есть, у вас есть активный буфер, который всегда отображается на экране, и вне-экранный / нулевой буфер, к которому вы фактически обращаетесь. Затем вы переворачиваете эти буферы, когда вы готовы визуализировать обновления на экране (это ближе к тому, как работает анимация фильма).
Следующий пример - ДЕЙСТВИТЕЛЬНО базовый пример перелистывания страниц.
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Panel;
import java.awt.image.BufferedImage;
import static testdoublebuffer.TestDoubleBuffer.UPDATE;
public class AppletDoubleBuffer extends Applet {
private BufferedPane pane;
@Override
public void init() {
pane = new BufferedPane();
setLayout(new BorderLayout());
add(pane);
}
@Override
public void start() {
pane.start();
}
@Override
public void stop() {
pane.stop();
}
public class BufferedPane extends Panel {
private BufferedImage activeBuffer;
private BufferedImage scratch;
private boolean running = false;
public BufferedPane() {
}
public void start() {
if (!running) {
running = true;
Thread thread = new Thread(new MainLoop());
thread.setDaemon(true);
thread.start();
}
}
public void stop() {
running = false;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
public void invalidate() {
synchronized (UPDATE) {
activeBuffer = null;
scratch = null;
}
super.invalidate();
}
@Override
public void update(Graphics g) {
if (activeBuffer != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(activeBuffer, 0, 0, this);
g2d.dispose();
}
}
public class MainLoop implements Runnable {
private int delay = 1000 / 25;
private int x = 0;
private int velocity = 5;
private int size = 10;
public void update() {
x += velocity;
if (x + size >= getWidth()) {
x = getWidth() - size;
velocity *= -1;
} else if (x <= 0) {
x = 0;
velocity *= -1;
}
if (getWidth() > 0 && getHeight() > 0) {
synchronized (UPDATE) {
if (scratch == null) {
scratch = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Graphics2D g2d = scratch.createGraphics();
int y = (getHeight() - size) / 2;
g2d.setBackground(Color.BLUE);
g2d.clearRect(0, 0, getWidth(), getHeight());
g2d.setColor(Color.RED);
g2d.fillOval(x, y, size, size);
g2d.dispose();
// Flip the buffers...
BufferedImage tmp = activeBuffer;
activeBuffer = scratch;
scratch = tmp;
}
}
}
@Override
public void run() {
while (running) {
long startTime = System.currentTimeMillis();
update();
repaint();
long duration = System.currentTimeMillis() - startTime;
if (duration < delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException ex) {
}
}
}
}
}
}
}
Лично я бы просто пропустил использование AWT и перешел бы на Swing, который предоставляет улучшенные / встроенные функции для такого типа вещей.
public void Update(Graphics gfx)
{
paint(gfx);
}
Должен быть в нижнем регистре, так как вы пытаетесь переопределить update(Graphics g)
метод в апплете.
Так и должно быть
@Override
public void update(Graphics gfx)
{
paint(gfx);
}
Что касается изменения фона, фон - это просто большой прямоугольник, который покрывает экран и придает ему определенный цвет. В вашем paint
, ты делаешь clearRect
, который очищает экран. Вместо этого просто переключите это на fillRect
после установки цвета.
Это может выглядеть примерно так
public void paint(Graphics g)
{
//setColor to whatever you want
//fillRect to cover the screen
}
Когда вы делаете это, вам нужно помнить одну вещь: не перепутайте два графических объекта. Как концепция, двойная буферизация работает, рисуя сначала в память (рисуя ее на внеэкранном изображении), а затем на экране. Вы хотите всегда рисовать на этом закадровом изображении, так как оно намного быстрее (и мы теряем мерцание).
Поэтому убедитесь, что вы делаете imageGraphicsObject.setColor
не screenGraphicsObject.setColor
или imageGraphicsObject.fillRectnot
screenGraphicsObject.fillRect`. В противном случае вы больше не будете использовать двойную буферизацию.