-1

I figured out a solution minutes after posting, but due to low reputation I couldn't delete the post

For the fun of it I decided to start working on something which might turn into a game at some point.

I'm trying to draw some circles and move them in a given direction currently. This causes flickering. It's very likely that I oversee something very basic but I can't figure out why it doesn't render smoothly.

My board class looks something like (removed what I deemed unnecessary):

public class Board extends Canvas implements Runnable {

    public static void main(String[] args) {
        Board board = new Board();

        board.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));

        JFrame frame = new JFrame("Circles");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(board);
        frame.pack();

        frame.setVisible(true);

        board.start();
    }

    @Override
    public void run() {
        while (running) {
            process();
            repaint();

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

The paint method:

    public void paint(Graphics g1) {
        super.paint(g1);
        Graphics2D g = (Graphics2D) g1;
        for (Ball ball : Ball.BALLS) {
            g.drawOval((int) ball.getLocation().getX(), (int) ball.getLocation().getY(), ball.getRadius(),  ball.getRadius());
        }
    }

My process method:

private void process() {
    if (utilities.randInt(1, 100) < 10 && Ball.getBallCount() < 40) {
        Ball.spawnNew(this);
    }
    for (Ball ball : Ball.BALLS) {
        ball.move(this);
    }
    Ball.BALLS.removeAll(Ball.TO_REMOVE);
    Ball.TO_REMOVE.clear();
}

The move method basically increments the x-value of the ball by a given value each time its called (moving it right).

Like I said, I'm unsure why it flickers so if you have any pointers please do tell.

Thanks!

TheG1
  • 11
  • 1
  • 7

2 Answers2

1

This sounds like a case where you need to perform double-buffering, so that one copy of your canvas can remain shown while you are updating the other.

You're using AWT here, and I don't know how to implement double-buffering manually with AWT. However, if you're willing to use Swing here you can take advantage of automatic double-buffering. See the question about How to make canvas with Swing? as well as Oracle Technology Network's article on Painting in AWT and Swing.

The basic idea would be:

  1. extend javax.swing.JPanel instead of Canvas (which means when you override paint(Graphics) you're now overriding it from javax.swing.JComponent instead of java.awt.Component)
  2. create a constructor with super(true) to enable double-buffering.

Edit: Also, as iccthedral points out, you're better off overriding paintComponent(Graphics) and including a call to super.paintComponent(Graphics). See Difference between paint, paintComponent and paintComponents in Swing.

Community
  • 1
  • 1
muffin
  • 608
  • 6
  • 13
1

You need double buffering. To do this you need to create a BufferedImage and get the Graphics from it. Paint everything to the image, render the image on to the screen, then finally fill the image with a the background color or image to reset it.

Sample:

//one time instantiation
BufferedImage b = new BufferedImage(width, height, mode);

In paint(Graphics g):

Graphics buffer = b.getGraphics();
//render all of the stuff on to buffer
Graphics2D g = (Graphics2D) buffer;
    for (Ball ball : Ball.BALLS) {
        g.drawOval((int) ball.getLocation().getX(), (int) ball.getLocation().getY(), ball.getRadius(),  ball.getRadius());
    }
g1.drawImage(b, 0, 0, width, height, null);
g.setColor(Color.BLACK);
//reset the image
g.drawRect(0, 0, width, height);
g.dispose();
TameHog
  • 920
  • 10
  • 22