The complete specification of assignment #3 can be found as part of the stream at iTunes.
Checking for collisions
Now comes the interesting part. In order to make Breakout into a real game, you have to be able to tell whether the ball is colliding with another object in the window. As scientists often do, it helps to begin by making a simplifying assumption and then relaxing that assumption later. Suppose the ball were a single point rather than a circle. In that case, how could you tell whether it had collided with another object?
If you look in Chapter 9 (page 299) at the methods that are defined at the GraphicsProgram level, you will discover that there is a methodpublic GObject getElementAt(double x, double y)that takes a position in the window and returns the graphical object at that location, if any. If there are no graphical objects that cover that position, getElementAt returns the special constant null. If there is more than one, getElementAt always chooses the one closest to the top of the stack, which is the one that appears to be in front on the display.
What happens if you callgetElementAt(x, y)where x and y are the coordinates of the ball? If the point (x, y) is underneath an object, this call returns the graphical object with which the ball has collided. If there are no objects at the point (x, y), you’ll get the value null.
So far, so good. But, unfortunately, the ball is not a single point. It occupies physical area and therefore may collide with something on the screen even though its center does not. The easiest thing to do—which is in fact typical of the simplifying assumptions made in real computer games—is to check a few carefully chosen points on the outside of the ball and see whether any of those points has collided with anything. As soon as you find something at one of those points, you can declare that the ball has collided with that object.
In your implementation, the easiest thing to do is to check the four corner points on the square in which the ball is inscribed. Remember that a GOval is defined in terms of its bounding rectangle, so that if the upper left corner of the ball is at the point (x, y), the other corners will be at the locations shown in this diagram:
These points have the advantage of being outside the ball—which means that getElementAt can’t return the ball itself—but nonetheless close enough to make it appear that collisions have occurred. Thus, for each of these four points, you need to:
- Call getElementAt on that location to see whether anything is there.
- If the value you get back is not null, then you need look no farther and can take that value as
the GObject with which the collision occurred.- If getElementAt returns null for a particular corner, go on and try the next corner.
- If you get through all four corners without finding a collision, then no collision exists.
It would be very useful to write this section of code as a separate method
private GObject getCollidingObject()that returns the object involved in the collision, if any, and null otherwise. You could then use
it in a declaration likeGObject collider = getCollidingObject();which assigns that value to a variable called collider.
From here, the only remaining thing you need to do is decide what to do when a collision occurs. There are only two possibilities. First, the object you get back might be the paddle, which you can test by checkingif (collider == paddle) ...If it is the paddle, you need to bounce the ball so that it starts traveling up. If it isn’t the paddle, the only other thing it might be is a brick, since those are the only other objects in the world. Once again, you need to cause a bounce in the vertical direction, but you also need to take the brick away. To do so, all you need to do is remove it from the screen by calling the remove method.
First get the position of the ball, then check all corners for an available object. If there is any return it, if not return null:
private GObject getCollidingObject() { double x = ball.getX(); double y = ball.getY(); double d = 2 * BALL_RADIUS; GObject object; object = getElementAt(x, y); if (object != null) return object; object = getElementAt(x + d, y); if (object != null) return object; object = getElementAt(x, y + d); if (object != null) return object; object = getElementAt(x + d, y + d); if (object != null) return object; return null; }
Adjust checkForCollision() to check for colliding objects. If it is the paddle change the vertical velocity, if it is no not remove it.
GObject collider = getCollidingObject(); if (collider == paddle) { vy = -vy; } else if (collider != null) { remove(collider); }
The code for this assignment is available on github.