简体   繁体   中英

How do you remove the flickering in the paint() method

I've had the flickering problem for a while and I would like to know if there is a simple way to solve the problem. I know you can use the BufferStrategy but I can not find a good, updated source that work for.png images. The flickering occures when I move my character or when the repaint() method is called.

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.ImageIcon;
import javax.swing.JFrame;

public class Game extends JFrame implements Runnable, KeyListener {

    private static final long serialVersionUID = 1L;

    Image back = new ImageIcon(Game.class.getResource("LEVEL1.png")).getImage();
    Image player = new ImageIcon(Game.class.getResource("freddyD.png")).getImage();
    Image studio = new ImageIcon(Game.class.getResource("studio0.png")).getImage();

    public int xPos = 100;
    public int yPos = 300;
    public boolean D = false;
    public boolean Q = false;

    public Game() {

        addKeyListener(this);
        setSize(900, 700);
        setLocationRelativeTo(null);
        setVisible(true);

    }

    public void paint(Graphics g) {

        g.drawImage(back, 0, 0, 6000, 6000, this);
        g.drawImage(studio, 100, 100, 340, 310, this);
        g.drawImage(player, xPos, yPos, 60, 104, this);

    }

    public void keyTyped(KeyEvent e) {

    }

    public void keyPressed(KeyEvent e) {

        int k = e.getKeyCode();

        if (k == KeyEvent.VK_D) {
            D = true;
        }

        if (k == KeyEvent.VK_Q) {
            Q = true;
        }
    }

    public void keyReleased(KeyEvent e) {

        int k = e.getKeyCode();

        if (k == KeyEvent.VK_D) {
            D = false;
        }

        if (k == KeyEvent.VK_Q) {
            Q = false;
        }

    }

    public void run() {

        while (true) {

            if (D) {

                xPos += 2;
                repaint();
            }

            if (Q) {

                xPos -= 2;
                repaint();
            }

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public static void main(String args[]) {
        new Game().start();
    }

    public void start() {
        new Thread(this).start();
    }
}

This is a relative common issue. Top level containers, like JFrame , are not double buffered. This means that each change made to the graphics context will be applied immediately, which is causing the flickering.

As a general rule of thumb you shouldn't:

  • Override paint of any component
  • Paint directly to top level containers

JFrame is actually a compound component, mean it has several other child components which are used to generate the general layout, and, because of the way the painting sub system works, these child components can be painted independently (or without notification to) the frame.

Start by reading through Performing Custom Painting and Painting in AWT and Swing for some more details about how painting in Swing works and how you should work with it.

As a general recommendation, you should start with a JPanel and override it's paintComponent method (don't forget to call super.paintComponent )

Remember, Swing is not thread safe, so you need to take that into consideration. You might consider using something like aSwing Timer instead of a Thread , if the frame rate is not super critical. A Swing Timer will ensure that all "tick" events take place in the Event Dispatching Thread, making it safer to update the UI (and the state the UI depends on).

If you need more direct control, then you will need to consider using a BufferStrategy , which will give you direct control over when something is painted, allowing you to better control the painting process.

KeyListener is generally a poor choice for this type of task, for a number of reasons. Instead you should be using Key Bindings API which solves these short comings.

For example...

This is just a simple paint example to get you started...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Game {

    public static void main(String[] args) {
        new Game();
    }

    public Game() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Rectangle hBox;
        private Rectangle vBox;

        private int hDelta = 1;
        private int vDelta = 1;

        public TestPane() {
            hBox = new Rectangle(0, 0, 10, 10);
            vBox = new Rectangle(0, 0, 10, 10);
            Timer timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    hBox.x += hDelta;
                    hBox.y = (getHeight() - 10) / 2;

                    if (hBox.x + hBox.width >= getWidth()) {
                        hBox.x = getWidth() - hBox.width;
                        hDelta *= -1;
                    } else if (hBox.x <= 0) {
                        hBox.x = 0;
                        hDelta *= -1;
                    }

                    vBox.y += vDelta;
                    vBox.x = (getWidth() - vBox.width) / 2;
                    if (vBox.y + vBox.height >= getHeight()) {
                        vBox.y = getHeight() - vBox.height;
                        vDelta *= -1;
                    } else if (vBox.y <= 0) {
                        vBox.y = 0;
                        vDelta *= -1;
                    }
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.RED);
            g2d.fill(hBox);
            g2d.setColor(Color.BLUE);
            g2d.fill(vBox);
            g2d.dispose();
        }

    }
}

The thing I always do that doesn't take that much time is just increase the time in Thread.sleep() . The golden number that I found to work while being low is 15 millis; anything lower than that starts to flicker. In your code, you use 10 millis, which might be the issue. If you don't want to do that (for speed reasons or whatever), use BufferedImage . It's pretty complicated, but if you want to know more here is a good site . The main thing is that BufferedImage .

This kind of issues can happen when you use single buffer for drawing. Check double buffering, it should solve your problem: https://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM