JoeyTheAsian
JoeyTheAsian

Reputation: 49

Java: white frames while painting canvas

public class Screen extends Canvas implements Runnable {
    private static final int MAX_FPS = 60;
    private static final int FPS_SAMPLE_SIZE = 6;

    private boolean[] keysPressed = new boolean[256];

    private ArrayList<Character> characters = new ArrayList<>();
    private ArrayList<Character> ai = new ArrayList<>();
    private Thread thread;
    private BufferedImage bg;

    //for testing putposes
    private Player slime = new Player("Slime.png", 100, 100);
    private Player bird = new Player("Bird.png", 250, 250);
    private Player giant = new Player("Giant.png", 250, 500);
    private Player swordsman = new Player("Swordsman.png", 500, 250);

    //screen dimensions
    private int height = ((Toolkit.getDefaultToolkit().getScreenSize().height-32)/5*4);
    private int width = Toolkit.getDefaultToolkit().getScreenSize().width;

    private long prevTick = -1;
    private LinkedList<Long> frames = new LinkedList<>();
    private int averageFPS;
    private boolean running;


    public Screen() {
    setSize(width, height);
    try {bg = ImageIO.read(new File("GUI Images/success.jpg"));}
    catch (Exception e) {Utilities.showErrorMessage(this, e);}
    characters.add(slime);
    //  ai.add(bird);
    //ai.add(giant);
    //  ai.add(swordsman);
    addKeyListener(new KeyListener() {
        public void keyPressed(KeyEvent e) {keysPressed[e.getKeyCode()] = true;}
        public void keyReleased(KeyEvent e) {keysPressed[e.getKeyCode()] = false;}
        public void keyTyped(KeyEvent e) {}
        });
    addMouseListener(new MouseListener() {
        public void mouseClicked(MouseEvent e) {}
        public void mouseEntered(MouseEvent e) {}
        public void mouseExited(MouseEvent e) {}
        public void mousePressed(MouseEvent e) {}
        public void mouseReleased(MouseEvent e) {}
        });
    addMouseMotionListener(new MouseMotionListener() {
        public void mouseDragged(MouseEvent e) {}
        public void mouseMoved(MouseEvent e) {}
        });
    addMouseWheelListener(new MouseWheelListener() {
        public void mouseWheelMoved(MouseWheelEvent e) {}
        });

    setVisible(true);
    start();
    }

    public void paint(Graphics g){
    BufferStrategy bs = getBufferStrategy();
    if(getBufferStrategy() == null){
        createBufferStrategy(3);
        return;
    }
    g = bs.getDrawGraphics();
    g.drawImage(bg,0,0,width,height, null);
    g.drawString(String.valueOf(averageFPS), 0, 0);
    for (Character character : ai){
        character.setY(character.getY() + (int)(Math.random() * 10 - 5));
        character.setX(character.getX() + (int)(Math.random() * 10 - 5));
    }
    for (Character character : ai) {g.drawImage(character.getImage(), character.getX(), character.getY(), null);}
    for (Character character : characters) {g.drawImage(character.getImage(), character.getX(), character.getY(), null);}
    g.dispose();
    bs.show();
    }

    public void run() {
    while (running) {
        tick();
        repaint();
    }
    }

    public synchronized void start() {
    running = true;
    thread = new Thread(this);
    thread.start();
    }

    public synchronized void stop() {
    try {thread.join();}
    catch (InterruptedException e) {Utilities.showErrorMessage(this, e);}
    }

    public void tick() {
    if (keysPressed[KeyEvent.VK_W] && (slime.getY() > 0)) {slime.setY(slime.getY() - 1);}
    if (keysPressed[KeyEvent.VK_S] && (slime.getY() < height)) {slime.setY(slime.getY() + 1);}
    if (keysPressed[KeyEvent.VK_A] && (slime.getY() > 0)) {slime.setX(slime.getX() - 1);}
    if (keysPressed[KeyEvent.VK_D] && (slime.getY() < width)) {slime.setX(slime.getX() + 1);}
    //  System.out.println(slime.getX() + ", " + slime.getY());

        long pastTime = System.currentTimeMillis() - prevTick;
        prevTick = System.currentTimeMillis();

        if (frames.size() == FPS_SAMPLE_SIZE) {
            frames.remove();
        }
        frames.add(pastTime);

        // Calculate average FPS
        long sum = 0;
        for (long frame : frames) {
            sum += frame;
        }
        long averageFrame = sum / FPS_SAMPLE_SIZE;
        averageFPS = (int)(1000 / averageFrame);

        // Only if the time passed since the previous tick is less than one
        // second divided by the number of maximum FPS allowed do we delay
        // ourselves to give Time time to catch up to our rendering.
    if (pastTime < 1000.0 / MAX_FPS) {
            try {
                Thread.sleep((1000 / MAX_FPS) - pastTime);
            System.out.println(averageFPS);
            } catch (InterruptedException e) {
                System.out.println(e);
        }
    }

    }
}

