3
\$\begingroup\$

I've written a Pong game in Java using the Swing library, and while the code works, the ball seems to lag across the screen. Perhaps my code is inefficient. I've included only the Board class because the majority of the code is within it and I reckon its what really needs optimizations. If more code is needed, please let me know (I just don't want to post too much code). What can I do to improve the game?

A note: a paddle's y coordinate is controlled by the mouse. It's a two player game where the current paddle switches after each hit. There is no AI.

public class Board extends JPanel {
    int width, height;

    private Paddle paddle1, paddle2;
    private Ball ball;

    private boolean current_paddle = true; // true = paddle1, false = paddle2
    public boolean game_over = false;
    private JLabel game_over_message;
    private final int max_point = 5; // game ends when a player reaches 5 points

    private int mouse_y; // y coordinate of the mouse relative to the window

    public Board(int width, int height) {
        this.width = width;
        this.height = height;

        this.setBackground(Color.BLACK);

        ball = new Ball(width / 2, height / 2); // ball constructor takes in the location (x, y) of the ball

        // paddle constructor takes in an x coordinate for its location on the board
        paddle1 = new Paddle(20);
        paddle2 = new Paddle(800 - Paddle.WIDTH - 20);

        this.setLayout(null);

        // x coordinate of the current score of player 1
        int score1_x = 300;

        paddle1.score_label.setBounds(score1_x, 0, 50, 50);
        paddle1.score_label.setForeground(Color.WHITE);
        add(paddle1.score_label);

        // x coordinate of the current score of player 2
        int score2_x = (int)((1 - (double)score1_x / width) * width);

        paddle2.score_label.setBounds(score2_x, 0, 50, 50);
        paddle2.score_label.setForeground(Color.WHITE);
        add(paddle2.score_label);
    }

    // the update method is called continuously in the game loop
    public void update() {
        final Point p = this.getMousePosition();
        if (p != null) {
            // sets the current paddle's y coordinate to the mouse's y coordinate
            this.mouse_y = p.y;
        }

        handlePaddleCollision(this.current_paddle ? paddle1 : paddle2);
        handleBorderCollision();

        if (this.current_paddle) { // if the current paddle is paddle 1 (true means paddle 1)
            paddle1.update(mouse_y);
        }
        else {
            paddle2.update(mouse_y);
        }
        ball.update();
        repaint();
    }

    private void handlePaddleCollision(Paddle current_paddle) {
        // checks if the ball has collided with a paddle horizontally
        final boolean x_collision = (int)ball.x + Ball.WIDTH * (this.current_paddle ? 0 : 1) ==
                                            (current_paddle.x + Paddle.WIDTH * (this.current_paddle ? 1 : 0));

        // checks if the ball's collision is within the range of the paddle's height
        final boolean within_y_range_of_paddle = ((int)ball.y + Ball.HEIGHT) >= current_paddle.y && 
                                        (int)ball.y <= (current_paddle.y + Paddle.HEIGHT);

        if (x_collision && within_y_range_of_paddle) {
            ball.dx *= -1;

            // the farther away from the center of the paddle the ball hits, the higher the bounce angle
            int y_hit = (int)ball.y + (Ball.HEIGHT / 2);
            int center_of_paddle = current_paddle.y + (Paddle.HEIGHT / 2);
            int dy_hit = center_of_paddle - y_hit;
            double dy_hit_normalized = (double)dy_hit / (Paddle.HEIGHT / 2);
            double bounce_angle = dy_hit_normalized * (Math.toRadians(75));

            ball.dy = -Math.sin(bounce_angle) / 300; // divide by 300 because without doing so, the velocity is way too high.

            this.current_paddle = !this.current_paddle; // after collision, change the current paddle to the other paddle.
        }
    }

    private void resetBall() {
        // puts ball back to the center of the screen and resets its velocity
        ball.x = this.width / 2 - Ball.WIDTH / 2;
        ball.y = this.height / 2 - Ball.HEIGHT / 2;
        ball.dx *= -1;
        ball.dy = 0.0;
        this.current_paddle = !this.current_paddle;
    }

    private void handleBorderCollision() {
        // if ball hits the top or bottom of screen
        if (ball.y <= 0 || ball.y >= 572 - Ball.HEIGHT) {
            ball.dy *= -1.0;
        }

        // if ball intersects the left border of the screen
        if (ball.x + Ball.WIDTH <= 0) {
            resetBall();
            paddle2.score++;
        }
        // if ball intersects the left border of the screen
        else if (ball.x >= 800) {
            resetBall();
            paddle1.score++;
        }

        // checks if either score has reached the max score, ending the game. 
        if (paddle2.score >= max_point ||
                paddle1.score >= max_point) {
            this.game_over = true;
            this.endGame();
        }
    }

    private void endGame() {
        // prints "Player x wins."
        String winner = (paddle1.score >= max_point) ? "1" : "2";
        game_over_message = new JLabel("Player " + winner + " wins.");
        game_over_message.setFont(new Font(game_over_message.getName(), Font.PLAIN, 60));
        game_over_message.setBounds(220, 200, 1000, 100);
        game_over_message.setForeground(Color.RED);
        this.add(game_over_message);
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        paddle1.paint(g);
        paddle2.paint(g);
        ball.paint(g);
    }
}
\$\endgroup\$
  • \$\begingroup\$ did you try to use double buffering? docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html \$\endgroup\$ – Timothy Truckle Dec 21 '16 at 14:30
  • \$\begingroup\$ It would be useful to see what your ball.update method does (and probably the rest of ball) as well as whatever calls update. You mention that the ball appears to lag. This can happen if you are drawing more frequently than your machine can handle. To get around this, you draw the objects at a certain refresh rate and calculate their position after the time change. It's unclear from the code you have posted what your approach to this is. \$\endgroup\$ – forsvarir Dec 21 '16 at 23:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.