如何修复糟糕的双缓冲

时间:2013-04-09 01:14:30

标签: java graphics awt flicker double-buffering

我尝试过双缓冲教程,我真的不知道我做错了什么。它在我做教程之前就已经工作了,但是这里和那里仍然偶尔闪烁。我有两个文件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 个答案:

答案 0 :(得分:2)

public void Update(Graphics gfx)
{
    paint(gfx);
}

应该是小写,因为您试图覆盖Applet中的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.fillRect not screenGraphicsObject.fillRect`。否则你就不再是双缓冲了。

答案 1 :(得分:2)

有许多不同类型的“双缓冲区”。基本是一个简单的屏幕外图像,您更新后会将其绘制到屏幕上。如果做得好,绘制图像通常会更快,然后将图形绘制到屏幕上。

另一种类型是页面翻转。也就是说,你有一个活动缓冲区,它总是呈现在屏幕上,一个屏幕外/暂存缓冲区就是你实际呈现的内容。然后,当您准备好向屏幕呈现更新时,您可以翻转这些缓冲区(这更接近电影动画的工作方式)。

以下示例是页面翻转的一个非常基本的示例。

enter image description here

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,为这类事物提供更好/内置的功能。