There's my code, essentially this is just test code for the screen to see if I can properly paint objects to the screen, but there's erratic white frames that keep getting painted. They don't seem to be caused by any sort of input in particular, they just happen seemingly randomly during frame refreshes. Any ideas as to what it might be?

Upvotes: 0

Views: 111

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347334

Don't override paint and use a BufferStrategy within it. Painting is usually done on a need to do bases (AKA "passive" rendering). BufferStrategy is a "active" rendering approach, where you take control of the paint process and update the buffer directly, the two don't tend to play well together

Take the logic from the current paint method and place it within your game loop.

This will require you to remove your call to start within the constructor, as the component won't have been added to a valid native peer (a component which is displayed on the screen).

Instead, create an instance of Screen, add it to your frame and then call start, for example...

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
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.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestScreen {

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

    public TestScreen() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                Frame frame = new Frame("Testing");
                frame.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }
                });
                Screen screen = new Screen();
                frame.add(screen);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
                screen.start();
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
        }

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

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.dispose();
        }

    }

    public class Screen extends Canvas implements Runnable {

        private static final int MAX_FPS = 60;
        private static final int FPS_SAMPLE_SIZE = 6;

        private boolean[] keysPressed = new boolean[256];

        private ArrayList<Character> characters = new ArrayList<>();
        private ArrayList<Character> ai = new ArrayList<>();
        private Thread thread;
        private BufferedImage bg;

        private long prevTick = -1;
        private LinkedList<Long> frames = new LinkedList<>();
        private int averageFPS;
        private boolean running;

        public Screen() {
            try {
                bg = ImageIO.read(new File("GUI Images/success.jpg"));
            } catch (Exception e) {
                e.printStackTrace();
            }
            addKeyListener(new KeyListener() {
                public void keyPressed(KeyEvent e) {
                    keysPressed[e.getKeyCode()] = true;
                }

                public void keyReleased(KeyEvent e) {
                    keysPressed[e.getKeyCode()] = false;
                }

                public void keyTyped(KeyEvent e) {
                }
            });
            addMouseListener(new MouseListener() {
                public void mouseClicked(MouseEvent e) {
                }

                public void mouseEntered(MouseEvent e) {
                }

                public void mouseExited(MouseEvent e) {
                }

                public void mousePressed(MouseEvent e) {
                }

                public void mouseReleased(MouseEvent e) {
                }
            });
            addMouseMotionListener(new MouseMotionListener() {
                public void mouseDragged(MouseEvent e) {
                }

                public void mouseMoved(MouseEvent e) {
                }
            });
            addMouseWheelListener(new MouseWheelListener() {
                public void mouseWheelMoved(MouseWheelEvent e) {
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            int height = ((Toolkit.getDefaultToolkit().getScreenSize().height - 32) / 5 * 4);
            int width = Toolkit.getDefaultToolkit().getScreenSize().width;
            return new Dimension(width, height);
        }

        public void run() {

            createBufferStrategy(3);
            BufferStrategy bs = null;

            while (running) {
                bs = getBufferStrategy();
                Graphics g = bs.getDrawGraphics();
                g.drawImage(bg, 0, 0, getWidth(), getHeight(), null);
                FontMetrics fm = g.getFontMetrics();
                g.setColor(Color.RED);
                g.drawString(String.valueOf(averageFPS), 0, fm.getAscent());
                g.dispose();
                bs.show();
                tick();
            }
        }

        public synchronized void start() {
            running = true;
            thread = new Thread(this);
            thread.start();
        }

        public synchronized void stop() {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void tick() {
//            if (keysPressed[KeyEvent.VK_W] && (slime.getY() > 0)) {
//                slime.setY(slime.getY() - 1);
//            }
//            if (keysPressed[KeyEvent.VK_S] && (slime.getY() < height)) {
//                slime.setY(slime.getY() + 1);
//            }
//            if (keysPressed[KeyEvent.VK_A] && (slime.getY() > 0)) {
//                slime.setX(slime.getX() - 1);
//            }
//            if (keysPressed[KeyEvent.VK_D] && (slime.getY() < width)) {
//                slime.setX(slime.getX() + 1);
//            }
//            //  System.out.println(slime.getX() + ", " + slime.getY());

            long pastTime = System.currentTimeMillis() - prevTick;
            prevTick = System.currentTimeMillis();

            if (frames.size() == FPS_SAMPLE_SIZE) {
                frames.remove();
            }
            frames.add(pastTime);

            // Calculate average FPS
            long sum = 0;
            for (long frame : frames) {
                sum += frame;
            }
            long averageFrame = sum / FPS_SAMPLE_SIZE;
            averageFPS = (int) (1000 / averageFrame);

            // Only if the time passed since the previous tick is less than one
            // second divided by the number of maximum FPS allowed do we delay
            // ourselves to give Time time to catch up to our rendering.
            if (pastTime < 1000.0 / MAX_FPS) {
                try {
                    Thread.sleep((1000 / MAX_FPS) - pastTime);
                    System.out.println(averageFPS);
                } catch (InterruptedException e) {
                    System.out.println(e);
                }
            }

        }
    }

}

Upvotes: 1

Related Questions