Search code examples
javabreakoutacm-java-libraries

Java programming, breakout game ball physics not behaving as expected


I am studying java with the book "The Art and Science of Java: An Introduction to Computer Science". One of the practice programs is to create a simple clone of the Breakout game.

I am currently able to load the game, but am having issues with the ball physics. I'm using the simplest physics possible, and can't see why it isn't working.

When the ball hits a wall, it bounces normally, but when it hits the paddle or a brick, it doesn't. I'm using the same code in the collision detection to change the direction that I used with the walls. The collisions are detected, I added a println and watched the console as it went through the collision events, but the direction change is not happening.

package chapter10;

/* 
 * This program creates a clone of the classic Breakout Game,
 * where a player bounces a ball with a paddle to "break" bricks 
 * along the top of the screen.
 * 
 * Controls: Left: left button, Right: right button
 */

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.RepaintManager;
import javax.swing.Timer;

import acm.program.*;
import acm.graphics.*;

public class BreakoutClone extends GraphicsProgram {

/* components */
private GRect paddle;
private GRect brick;
private GOval ball;

/* static variables */

private static final double PADDLE_HEIGHT = 5;
private static final double BALL_SPEED = 2;
private static final double PADDLE_SPEED = 2;
private static final double ROWS_BRICKS = 6;
private static final double COLUMNS_BRICKS = 10;
private static final double TOP_GAP = 50;

/* variables */

private int numTurns = 3;
private double paddleWidth = 50;
private int dx = 2;
private int dy = 2;

public void init() {
    setSize(700, 600);

    paddle = new GRect(0, getHeight() - 30, paddleWidth, PADDLE_HEIGHT);
    paddle.setFilled(true);
    add(paddle);

    addBricks();

    ball = new GOval(getWidth() / 2, 175, 5, 5);
    ball.setFilled(true);
    add(ball);
}

public void run() {
    animateBall();
    // TODO animate paddle
}

public void addBricks() {
    double gap = 20;
    double brickWidth = getWidth() / COLUMNS_BRICKS;
    for (int r = 0; r < ROWS_BRICKS; r++) {
        for (int b = 0; b < COLUMNS_BRICKS; b++) {
            brick = new GRect(b * brickWidth, gap * r + TOP_GAP,
                    brickWidth, 10);
            brick.setFilled(true);
            add(brick);
        }
    }
}

public void endGame() {
    // TODO write end game method
}

public void animateBall() {

    while (numTurns > 0) {

        ball.move(dx, dy);
        pause(15);

        /* Look for Object Collision */
        GObject topRightObject = getElementAt(ball.getX() + 5, ball.getY());
        GObject topLeftObject = getElementAt(ball.getX(), ball.getY());
        GObject botRightObject = getElementAt(ball.getX() + 5,
                ball.getY() + 5);
        GObject botLeftObject = getElementAt(ball.getX(), ball.getY() + 5);

        /* Bounce off walls */

        if ((ball.getX() >= getWidth() - 5) || ball.getX() <= 0) {
            dx = -dx;
        }
        if (ball.getY() <= 0) {
            dy = -dy;
        }
        if ((ball.getY() >= getHeight() - 5)) {
            dy = -dy;
            // numTurns--;
            // if (numTurns == 0) {
            // endGame();
            // } else {
            // run();
            // }
        }

        /* Bounce off objects, remove bricks */

        if (topRightObject != null) {
            dy = -dy;
            hasCollided(topRightObject);
        }
        if (topLeftObject != null) {
            dy = -dy;
            hasCollided(topLeftObject);
        }
        if (botRightObject != null) {
            dy = -dy;
            hasCollided(botRightObject);
        }
        if (botLeftObject != null) {
            dy = -dy;
            hasCollided(botLeftObject);
        }
    }
}

private void hasCollided(GObject obj) {
    if (obj.equals(paddle)) {
        System.out.println("detecting paddle");

    } else {
        System.out.println("detecting brick");

        remove(obj);
    }
}
}

Solution

  • Add an if(dy<0) around the part that reverses direction of the ball when it hits the paddle (assuming a negative dy means the ball is heading towards the paddle). This removes the possibility that your direction is being changed, but more than once per collision