Solution for
Programming Exercise 6.7
THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to
the following exercise from this on-line
Java textbook.
Exercise 6.7:
Section 3.7 discussed SimpleAnimationApplet2, a framework
for writing simple animations. You can define an animation by writing a subclass and defining
a drawFrame() method. It is possible to have the subclass implement the
MouseListener interface. Then, you can have an animation that responds to mouse
clicks.
Write a game in which the user tries to click on a little square that
jumps erratically around the applet. To implement this,
use instance variables to keep track of the position of the square. In the drawFrame()
method, there should be a certain probability that the square will jump to a new location.
(You can experiment to find a probability that makes the game play well.) In your mousePressed
method, check whether the user clicked on the square. Keep track of and display the number of
times that the user hits the square and the number of times that the user misses it.
Don't assume that you know the size of the applet in advance.
Discussion
Here is the applet:
In order to respond to mouse events, this applet must implement
the MouseListener interface. This applet, JumpingSquareGame, has
SimpleAnimationApplet2 as its direct superclass, rather than
JApplet, so the source code begins:
public class JumpingSquareGame extends SimpleAnimationApplet2
implements MouseListener {
Several instance variables are needed to keep track of the
state of the applet. Since the square can move, we need to keep
track of its position. I do this with two integer instance variables,
squareLeft and squareTop. Since the applet
displays the number of hits and the number of misses, these will have
to be stored in instance variables. Each time the user clicks on the
applet, one or the other of these variables will be incremented.
The class includes an init() method which registers the
applet to listen for mouse events on itself. It also places the square at
the center of the applet. The size of the square is given by a named
constant, SQUARE_SIZE, so the init() method says:
public void init() {
// Put the square in the center of the applet, and
// set the applet to listen for mouse clicks.
squareLeft = getSize().width / 2 - SQUARE_SIZE / 2;
squareTop = getSize().height / 2 - SQUARE_SIZE / 2;
addMouseListener(this);
}
The reasoning behind the formulas for squareLeft and
squareTop is this: The center point of the applet has
coordinates (getSize().width/2, getSize().height/2).
We want to put the center of the square at this point. The value
we want for squareLeft is the position of the left edge
of the square. To get this, we start in the middle of the
applet, getSize().width/2, and back up by subtracting the size
of half of the square, SQUARE_SIZE/2. The same reasoning
works for squareTop.
There could be a problem in this init() method. We are
writing a subclass of SimpleAnimationApplet2, which could
have its own init() method. If so, we've just replaced it
with our own init() method in the subclass. When our applet
runs, our init() method will be executed, and the one in
the superclass will not be executed. What if the init()
method in the SimpleAnimationClass does important initializations?
They won't be executed, and the animation won't work. Now, as a matter
of fact, I looked at the source code of SimpleAnimationApplet2
and saw that it does not define
an init() method. So we are OK. (We are just replacing the
init(). method from the Applet class, which doesn't
do anything.) But I should be able to write
a subclass without looking at the source code of the superclass.
In that case, I should make sure that my init() method calls
the init() method from the superclass. This can be done
through the special variable super, with the command
super.init(). Then, the init() method
would be written:
public void init() {
// Put the square in the center of the applet, and
// set the applet to listen for mouse clicks.
super.init(); // Call the init() method
// of the superclass.
squareLeft = getSize().width / 2 - SQUARE_SIZE / 2;
squareTop = getSize().height / 2 - SQUARE_SIZE / 2;
addMouseListener(this);
}
The point of the SimpleAnimationApplet2 is that it calls
the method, drawFrame, repeatedly, several times per second.
This method has to draw one frame of
the animation. It can also change the state of the applet to get
ready for the next frame. It's easy enough to draw the frame.
(I spent a little time trying to choose colors that look nice.)
The change of state that we have to worry about is that the
square might jump between the current frame and the next frame.
The exercise suggests that there should be a certain small probability
that the square will jump. In my drawFrame method, I
implement this by saying
if (Math.random() < 0.2)
jump();
where jump() is a method that I have defined to move the
square to a random point. Since Math.random() is a random
number between 0 and 1, there is a 1 in 5 chance that it is between
0 and 0.2. So, the square will jump, on the average, in one frame out
of 5.
The mousePressed routine has to respond when the user
clicks on the applet. It checks whether the (x,y) point
where the user clicked is inside the current position of the square.
If so, then the number of hits goes up by one. If not, then
the number of misses goes up by one. I also make the square jump
if the user hits it:
public void mousePressed(MouseEvent evt) {
// When the user clicks the mouse, determine whether the user
// clicked the square. If so, add one to hits. If not, add
// one to misses. Note that if the user hits the square, it
// jumps so that the user will not be able to get multiple
// hits by clicking the same place over and over.
int x = evt.getX();
int y = evt.getY();
if (x >= squareLeft && x < squareLeft + SQUARE_SIZE
&& y >= squareTop && y < squareTop + SQUARE_SIZE) {
hits++;
jump();
}
else {
misses++;
}
}
The complete source code is given below.
The Solution
/*
A little game in which a square jumps around in the applet and
the user tries to click on it. The number of times the user
hits the square and the number of times the user misses are
reported. This applet is based on SimpleAnimationApplet2,
and it requires that class.
*/
import java.awt.*;
import java.awt.event.*;
public class JumpingSquareGame extends SimpleAnimationApplet2
implements MouseListener {
static final Color appletBorderColor = new Color(180, 70, 0);
static final Color appletFillColor = new Color(255, 240, 180);
static final Color squareBorderColor = new Color(0, 0, 160);
static final Color squareFillColor = new Color(220,220,255);
static final int SQUARE_SIZE = 30; // Length of side of the square.
int squareLeft, squareTop; // Top-left corner of the square.
int hits = 0; // Number of times user has hit the square.
int misses = 0; // Number of times user has missed the square.
public void init() {
// Put the square in the center of the applet, and
// set the applet to listen for mouse clicks.
squareLeft = getSize().width / 2 - SQUARE_SIZE / 2;
squareTop = getSize().height / 2 - SQUARE_SIZE / 2;
addMouseListener(this);
}
public void drawFrame(Graphics g) {
// Draw the game, showing the square in its current position
// and displaying the hits and misses. There is a small
// chance that the square will jump.
g.setColor(appletFillColor); // Fill in the background.
g.fillRect(0, 0, getSize().width, getSize().height);
g.setColor(appletBorderColor);
g.drawRect(0, 0, getSize().width - 1, getSize().height - 1);
g.drawRect(1, 1, getSize().width - 3, getSize().height - 3);
g.setColor(squareFillColor); // Draw the square.
g.fillRect(squareLeft, squareTop, SQUARE_SIZE, SQUARE_SIZE);
g.setColor(squareBorderColor);
g.drawRect(squareLeft, squareTop, SQUARE_SIZE - 1, SQUARE_SIZE - 1);
g.drawRect(squareLeft + 1, squareTop + 1, SQUARE_SIZE - 3, SQUARE_SIZE - 3);
g.setColor(appletBorderColor); // Report hits and misses.
g.drawString( hits + " Hits", 6, 16);
g.drawString( misses + " Misses", 6, 28);
if (Math.random() < 0.2) // A 1/5 chance that the square will jump.
jump();
} // end drawFrame()
void jump() {
// Move the square to a new random location. The range of possible
// values will prevent the square from going outside the applet
// or from overlapping the 2-pixel border around the applet.
// Note that it is not necessary to call repaint, since this
// is an animation that is redrawn continually.
squareLeft = 2 + (int)(Math.random() * (getSize().width - 4 - SQUARE_SIZE));
squareTop = 2 + (int)(Math.random() * (getSize().height - 4 - SQUARE_SIZE));
}
public void mousePressed(MouseEvent evt) {
// When the user clicks the mouse, determine whether the user
// clicked the square. If so, add one to hits. If not, add
// one to misses. Note that if the user hits the square, it
// jumps so that the user will not be able to get multiple
// hits by clicking the same place over and over.
int x = evt.getX();
int y = evt.getY();
if (x >= squareLeft && x < squareLeft + SQUARE_SIZE
&& y >= squareTop && y < squareTop + SQUARE_SIZE) {
hits++;
jump();
}
else {
misses++;
}
}
public void mouseReleased(MouseEvent evt) { }
public void mouseClicked(MouseEvent evt) { }
public void mouseEntered(MouseEvent evt) { }
public void mouseExited(MouseEvent evt) { }
} // end class JumpingSquareGame
[ Exercises
| Chapter Index
| Main Index
]