Breakout – Part 1

Breakout is a classic game. The original game of Breakout was invented by Steve Wozniak before he founded Apple with Steve Jobs.

The main part of the game is to have bricks at the top of the screen, in x rows going downwards, and you have a paddle (bat) and a ball which you hit with the paddle at the bottom of the screen to try and direct the ball to hit the bricks to get rid of them all. The name breakout comes from when you knock down one part of the wall (e.g. left hand side) you can direct the ball to go up that side and then it would have broken out of the bricks and start to knock down the wall from the top.

It is a classic game, and also a game that most people can do, but it is like most things and that is you have to break things into pieces of the puzzle.

To start with, the assignment documentation does make a good start with breaking things down in the pieces, here the main parts that I thought of as well added into the list from the assignment documentation.

  • Build the bricks (wall)
  • Paddle (bat), mouse to move the paddle
  • Bat to hit, and collide with objects
  • Make the ball move around the screen
  • Whilst the ball is moving, check for collisions (with the bricks/paddle)
  • If the ball collides with a brick, remove it and reverse the y direction to go in the opposite direction
  • If the ball collides with the paddle, alter the direction for the ball depending on where it hits the paddle
  • Create some textural information, e.g. score, click to start, etc
  • Extras, music, extra scores/life’s balls that are generated when you hit a brick with the ball
  • When all of the bricks are knocked out, display a message

So to start with, the bricks (wall of bricks), since there is some declarations already setup in the source file

/** Number of bricks per row */
	private static final int NBRICKS_PER_ROW = 10;
 
/** Number of rows of bricks */
	private static final int NBRICK_ROWS = 10;

then we can create a rows of bricks accordingly to the constant (final) values

		int startX, startY = BRICK_Y_OFFSET;
		GRect bricks;
		// iRow = going downwards
		for (int iRow =0; iRow < NBRICK_ROWS; iRow++)
		{
			startX = BRICK_SEP;
			// iAisle = going across
			for (int iAisle = 0; iAisle < NBRICKS_PER_ROW; iAisle++)
			{
				bricks = new GRect(startX,startY, BRICK_WIDTH,BRICK_HEIGHT);
				bricks.setFillColor(colours[iRow]);
				bricks.setFilled(true);
				bricks.setColor(colours[iRow]);
				add(bricks);
				startX += BRICK_WIDTH + BRICK_SEP;
			}
			startY += BRICK_HEIGHT + BRICK_SEP;
		}

here are my private variables so that the rest of the methods can use them to move/create that object in questions

	private GRect paddleObject;
	private GOval ballObject, extraScore, extraLife;
	private GLabel textDisplay, status;
	private double ballVX, ballVY, ballX, ballY;
	private int lifesLeft, score, pauseDelay;
	private RandomGenerator rgen = RandomGenerator.getInstance();
	private Color colours[] = new Color[NBRICK_ROWS];

The next thing, is to create a paddle and also a mouse listener for the mouse movement on the screen to move the paddle in the x direction (left and right). (paddleObject being the paddle from the private variables)

// mouse listener
	public void mouseMoved(MouseEvent e)
	{
		Point movePos = e.getPoint();
		if (movePos.x < (WIDTH- PADDLE_WIDTH))
			paddleObject.setLocation(movePos.x,HEIGHT - (PADDLE_Y_OFFSET + PADDLE_HEIGHT));
	}
// create the paddle
	private void addPaddle()
	{
		paddleObject = new GRect((WIDTH/2) - (PADDLE_WIDTH/2), HEIGHT - (PADDLE_Y_OFFSET + PADDLE_HEIGHT),
								PADDLE_WIDTH, PADDLE_HEIGHT);
		paddleObject.setColor(Color.BLACK);
		paddleObject.setFillColor(Color.BLACK);
		paddleObject.setFilled(true);
		add(paddleObject);
	}

