Search code examples
javagraphicspaint

Having trouble making object move without flickering in Java


I have looked into Double Buffering and plan on implementing it eventually but as of right now I can't figure out how to use it or anything like it. I am trying to make pong so I plan on adding three objects total but for now I just want to get one object to work smoothly. I'm fairly new to graphics so I don't know entirely what I'm doing and I'm just trying to learn as I go.

Here is my code:

Pong:

public static void main(String[]args) {
    JFrame window= new JFrame();
    window.setTitle("Pong Game");
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.setPreferredSize(new Dimension(800,500));
    window.pack();
    window.setVisible(true);
    Ball ball= new Ball();
    Paddle player= new Paddle();
    window.getContentPane().add(ball);
    for(;;) {
        ball.move();
        //window.setContentPane(ball);
        window.setContentPane(player);
        player.move();
    }
}

Paddles:

double x, y, ymove;
boolean cpu;

public Paddle() {
    x=5;
    y=180;
    ymove=.1;
}

//passing an integer through to make the computer paddle
public Paddle(int a) {
    cpu= true;
    x=761;
    y=180;
    ymove=.1;
}

public void paint(Graphics g) {
    g.setColor(Color.blue);
    g.fillRect((int)x, (int)y, 18, 120);
}

public void move() {
    y+=ymove;
    if(y>=500-160||y<=0) {
        ymove*=-1;
    }
}

Ball:

double x, y, xspeed, yspeed;

public Ball() {
    x=200;
    y=200;
    xspeed=0;
    yspeed=.1;
}

public void move() {
    x+=xspeed;
    y+=yspeed;
    if(y>=440||y<=0) {
        yspeed*=-1;
    }
}

public void paint(Graphics g) {
    g.setColor(Color.black);
    g.fillOval((int)x, (int)y, 20, 20);
}

Solution

  • This Answer is a very very simplified explanation! I recommend checking out this linux journal, which explores something similia.

    The main issue, which causes the "flickering" is, that the draw is done "to fast".

    Take your main loop:

    for(;;) {
        ball.move();
        window.setContentPane(ball);
        window.setContentPane(player);
        player.move();
    }
    

    This loop updates the positions of the ball and afterwards "adds it to the content pane". While it is drawn, the next image is already added and drawn. This is causing the flickering (again, note: this is very simplified).

    The simplest solution to fix the "flickering" is, to let the Thread sleep after it has drawn and "wait" until the draw is finished.

    boolean running = true;
    int delay = 15; // adjust the delay
    while(running) {
        ball.move();
        player.move();
        window.setContentPane(ball);
        window.setContentPane(player);
        try {
            Thread.sleep(delay);
        } catch(InterruptedException e) {
            // We were interrupted while waiting
            // Something "woke us up". Stop the loop.
            e.printStackTrace();
            running = false;
        }
    }
    

    This Thread.sleep method let's the current Thread "wait" for the specified time.

    The delay can be adjusted to something more practical. You could for example calculate how many frames you want and sleep for that amount.

    Another way would be to "time" the updates. This could be done with a timer. Since it is more or less deprecated, i implement it using the ScheduledExecutorService

    int delay = 15; // adjust the delay
    
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool();
    scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
        public void run() {
            ball.move();
            player.move();
        }
    }, delay, TimeUnit.MILLISECONDS)
    

    As of Java8, you might write this using a Lambda like this:

    scheduledExecutorService.scheduleAtFixedRate(() -> {
        ball.move();
        player.move();
    }, delay, TimeUnit.MILLISECONDS)
    

    To stop it, you could now call:

    scheduledExecutorService.shutdown();
    

    However: There are more sophisticated solutions. One is, as you already noted, the double buffering. But there are also multiple different techniques, that compensate more difficult problems. They use something called page flipping.