Как исправить плохой двойной буфер

Я пытался следовать учебнику по двойной буферизации, и я действительно не знаю, что я сделал неправильно. Это работает до того, как я сделал урок, но время от времени мерцание есть. У меня есть два файла 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.fillRectnotscreenGraphicsObject.fillRect`. В противном случае вы больше не будете использовать двойную буферизацию.

Другие вопросы по тегам