Important! Turn off the file backup feature in DrJava—it causes problems on some system configurations! Go to menu item Edit, then to Preferences, choose the last category Miscellaneous, and uncheck the box that says "Keep Emacs-style Backup Files."
Academic Integrity: You may work on your own or with one partner. You may discuss background issues and general solution strategies with others, but the project you submit must be the work of just you (and your partner). If you work with a partner, you and your partner must register as a group in CMS and submit your work as a pair.
In this project, you will learn how to use classes and objects, and how to develop and test code incrementally—one class (or even method) at a time. You will write the classes and methods to run a simplified version of a little game called BoomShine.
The objective of this game is to make as many of the bouncing balls explode as you can. The player gets to start one explosion and any ball that comes into contact with an exploding ball in turn explodes. In other words, the player chooses the location of the exploding ball in order to make a chain reaction last as long as possible. In our simplified game, there will be two balls, and the goal is to explode both of them.
Included with the assignment files is one possible complete implementation of the game that you can play with to get an idea of what the final product should be like. (See README.txt on how to run the example game.) Note that you are free to experiment a bit with your solution, and the result doesn't have to look exactly like the sample given, as long as it achieves the stated requirements.
This project will involve implementing the functionality of five classes:
Point
, Velocity
, Ball
,
Walls
, and BoomShine
. Skeleton files are provided
on the Projects page to get you started. In addition, we are providing the
complete GameFrame
class that takes care of all the graphics and
window functionality of the game.
The skeleton files have comments before each class and method that specify
what the methods are required to do. These are their specifications,
and you should implement the functionality as specified. Do not
change any of the given method headers or the given variable declarations.
You can, however, implement additional private
methods if you
like.
Do not use arrays in this project. We'll use arrays in the next project to create a more interesting game!
Basically, the game goes as follows. First there will be two balls (class
Ball
) moving at some velocity. They will bounce when they hit
the wall, but they will pass over one another (the balls do not bounce off
one another). They can explode, but they only do so when they hit another
ball that is exploding. The explosion is a chain reaction initiated by a
mouse click, which essentially creates another ball (ball3
).
ball3
does not move and it starts to explode once created. An
explosion will eventually collapse and stop touching off other explosions.
The following diagram illustrates the game.
First, let's determine the relationship between the classes. A ball can be
represented as a point with a radius. The radius is a simple numeric value,
but a point on the Cartesian plane has two numeric values, the x- and
y-coordinates. We can "package" the x- and y-coordinates into one type,
called the Point
class. Look at the skeleton Point
class now, you will see that it has two fields, x
and
y
. Now look at the Ball
class. It has several
fields, including the center
, which is a point so it is of type
Point
, and the radius
(type double
).
Another property, or state, of a Ball
is the velocity. A
velocity vector in our 2-dimensional game has two components: x and y.
Therefore, we have also created a Velocity
class with the fields
x
and y
. Did you notice the similarity
(redundancy), between classes Point
and Velocity
?
We'll work with the redundant code for now. Later, in Project 6, we will look
at another design.
The Walls
class defines the 2-dimensional space within which
the Balls
move. The Walls
has the properties height
and width, and in this class we will define how a Ball
will
bounce off the Walls
.
The BoomShine
class simulates the game. In the
main
method you will create the necessary Walls
and
Ball
s objects and implement the rules of the game.
So where do you start?? Choose the most independent class, one that
doesn't depend on other classes! That would be class Point
or
Velocity
. Below are more information on implementing the
individual classes. Be sure to follow the specifications here and in the
comments. Watch for the comments that start with the marker TODO
. These comments describe what to do in (each section
of) the methods.
Before you write or change any code, compile all six classes that you have downloaded. The given and skeleton classes should compile successfully (even though you cannot execute the game yet). As you implement the classes, compile and test your code often. Since we have given you code that compiles to begin with, we expect that your submitted project will at least compile, even if it doesn't execute flawlessly.
Velocity
class (Velocity.java)You can start by implementing the class Velocity
, as it is
the simplest. Velocity
contains only data, so you only need to
implement the constructor, getters, and setters. Test your code
before you move on to another class! For example, you may want to type the
following statements in DrJava's Interactions Pane to make sure that your
Velocity
class is correct:
Velocity v= new Velocity(3,1.6); System.out.println(v.getX()); //expect to see 3 v.setX(7.1); System.out.println(v.getX()); //expect to see 7.1
You can write similar code to test the methods related to the y component.
Point
class (Point.java)The Point
contains mostly data, except the method
distance
which calculates the distance to another
Point
. You will need this method later when you check for
collisions. Again, test your code after you implement the methods.
Use similar test code as that shown above for testing the x and y components.
To test the distance
method, create two Point
s and
calculate the distance between them:
Point p1= new Point(0,0); Point p2= new Point(1,3); System.out.println(p1.distance(p2)); //expect to see 3.16...
Always test your code before moving on to another class! It may be tempting to skip the testing, but that will burn more time in the end since you may end up with a large number of errors in multiple classes. Debugging one class at a time, with a small number of errors and dependencies, is far easier than debugging multiple classes with numerous confounding errors all at the same time.
Ball
class (Ball.java)The center
of a ball is of class Point
. It
absolutely determines the location of the ball. A ball has a
radius
, which should be changed when it explodes. A ball has a
velocity
(class Velocity
), that can be changed by
the walls (method checkBounce
in Walls
). You will
need the velocity to calculate the location of the ball over time.
Implement the constructor, all the getters and setters specified in
Ball.java, and two essential methods: update
and
checkCollision
. Method update
has the signature
public void update(double secsElapsed)
It will be passed the number of seconds (can be fractional) elapsed since
the last call to this method. Take a look in class BoomShine.java
for an example method call. The update to be done depends on whether the ball
is exploding. If the ball is not exploding (based on the value in the field
isExploding
), then it is moving according to its current
velocity, so you need to change its location over time by changing its
center
. Don't change the value of isExploding
in
this method, as it will be determined elsewhere. Similarly, whether the ball
is at the walls will be checked in another method (in class
Walls
). If the ball is exploding, then it doesn't move and its
radius will grow over time to a maximum before shrinking to zero. You have
the freedom to decide how the explosion will look. For example, you can have
the radius grow linearly with time or have certain acceleration as the ball
grows. You can change its color and decide when to shrink. However, keep in
mind that the explosion and eventual shrinkage should look gradual, i.e.,
they happen over a number of calls to update
. Hint: the
field explodingTime
will be handy. Also, the explosion should
not grow so big that it covers most of the game frame (the game would be too
easy then!).
Testing: Create a Ball
object and use simple print
statements to show the field values. A few examples are shown below, but you
should do additional tests!
//Create a Ball at (19,7) with radius 10, velocity 100 in the // negative x direction, and color code 4: Ball b1= new Ball(new Point(19,7), 10, new Velocity(-100,0), 4); //Several example calls to the Ball's getter methods: System.out.println(b1.getRadius()); //expect to see 10.0 System.out.println(b1.getCenter().getY()); //expect to see 7.0 System.out.println(b1.getVelocity().getX()); //expect to see -100.0
Notice in the last few statements that you can chain up your method calls.
For example, b1
refers to a Ball
, which has a
getCenter()
method. This getCenter()
method returns
something of type Point
, which has a getY()
method.
This is why the entire expression to access the y-coordinate, which is in a
Point
, which is in a Ball
, is written as
b1.getCenter().getY()
.
update
method:
//Update the ball's position after 0.1 seconds (it isn't exploding) b1.update(0.1); System.out.println(b1.getCenter().getX()); //expect to see 9.0 System.out.println(b1.getCenter().getY()); //expect to see 7.0
You get to decide on how the explosion works, so you should create some tests for that case as well.
At this point, not only can you compile the skeleton classes, you can run
the program as well! (Run the main
method in class
BoomShine
). We have provided enough example code in classes
Walls.java and BoomShine.java to show a ball moving to the
left, but the ball can only bounce back from the left wall and will move into
the other walls. You will fix this later (in class Walls
). Now
close the graphics window and go back to programming!
Complete the checkCollision
method to detect whether a ball
is colliding with another. As stated in the specifications, do not change the
state of either ball. You will need to use the method distance
from class Point
.
Walls
class (Walls.java)The walls are actually a rectangle with a certain width and a certain
height. Implement or modify the getters and setters as specified in order to
work with walls of a different size. The method checkBounce
in
the skeleton only checks whether the ball hits the left wall. You need to
modify this method to have the other walls bounce the ball back as well. Use
the code for the left wall as an example.
Testing: Below are some tests for bouncing off the left wall. Test your code for the other walls as well! Below, we have created a ball that is just a little "in" the left wall. This can happen during the simulation since we update the position of a ball just once in an iteration—the positions over time are discrete values, not a continuous function. However, since each iteration takes only fractions of a second, the portion of the ball that gets in the wall is acceptably small and is generally not visible.
Walls walls= new Walls(800, 600); Ball b2= new Ball(new Point(9.9,70), 10, new Velocity(-200,0), 4); walls.checkBounce(b2); System.out.println(b2.getCenter().getX()); //Expect to see 10.1 System.out.println(b2.getVelocity().getX()); //Expect to see 200.0
BoomShine
class (BoomShine.java)Congratulations! You now have all the mechanisms to implement the game. All the careful testing that you have done up to this point should pay off by allowing you to concentrate on the logic of the simulation, not errors in the previous classes! The given skeleton only creates one ball moving to the left. You need to modify the code so that there are two balls, each starting from a random position, with a random radius, and moving at a random velocity. Use reasonable ranges for your random numbers. A suggested range for each velocity component is 150 to 400 (both positive and negative).
In the game loop (the infinite loop in the skeleton), you need to add code
for all the actions that have to be taken during each iteration of the game.
What do you see when the game starts? The two balls move around while the
game appears to be waiting for the user to make one mouse click. The given
skeleton code shows you how to use method update
to move the
ball in each iteration, but how does the "waiting" work? The program actually
doesn't wait! At each iteration, it checks to see if a mouse click has been
made. The given GameFrame
class provides the method
getClick
to accept a mouse click and return the clicked location
as a Point
. If a user has not made a mouse click, method
getClick
returns the value null
. Properly calling
getClick
and dealing with the returned value gives the
appearance of the program "waiting" for a single mouse click. The skeleton
creates the GameFrame
object and calls it frame
.
The syntax for calling the getClick()
method is therefore
frame.getClick()
.
ball3
) at the mouse
click and set it to explode.
The other actions in the game loop include updating the position or radius
of the balls (method update
), checking for bounces against the
walls, and checking for collisions. Recall that a ball that collides with an
exploding ball will start to explode. If both ball1
and
ball2
have exploded, send out a message using the
setMessage
method defined in class GameFrame
to let
the user know he or she has won. The syntax is
frame.setMessage("
your message")
.
GameFrame
class (GameFrame.java)This is the class provided with the project. Do not modify it. Two methods
defined in this class that you will call directly are getClick
and setMessage
. Method getClick
reads the location
of a mouse click and returns the location as a Point
. It has the
signature
public Point getClick()
Method setMessage
displays a message in the graphics window
and has the signature
public void setMessage(String message)
Submit to CMS the .java files for your 5 classes:
Point
, Velocity
, Walls
,
Ball
, and BoomShine
. Make sure that you submit your
*.java files, and not your *.class files. Double
check this!