Solution for
Programming Exercise 8.4
THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to the following exercise from this on-line Java textbook.
Exercise 8.4: For this problem, you will need to use an array of objects. The objects belong to the class MovingBall, which I have already written. You can find the source code for this class in the file MovingBall.java. A MovingBall represents a circle that has an associated color, radius, direction, and speed. It is restricted to moving in a rectangle in the (x,y) plane. It will "bounce back" when it hits one of the sides of this rectangle. A MovingBall does not actually move by itself. It's just a collection of data. You have to call instance methods to tell it to update its position and to draw itself. The constructor for the MovingBall class takes the form
new MovingBall(xmin, xmax, ymin, ymax)where the parameters are integers that specify the limits on the x and y coordinates of the ball. In this exercise, you will want balls to bounce off the sides of the applet, so you will create them with the constructor call "new MovingBall(0, getSize().width, 0, getSize().height)". The constructor creates a ball that initially is colored red, has a radius of 5 pixels, is located at the center of its range, has a random speed between 4 and 12, and is headed in a random direction. If ball is a variable of type MovingBall, then the following methods are available:
- ball.draw(g) -- draw the ball in a graphics context. The parameter, g, must be of type Graphics. (The drawing color in g will be changed to the color of the ball.)
- ball.travel() -- change the (x,y)-coordinates of the ball by an amount equal to its speed. The ball has a certain direction of motion, and the ball is moved in that direction. Ordinarily, you will call this once for each frame of an animation, so the speed is given in terms of "pixels per frame". Calling this routine does not move the ball on the screen. It just changes the values of some instance variables in the object. The next time the object's draw() method is called, the ball will be drawn in the new position.
- ball.headTowards(x,y) -- change the direction of motion of the ball so that it is headed towards the point (x,y). This does not affect the speed.
These are the methods that you will need for this exercise. There are also methods for setting various properties of the ball, such as ball.setColor(color) for changing the color and ball.setRadius(radius) for changing its size. See the source code for more information.
For this exercise, you should create an applet that shows an animation of 25 balls bouncing around on a black background. Your applet can be defined as a subclass of SimpleAnimationApplet, which was first introduced in Section 3.7. Use an array of type MovingBall[] to hold the 25 balls. The drawFrame() method in your applet should move all the balls and draw them.
In addition, your applet should implement the MouseListener and MouseMotionListener interfaces. When the user presses the mouse or drags the mouse, call each of the ball's headTowards() methods to make the balls head towards the mouse's location.
Here is my solution. Try clicking and dragging on the applet:
Discussion
This is actually not a very difficult problem, although it might take time to get used to working with arrays of objects. An instance variable of type MovingBall[] is needed to hold the data for the 25 balls. This instance variable can be declared as
MovingBall[] balls;The array can be created in the applet's init() method with the command
balls = new MovingBall[25];However, this just gives an array filled with null values. There aren't any balls yet. Each of the balls must be created with a call to the constructor from the MovingBall class:
for (int i = 0; i < 25; i++) { balls[i] = new MovingBall(0, getSize().width, 0, getSize().height); }The array of MovingBalls is used throughout the rest of the applet. In the drawFrame method, each ball must be moved and drawn. The i-th ball can be moved by calling its travel() method with the command "balls[i].travel()". It can be drawn in the graphics context g by calling its draw() method with the command balls[i].draw(g);". To apply these commands to every ball in the array, we need a for loop
for (int i = 0; i < balls.length; i++) { balls[i].travel(); balls[i].draw(g); }(Using "i < balls.length" instead of "i < 25" in this loop will make it easier to change the number of balls. It guarantees that even if the size of the array is changed, the for loop will still work as written.) Similarly, in the mousePressed() and mouseDragged() routine, we need a for loop to tell each ball to head towards the location of the mouse, (evt.getX(),evt.getY()):
for (int i = 0; i < balls.length; i++) { balls[i].headTowards(evt.getX(),evt.getY()); }That's really all there is to it. You might want to try variations like giving the balls random colors or sizes. This can be done in the init() method. In my applet, I decided to use applet parameters to make it possible to customize the applet by specifying the number of balls and by setting the speed at which the animation plays. In fact, the applet at the top of this page shows 50 balls instead of the default 25, and the animation is playing at 50 milliseconds per frame instead of at the default 100 milliseconds per frame. (The applet on the page of exercises used the default values.) Recall that applet parameters are specified in the <applet> tag on the web page. The tag for the applet at the top of this page is
<applet code="BallisticBalls.class" width=270 height=240> <param name="frameTime" value="50"> <param name="ballCount" value="50"> </applet>The param with name "ballCount" specifies the number of balls in the applet. The param with name "frameTime" specifies the number of milliseconds to use for each frame of the animation. You can look at the source code for the applet, below, to see how I use these params. The method getIntParam() demonstrates how to get an integer value from an applet param.
The Solution
/* This applet shows an animation of red balls moving on a black background. The balls "bounce" off the sides of the applet. The number of balls can be set as the value of an applet param with name "ballCount". The default number is 25. The number of milliseconds can be set as the value of an applet parameter with name "frameTime". The default is 100 milliseconds. A value of 50 milliseconds will give a nicer speed. If the user clicks on the applet, or drags the mouse on the applet, all the balls head towards the mouse location. The "balls" are represented by objects of type MovingBall. This applet depends on the MovingBall class and on the SimpleAnimationApplet, which it extends. */ import java.awt.*; import java.awt.event.*; import java.applet.Applet; public class BallisticBalls extends SimpleAnimationApplet implements MouseListener, MouseMotionListener { MovingBall[] balls; // An array to hold the balls. public void init() { // Initialize the applet. The applet listens for // mouse events on itself. The balls are created. // The number of balls and number of milliseconds // per frame are set from applet parameters (or to // default values). addMouseListener(this); addMouseMotionListener(this); setMillisecondsPerFrame( getIntParam("frameTime", 100) ); /* Create an array to hold the balls, then create the balls. */ int ballCt = getIntParam("ballCount", 25); // How many balls? balls = new MovingBall[ ballCt ]; // Create the array for (int i = 0; i < balls.length; i++) { // Create each of the ball objects. The parameters specify // that the balls are restricted to moving within the bounds // of the applet. balls[i] = new MovingBall(0, getSize().width, 0, getSize().height); } } // end init() private int getIntParam(String paramName, int defaultValue) { // Get a non-negative integer applet parameter. This routine // will read an applet param with the name paramName. // If the param has a value that is a POSITIVE integer, // then that value is returned. Otherwise, the defaultValue // is returned. String param = getParameter(paramName); // Get param, if any. // (If there is none, then // the value is null.) if (param != null) { try { // Try to convert the parameter string to an int. int N = Integer.parseInt(param); if (N > 0) // Return the integer only if it is > 0. return N; } catch (NumberFormatException e) { // The param value is not a legal integer. } } // If we get here, then the param did not exist in the <applet> tag // or was not a legal integer, or was <= 0. So, return the // default value that was specified in the function call. return defaultValue; } // end getIntParam public void drawFrame(Graphics g) { // This method is called by the SimpleAnimationApplet framework // to compute and draw the next frame in the animation. The // applet is filled with black. Then the balls are moved and // drawn. g.setColor(Color.black); // Fill the applet with a black background. g.fillRect(0, 0, getSize().width, getSize().height); /* Tell each ball to move. It moves an amount depending on its current direction and speed, and it will "bounce" off the side of the applet if necessary. Then the ball is told to draw itself in the graphics context g. */ for (int i = 0; i < balls.length; i++) { balls[i].travel(); balls[i].draw(g); } } // end drawFrame() public void mousePressed(MouseEvent evt) { // The user has clicked on the applet. Tell all the // balls to head towards the location of the mouse. for (int i = 0; i < balls.length; i++) balls[i].headTowards(evt.getX(), evt.getY()); } public void mouseDragged(MouseEvent evt) { // The user has dragged the mouse on the applet. Tell all // the balls to head towards the location of the mouse. for (int i = 0; i < balls.length; i++) balls[i].headTowards(evt.getX(), evt.getY()); } public void mouseReleased(MouseEvent evt) { } public void mouseMoved(MouseEvent evt) { } public void mouseClicked(MouseEvent evt) { } public void mouseEntered(MouseEvent evt) { } public void mouseExited(MouseEvent evt) { } } // end class BallisticBalls
[ Exercises | Chapter Index | Main Index ]