## 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 discussedSimpleAnimationApplet2, a framework for writing simple animations. You can define an animation by writing a subclass and defining adrawFrame()method. It is possible to have the subclass implement theMouseListenerinterface. 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 yourmousePressedmethod, 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

MouseListenerinterface. This applet,JumpingSquareGame, hasSimpleAnimationApplet2as its direct superclass, rather thanJApplet, 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,

squareLeftandsquareTop. 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 theinit()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

squareLeftandsquareTopis 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 forsquareLeftis 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 forsquareTop.There could be a problem in this

init()method. We are writing a subclass ofSimpleAnimationApplet2, which could have its owninit()method. If so, we've just replaced it with our owninit()method in the subclass. When our applet runs, ourinit()method will be executed, and the one in the superclass will not be executed. What if theinit()method in theSimpleAnimationClassdoes 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 ofSimpleAnimationApplet2and saw that it does not define aninit()method. So we are OK. (We are just replacing theinit(). method from theAppletclass, 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 myinit()method calls theinit()method from the superclass. This can be done through the special variablesuper, with the commandsuper.init(). Then, theinit()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

SimpleAnimationApplet2is 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 mydrawFramemethod, I implement this by sayingif (Math.random() < 0.2) jump();where

jump()is a method that I have defined to move the square to a random point. SinceMath.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

mousePressedroutine 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 ]