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:

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 ]