I am not going to go through all of the source code, but just in parts of the interesting stuff, so collisions, to start with we need to know if a object X has any colliding parts to it to another object and thus return the object that it has collided with, why returning the object and also passing in a object reference ? , because then we are able to use a different graphics object (e.g. the ball or a extras) and also return the actual object that we are colliding with.

	// get any Graphics Object surrounding the ball Object, be it the bat and ball, or bat and extras
	private GObject getCollidingObject(GObject ballObj)
	{
		if (!ballObj.isVisible()) return null;
		GPoint ballPoint = ballObj.getLocation();
		GObject coll;
		Point addingPoints[] = new Point[4];
		addingPoints[0]= new Point(0,0);
		addingPoints[1]= new Point(0,BALL_RADIUS);
		addingPoints[2]= new Point(BALL_RADIUS,BALL_RADIUS);
		addingPoints[3]= new Point(BALL_RADIUS,0);
		for (int i =0; i< 4; i++)
		{
			coll = getElementAt(ballPoint.getX()+addingPoints[i].x, ballPoint.getY()+addingPoints[i].y);
			if (coll != null)
					return coll;
		}
		return null;
	}

the reason why I am doing the check to see if we have collided with 4 points, if you think about it, there are 4 points that the object can collide with, top left/right, bottom left/right and if any of those points are not null (e.g. a object) then just exit the for loop and return that object, there is no need to keep on checking because we have found a object.

Next is if we have collided with a brick, and thus see if there is any bricks left, we can either use the number of bricks and every time we collide with one use a local variable to minus from until we reach 0, or go through the list of graphical objects on the screen and see if there is any bricks (GRect) left on the screen if not then all the bricks are gone (of course there still will be one left because that is the paddle (paddleObject)). either way is a good way, just that I use bricks minus way because it uses less speed (e.g. not finding graphics objects).

			bricksLeft--;
			if (bricksLeft == 0) 
			{ 
			        emptyBricks = true; 
				break;
			}
			/*//another way of checking to see if there is any GRects left.!!.
			emptyBricks = true;
			for (int i =0; i < getElementCount(); i++)
			{
				if (getElement(i).getClass().getSimpleName().equalsIgnoreCase("GRect"))
				{
					if (!getElement(i).getClass().equals(paddleObject))
					{
						emptyBricks= false;
						break;
					}
				}
			}
			if (emptyBricks) break;
								 */

the other is if we have collided with the paddle, then move the ball in another direction depending on where on the paddle we hit

	// difference between two points.
	private GPoint differenceBetween(GPoint a, GPoint b)
	{
		return new GPoint(a.getX() - b.getX(), a.getY() - b.getY());
	}
.....
	GPoint difBetween = differenceBetween(ballObject.getLocation(), colliding.getLocation());
	ballVX = (difBetween.getX() - (PADDLE_WIDTH/2))/2;
	ballVY = -(difBetween.getY()/BALL_SPEED);
	// for when the ball may try and bounce of the side of the bat and then the wall!!.
	if (ballVY == 0.0) ballVY = 1.0;
	// move in the opposite direction
	ballVY = -ballVY;

so basically we need to the point on the bat where the ball has hit, left/right top/side, (how far from the middle of the bat) and then alter the balls velocity accordingly.

The rest of the assignment is creating more of the same and textural information (making sure that the ball is not colliding with them and move !!!) and also any extra’s that you can think of, I thought that I would create extras balls that had either extra points (score) or another one that is a extra life if you catch them with your paddle.

Here is the output of the breakout game and also the full source code (I have included the full source code in the zip file above and also the PDF assignment which has more details inside).

2 thoughts on “Breakout – Part 1”

  1. This program has a bug. When the ball hits the side of the paddle, it bounces on and on and never falls.

  2. Well noticed Brain, I think that just checking about the ballVY = 0.0 is probably the cause of that issue, so probably would need to check against more of a wider range as such.

    if (ballVY > -0.1 && ballVY < 0.2)
    	ballVY = 0.4;

    which should fix the problem :).

Leave a Reply

Your email address will not be published.