Lab 10
Java Game
  Ex 1:
  Ex 2:
Interaction
  Ex 3:
  Ex 4:
  Ex 5:
Ball World
The manager
  Over to you
  Ex 6:
  Ex 7:
  Ex 8:
Class extension
  Ex 9:
How to submit?
Policies
Back to labs

Lab 10

In this lab you are going to investigate more about how to react to events in Java and a way to set up animation. The techniques you learn should help you develop a really great final project. Try to turn in as many exercise solutions as you can.

Java Applet Game

Events happen when users do things like hitting keys on the keyboard or moving the mouse. In this lab we will see that objects can create events too. We will be using a Timer object to cause updates to occur in a JApplet at regular intervals.

I will lead you through the steps needed to create an applet like this:



Try to click on the mouse when it is visible. Each time you successfully do that you will gain a point. When you get to 10 points you will go to the next level. If you click when the mouse has changed to a devil you will lose 20 points. Your aim is to rise through as many levels as possible. You can restart a game by sliding the mouse out of the applet space.



Exercise 1

Play the game. All you have to do for this exercise is report to the grader what was the highest level you were able to ascend to.


One image in a JApplet

The most obvious features of the game are an image of Mighty Mouse and an image of the Tasmanian Devil. The mouse appears in an apparently random position on the screen where it is visible for a short time before being replaced by a devil in the same position. Let's talk about Java code that can cause images to appear. As always, mine is not the only way to do it and you are welcome to research different techniques.

First of all, you need to decide what class to extend. Possibilities are:

  • a JFrame
  • a JApplet

A JFrame has the advantage that it's a little easier (in my opinion) to load images. A JApplet has the advantage that you can post it on the web and show it off to friends and relatives all over the world.

Just so you get a taste of both, I'm going to provide you with a lot of my code (for a JApplet) and will expect you to create your code for a JFrame.

So let's start with a JFrame to display Mighty Mouse (whom I sometimes refer to as Mickey. Go figure.)

I'll give you a JApplet and in the next exercise I'll ask you to make an equivalent JFrame.

Begin by creating a new Java project in your workspace; call it lab11; create a new package (again call it lab11) and make a new class (call it MyMouse1) extending JApplet. Copy this text into your edit window. You will need to click all the red Xs and do the appropriate imports. Most are easy, but be sure to get java.awt.Image (not some other Image class). When all is complete you will find a bin directory containing a folder called lab11, a file java.policy.applet and a .html file. This is where you will store your mouse image (unless you want to change my code). Find an image of a cartoon mouse (use Google) and save it to that bin directory, and name it mighty_mode.jpg if you want to use my code unchanged.

Check that my code works for you. When you run the program you should see a window with your mouse image.

Did you notice that my instance variables are declared protected rather than the usual private. That's because I'm intending to add functionality to my classes by extending them. When a variable is declared protected it can be modified by methods in classes that extend this class. If they are private, not even child classes can access them directly.



One Image in a JFrame

In this section we'll look at some differences between JApplets and JFrames. In the process, we'll learn a lot about browsers and operating systems. You should also read section14.4 of our textbook, starting on page 544 about applets vs applications.

Fo now let's see what we need to do to make a JFrame that works like the applet above.

Make a new class (still in your lab11 package of course) called MyMouse2. Tell Eclipse you want to extend JFrame and click the box that says you want a main method. Then copy the code from MyMouse1, starting at "// Constants" and finishing just before the last closing brace; and paste it just above the line declaring method main. Notice the errors that Eclipse tags:

  1. base = getDocumentBase()
  2. micky = getImage(...)

These reflect a difference between JApplets (expected to run in a browser, very likely over the internet) and JFrames (expected to run as standalone applications on a single computer). getDocumentBase is all to do with finding out the URL (Universal Resource Locator -- or web-address) where the applet is, and where, presumably, associated resources like the image are sitting. It doesn't make sense for a JFrame to need a URL. Look at the documentation for JApplet where you will find a link to the method Image getImage(URL url, String name) that actually is inherited from the Applet class. Clearly, this getImage method is not appropriate for a JFrame. So how does a JFrame load an image?

Check out the documentation for the JFrame class and you will not find any getImage methods. Not even inherited methods from ancestor classes. The good people at Sun made a design decision that JFrames do not themselves load images. So how do you get images into a JFrame?

The good people at java.sun are pretty smart. Why do you think they made image loading into JApplets different from image loading into JFrames. One reason might be that JApplets run in browsers on the internet and things like browsers and URLs work exactly the same way for Windows, for Unix, for Macs, etc. But file systems on Windows, on Macs and on Linux systems can differ substantially. So Java has an intermediate step for loading images from a file syste. It uses a Toolkit intermediary. The Toolkit provides identical functionality on different platforms from the point of view of the rest of the program. But it may be doing vastly different things in order to work its magic. A Windows Toolkit accesses a differently organized file system from what a Mac Toolkit does.

In short, a Toolkit abstracts the notion of getting stuff from an operating system. Take a look at the documentation for Toolkit and you'll see what I mean.

As a result, here's one way to load images into a JFrame:

        Toolkit tk = Toolkit.getDefaultToolkit();
        micky = tk.getImage("mighty_mouse.jpg");
        mt = new MediaTracker(this);
        mt.addImage(micky, 1);
        try {
            mt.waitForAll();
        }
        catch(InterruptedException ie) {}

Notice that getDefaultToolkit() is a static method in the class Toolkit.

You need to delete a couple lines from your init() method and add a couple.

There are a few more steps:

  1. Add the line new MyMouse2() to the main method, so you will create a MyMouse2 object.
  2. This requires adding a constructor. You'll need to do everything that init() does -- so call init(), plus you'll need to setVisible(true) and setDefaultCloseOperation(EXIT_ON_CLOSE). As we've discussed in class, these are necessary because of design decisions my the good people at java.sun who decided what the defaults should be. (Default JFrames are not visible, and thier "go-away_box"es do not work.) Here's my constructor:
        public MyMouse2() {
            init();
            setVisible(true);
            setDefaultCloseOperation(EXIT_ON_CLOSE);
        }
    

One last thing. You will need a second copy of your .jpg file! That's because the URL of the running JApplet was that bin directory where you placed your jpg file. The place in the operating system's file system from where the JFrame is running is actually one level higher. So copy the image from the bin directory, and paste a copy of it in the parent folder. That folder will now contain folders src and bin as well as the copy of your image.

Exercise 2

Finish the JFrame MyMouse2 following the hints above. It should work just like my JApplet.


Adding Interaction



Click on this applet. Each time you click the mouse moves to a new random location. Clearly we have established interaction. But it's still basically your program as before. Just a bit more functionality.

In Eclipse create a new class. It should be in package lab10, its source folder should be lab10/src, its name should be something like MyMouse2Clickable, its superclass should be lab10.MyMouse2, it should implement the java.awt.event.MouseListener interface and you would like Eclipse to create method stubs for "Inherited abstract methods". Make sure your window conforms to this and go ahead and create the new class.

We would like to make a bigger JFrame than we had for MyMouse2. So we'll a different setSize call than was in MyMouse2.

We would like to listen for mouse clicks. So we'll need to addMouseListener(this).

Otherwise the initialization for the new program is just like the old. We are going to override the init() method of the parent class. We do that by writing an init() method in the new class that:

  1. does everything the parent class did -- hence super.init()
  2. makes us twice as big -- hence setSize(2*SIZE, 2*SIZE)
  3. adds a MouseListener, namely us! -- hence addMouseListener(this)

We need to write this method:

        public void init() {
                super.init();
                addMouseListener(this);
                setSize(2*SIZE,2*SIZE);
        }

The only other thing that the new program has to do beyond what MyMouse2 did is respond to mouse clicks. Eclipse has already provide you with a stub

{
     // TODO Auto-generated method stub

}
for the public void mouseClicked(MouseEvent arg0) method. All you need to do is write your own code for the method. For example, to make a JFrame just like my applet above, you can write:
        public void mouseClicked(MouseEvent arg0) {
                mickyX = rng.nextInt(getWidth()-micky.getWidth(this));
                mickyY = rng.nextInt(getHeight()-micky.getHeight(this));
                repaint();
        }

rng is a random number generator that I declared as an instance variable outside of all the methods as protected Random rng = new Random();



Exercise 3

First follow the steps described above to be sure you can make a JFrame that behaves like my applet above. Now that you know how to do that, I want you to extend your MyMouse3 class differently. Please use your imagination and creativity. For an example, look at my applet below.





This applet extends the class of the previous applet. Click once to start it. Then as you slide your mouse around you should see Mighty Mouse moving to avoid you. Try to click near the middle of Mighty Mouse. When you succeed, he'll shrink (so it's harder to get him again). When you miss, he'll grow (so it'se easier to hit him next time). When you have three more hits than misses, the game ends with you winning. If you miss three more times than you hit, you will lose. In either case, you can click to restart. The slider at the bottom lets you set the difficulty level of the game.

If you're interested, I wrote

public class MyTimidMouse extends MyMouse3 implements MouseMotionListener 
where MyMouse3 was the class for the previous applet. Notice I've now become a MouseMotionListener now too, so I can respond to mouseMoved and mouseDragged events. If you'd like to see how I tried to make the image move away from the user, here's my code. I get the x and y coordinates of where the mouse is by sending getX and getY messages to the mouse mouseEv. Then I calculate mx and my as the coordinates of the center of the image. Then dx and dy will be the differences between the mouse and the image center coordinates. Then I try to move the image to a point that's further away from me, in fact by dx*6/5 and dy*6/5 instead of dx and dy. And then I have to make sure the image is all in my rectangle. If not, I'll have to reappear at the opposite edge. The reason for the counter going from MAXCOUNTER down to 0 and then resetting is to prevent my computer from responding to too many mouse movement events. That would lead to a hopeless blur. Instead I only respond to one in MAXCOUNTER mouse move events. Increase MAXCOUNTER to slow down the action, increase it if you want a lot of speed (that's basically how my JSlider works). So here it is, my code for handling mouse moves:
  public void mouseMoved(MouseEvent mouseEv) {
    int x,y,mx,my,dx,dy,newmx,newmy;
    if (counter == MAXCOUNTER) {
      x = mouseEv.getX();               // Where is
      y = mouseEv.getY();               // the mouse?
      mx = mickyX + micky.getWidth(this)/2;        // Here is the
      my = mickyY + micky.getHeight(this)/2;       // center of the image
      dx = mx - x;              // dx and dy
      dy = my - y;              // give the distance between
      newmx = x + dx*6/5 - micky.getWidth(this)/2;    // make the distance to
      newmy = y + dy*6/5 - micky.getHeight(this)/2;   // newmx, newmy bigger
      // Now make sure we're in the visible area:
      while (newmx < 0) newmx = newmx + getWidth();
      while (newmx > getWidth() - micky.getWidth(this)) newmx = newmx - getWidth() + micky.getWidth(this);
      while (newmy < 0) newmy = newmy + getHeight() - SLIDERSPACE;
      while (newmy > getHeight() - SLIDERSPACE - micky.getHeight(this)) newmy = newmy - getHeight() + micky.getHeight(this);

      //Now move Micky
      mickyX = newmx;
      mickyY = newmy;
      repaint();
    }
    if (--counter == 0) counter = MAXCOUNTER;
  }

Your applet need not be as complicated as this. All I ask is that you extend your MyMouse3 class imaginatively.



Adding a timer

Take a look at the API documentation for the class javax.swing.Timer. It's an object that can fire events at regular intervals. We're going to use it to modify MyMouse3



so that the image will disappear for a second then reappear for a second... like this



You will extend MyMouse3 (or, if you particularly like your program from the previous exercise, you are welcome to extend that instead). Begin by adding a Timer:

protected Timer flasher;
I have named mine flasher. Eclipse will tell you to import a Timer -- be sure you choose javax.swing.Timer (there are others around that won't behave the same way).

In your init() method, or perhaps in your constructor, you will need to instantiate the Timer:

    flasher = new Timer(1000, this);
The 1000 means that flasher will cause an event once every 1000 milliseconds (or once per second, as most humans would prefer to put it!). this is necessary because the timer needs an ActionListener to respond to its once-per-second signals. At this point you should add the words implements ActionListener to your class header. Mine now reads:
public class MyMouse4 extends MyMouse3 implements ActionListener 

Eclipse will offer to import java.awt.event.ActionListener and will offer to add unimplemented methods. You should accept both offers. You will then have a file that looks like this:

package lab11;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;

public class MyMouse4 extends MyMouse3 implements ActionListener {
        protected Timer flasher;

        public void actionPerformed(ActionEvent arg0) {
                // TODO Auto-generated method stub
                
        }
}

The action I want to add is to make the image visible if it is currently not visible, or invisible if it is currently visible.

So add a protected boolean visible and give it an initial value of true.

Then the action performed will be

  1. setting visible to the logical not of itself.
  2. Making sure the screen is repainted

Finally, you need to arrange that the image will only be painted if visible is true.

So you need to override the public void paint(Graphics g) method that was inherited from the parent class(es).

You should look a defined public void paint(Graphics g) method in your most recent ancestor. Its code is probably something like:

                super.paint(g);
                g.setColor(Color.white);
                g.fillRect(0,0,getWidth(),getHeight());
                g.drawImage(micky, mickyX, mickyY, this);
Copy the method and paste it into your new class. You need to put if (visible) in front of one of those lines.

You're almost done. The last step is to start your timer! If you don't do that, it won't work!

Exercise 4

Follow the steps above and make your previous exercise have a flashing image. This should be very easy.

You really do have all the ingredients necessary to create your own game like the one I showed you at the start of this lab manual. Here it is again:



Here are some differences that you will need to work on:

  1. You need two images not one.
  2. Instead of flashing on an off you need to switch between the two images
  3. You need somewhat different responses to a mouse click depending on who is showing
  4. You need to be able to display images at different sizes.



Exercise 5

Create a game that's comparable to or (more likely knowing you) better than my Micky-Taz game.




Ball World

Ball World consists of a set of programs developed originally by Dan Hutchins and Rhys Price Jones. That was in the early 1990s! It has been extensively updated and improved by, among others, Stephen Wong and John Donaldson and Rhys Price Jones again. Try it!




Yes! It's ok to hit the "Go!" button more than once.



Two classes (at least!)

Observe first of all that in order to program Ball World you need two distinct forms of object.

  • There are Ball objects, and
  • There is some kind of container in which the Balls move around



Balls

A Ball is an object. To create objects, we need a class. So you need:

public class Ball
Let's look first at the problem of drawing moving Balls. Here are some observations:
  • A ball has a position
  • A ball has a velocity
  • A ball has a size
  • A ball has a color
  • The position of each ball changes very often
  • When it hits a "wall" the ball bounces off and its velocity changes
Those first three attributes: position, velocity and color. Additionally a Ball object should know about the area in which it is bouncing around. This will be a Canvas. All of these attributes constitute part of the state of a ball object. It is good programming practice to use private variables to contain an object's state. But if there is any chance you might extend the class, you should make them protected rather than private. In our case:
        protected Canvas canvas;
        protected int xpos, ypos;
        protected int xvelocity,yvelocity;
        protected int diameter;
        protected Color color;
Notice, we are already using two classes Color and Canvas. Where are they?

So now we know we need to start

import java.awt.Color;
import java.awt.Canvas;
public class Ball...
But, from our study of the java documentation, we know it's very likely we'll need a lot of classes from the java.awt package. So:
import java.awt.*;
public class Ball
is a better beginning.

Next observe that the ball needs to change its position very often. For this reason it needs something like a move() method:

        public void move(){
                xpos+=xvelocity;
                ypos+=yvelocity;
        }
And it needs to be able to draw itself. To draw in Java requires a Graphics object.

Also by looking at the documentation, it's not hard for us to come up with


        public void draw(){
                Graphics myGraphics = canvas.getGraphics();
                myGraphics.setColor(color);
                myGraphics.fillOval(xpos,ypos,diameter,diameter);
        }

As usual, in object-oriented programming and design, we have designated state as private or protected variables, and behavior as public methods.

Did you notice that our move() method is wholly inadequate. As it is, a ball would, as soon as it gets to the edge of our window, disappear from sight, never to return. For this reason, a ball needs to be aware of its environment. If it knows the height and width of the window in which it's moving, then it can "bounce" appropriately. Look at this code until you understand it!


        public void move(){
                int width = canvas.getWidth();
                int height = canvas.getHeight();
                xpos+=xvelocity;
                if(xpos+diameter > width){
                        xpos = xpos - 2*(xpos+diameter-width);
                        xvelocity = -xvelocity;
                }
                if(xpos < 0){
                        xpos = -xpos;
                        xvelocity = -xvelocity;
                }

                ypos+=yvelocity;
                if(ypos+diameter > height){
                        ypos = ypos - 2*(ypos+diameter-height);
                        yvelocity = -yvelocity;
                }
                if(ypos < 0){
                        ypos = -ypos;
                        yvelocity = -yvelocity;
                }
        }
One more observation. The ball can figure out width and height if it knows what it's being drawn on. We're going to use a Canvas. Look up the documentation so you'll know why. In some applications, you may want to access and update the velocity components. Although it's likely we won't need the accessors and modifiers for our Ball World application, it's good practice to include:

        public int getXVelocity(){
                return xvelocity;
        }

        public int getYVelocity(){
                return yvelocity;
        }

        public void setXVelocity(int xv){
                xvelocity = xv;
        }

        public void setYVelocity(int yv){
                yvelocity = yv;
        }


Who moves the balls?

We've seen all the code needed so that Ball objects can be constructed, so that they know their own position, velocity, size, color and also so that they know their surroundings (the Canvas). In this way, they can move themselves and draw themselves.

But how do they know when to move and when to draw themselves?

This is a big big big big question. That's what design is all about. So far we've developed a simple object that knows where it is, what it looks like and how to update itself. Some (poor) designers would now extend the model and add more functionality to the ball. They will tell it to redraw itself, say 50 times per second. (That's one standard that's used for animation.) So now we have to add a clock or a timer to the state of each ball. And maybe the clocks need to talk to each other to make sure they keep similar time? That would quickly become a coder's nightmare.

We prefer to have a manager who will keep track of all the balls, and will simultaneously send a signal to all of the balls at once telling them to redraw themselves. An individual ball will not have a timer. Instead, we'll provide a public method so that an object that does have a timer can tell the ball it's time to move and draw itself:

        public void respond(){
                move();
                draw();
        }

We'll use a design pattern called Observer. (It's also called Dependents or Publish-Subscribe.) In 1994 Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (who we will henceforth refer to as the Gang of Four) published an influential text called Design Patterns: Elements of Reusable Object-Oriented Software. They observed that many programming problem solutions involve similar design issues, and proposed a set of "design patterns" to cover the different scenarios.

The intent of the Observer pattern is to define a many-to-one dependency between objects so that when one object (a timer in our main class) changes state, all its dependents (the many balls that have been created) are all notified and will do their own updates automatically.

As the gang of four state:

"A common side-effect of partitioning a system into a collection of cooperating classes is the need to maintain consistency between related objects. You don't want to achieve this consistency by making the classes tightly coupled, because that reduces their reusability."

To be as flexible as possible, we define an interface CanBeTold. All objects that implement this interface will have a respond() method taht will be called whenever a manager tells them.

So we begin the Ball class with

public class Ball implements CanBeTold

CanBeTold is simply an interface that guarantees that the object will indeed have a respond() method:

/**
 * Objects that CanBeTold have a respond method.  When they are sent a
 * respond() message, they will do execute that code.
 */
package lab10;

/**
 * @author rhyspj
 *
 */
public interface CanBeTold {
        public void respond();
}

To enter this code in Eclipse, be sure to create a new Interface. (All the code is in a .zip file which I will link you to shortly.)





The Manager

So now we know that each of the Ball objects implements CanBeTold. Who is going to do the telling? Let's call the object they are waiting for instructions from a Manager.

Here is a Manager that lets us store a bunch of CanBeTold objects (not necessarily just Balls):

This is a Manager. It contains a data structure into which we can store Observers. We chose to use a java.util.Vector (hence the import statement at the top). We have the following functionality in a Manager:

  • You can register any object that implements CanBeTold,
  • You can send a respond() message to all the registered CanBeTolds,
  • You can clear the system and get rid of all the CanBeTolds.

Notice that the Manager keeps all its CanBeTold objects in a Vector of CanBeTolds. This is rather like the list in Alice. Except that you can specify just what types will be kept in there.

The individual Ball objects are all CanBeTolds, and they are all bouncing around in a window of some kind. We will use a JApplet for this window.

We will define a class Ballworld to extend JApplet. Within our Ballworld we will place a Manager. As we create Ball objects, we will register them with the Manager. In this way, the CanBeTolds will be the balls and the Manager will be the JApplet. Let's get cooking with our first Ballworld: TinyBallWorld.

At this point you may want to download a collection of files that will enable you to set up the first few BallWorlds: BallCode1. The runnable program here is TinyBallWorld.

Here's the state that we maintain for TinyBallWorld:

        protected JButton goButton;
        protected JPanel buttonPanel;
        protected BWCanvas canvas;
        protected Manager ballDispatcher;
        protected javax.swing.Timer animationTimer;

We've discussed the BWCanvas and the Manager objects. The JButton starts the applet, and it sits in a JPanel. The Timer is used to control the animation. Basically we will initialize it to tick at some rate (50 seems to work quite well). When a timer hits zero (the end of a tick) it generates an ActionEvent and then resets itself. We will arrange to catch the ActionEvent and use it to trigger the screen erase and redrawing of the Balls.

The code to listen for, and act upon these Timer-generated ActionEvents is most conveniently located in an anonymous inner class, like this:

        animationTimer = new Timer(50, new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    canvas.repaint();
                }
            });

Similarly we'll want an action listener on the button. Here's the code:

                goButton = new JButton("Go!"); // create a JButton object
                buttonPanel.add(goButton);  // add it to the button panel

                goButton.addActionListener(new ActionListener(){
                        public void actionPerformed(ActionEvent e){
                                int xc = (canvas.getWidth()-15)/2;
                                int yc = (canvas.getHeight()-15)/2;

                                Ball b1 = new Ball(canvas, xc, yc, 
                                                3, -3, 30, Color.black);
                                ballDispatcher.register(b1);
                                Ball b2 = new Ball(canvas, xc, yc, 
                                                -3, -3, 30, Color.blue);
                                ballDispatcher.register(b2);
                                Ball b3 = new Ball(canvas, xc, yc, 
                                                -3, 3, 30, Color.red);
                                ballDispatcher.register(b3);
                                Ball b4 = new Ball(canvas, xc, yc, 
                                                3, 3, 30, Color.yellow);
                                ballDispatcher.register(b4);
                        }
                });
Every time you hit the goButton you will trigger this action to launch four balls.

The rest of the details of the initialization of the applet involve placement of the components. Ask a lab instructor if in any doubt.



Over to you

Did you see how the code to make the peculiar moving pattern in the applets above basically resides in the actionPerformed method of the anonymous listener on the "go" button. You can make different versions of this to produce different ball worlds. Try some of these:



Exercise 6

Make an applet that behaves like this




Name the class you hand in BallWorld2. Let me emphasize: The only place you need to make changes to TinyBallWorld is in the actionPerformed method of the listener on the go button.

and again:



Exercise 7

Make an applet that behaves like this




Name the class you hand in BallWorld3. Let me emphasize once again: The only place you need to make changes to TinyBallWorld is in the actionPerformed method of the listener on the go button.

For the next exercise, be as creative as you can.

Exercise 8

Create your own BallWorld. Be artistic. Be creative. You will be graded on aesthetics this time. Name the class you hand in BallWorld4.





Class Extension

Here is a class, called simply BallWorld, that lets you launch as many randomly sized, randomly positioned, and randomly moving balls as you could wish.


package lab11;

/**
 * BallWorld -- the famous bouncing ball applet can be run either
 * as an applet or an application
 * @author Rhys Price Jones
 * @version 14xi08
 */

import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;

public class BallWorld extends JApplet
{
        // constants
         
        protected final int SIZE = 500;
        protected final int UPDATE_FREQ = 50;  // update the image every 50 milliseconds

        protected JButton addBallButton;
        protected JButton clearButton;
        protected JPanel buttonPanel;
        protected BWCanvas canvas;
        protected Manager ballManager;
        protected javax.swing.Timer animationTimer;
        protected Random rng;
        protected Graphics g;
        
        public void init()
        {
                setSize(SIZE,SIZE);
                animationTimer = new Timer(UPDATE_FREQ, new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
                                canvas.repaint();
                        }
                });
                rng = new Random();
                ballManager = new Manager();
                canvas = new BWCanvas(ballManager);

                Container contentPane = getContentPane(); 
//              Get a reference to the frame's content pane

                contentPane.add("Center",canvas); // add the canvas to the content pane

                buttonPanel = new JPanel();  // create a JPanel object
                buttonPanel.setBackground(Color.gray);
                buttonPanel.setLayout(new GridLayout(0,1));    // all in one column
                contentPane.add("East",buttonPanel);  
//              add it to the right side of the content pane

                addBallButton = new JButton("Add Ball"); // create a JButton object
                buttonPanel.add(addBallButton);  // add it to the button panel
                clearButton = new JButton("Delete all Balls"); // create a JButton object
                buttonPanel.add(clearButton);  // add it to the button panel

                addBallButton.addActionListener(new ActionListener(){
                        public void actionPerformed(ActionEvent e){

                                //  Choose a random diameter from 40 to 79 pixels
                                int diameter = (rng.nextInt(40))+40;

                                //  Choose a random initial velocity  1 to 7 or negative
                                int xvelocity = rng.nextInt(7)+1;   
                                if (rng.nextInt(2) > 0) xvelocity = -xvelocity; 
                                int yvelocity = rng.nextInt(7)+1;   
                                if (rng.nextInt(2) > 0) yvelocity = -yvelocity; 
                                //  Choose a random initial position
                                int width = canvas.getWidth();
                                int height = canvas.getHeight();
                                int xpos = rng.nextInt(width-diameter);
                                int ypos = rng.nextInt(height-diameter);
                                //  Choose a color with random values for red, green, blue
                                Color color=new Color(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
                                Ball b = new Ball(canvas,  xpos, ypos,
                                                xvelocity, yvelocity, diameter, color);
                                ballManager.register(b);
                        }
                });

                clearButton.addActionListener(new ActionListener(){
                        public void actionPerformed(ActionEvent e){
                                ballManager.clear();
                        }
                });

                setVisible(true); // make the applet visible on the screen
                g = canvas.getGraphics();

        }

        public void start(){
                animationTimer.start();
        }

        public void stop(){
                animationTimer.stop();
        }


        public static void main(String args[]) {
                JFrame f = new JFrame("Balls");
                f.setBounds(100,100,900,700);
                f.setTitle("Balls Application");
                f.setVisible(true);
                JApplet applet = new BallWorld();
                applet.init();
                applet.start();
                f.setContentPane(applet.getContentPane());
                f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
                f.validate();
        } 
}

It works like this:




If you look at the code, you will see that this can run either as an applet or as an application. Tricky eh?



Would you prefer?

But what about this applet?




It's not much harder. You just need a few more buttons and associated listeners. For example, here's the listener for the square ball:

                addSquareBallButton.addActionListener(new ActionListener(){
                        public void actionPerformed(ActionEvent e){


                                //  Choose a random diameter from 40 to 79 pixels
                                int diameter = (rng.nextInt(40))+40;

                                //  Choose a random initial velocity  1 to 7 or negative
                                int xvelocity = rng.nextInt(7)+1;   
                                if (rng.nextInt(2) > 0) xvelocity = -xvelocity; 
                                int yvelocity = rng.nextInt(7)+1;   
                                if (rng.nextInt(2) > 0) yvelocity = -yvelocity; 
                                //  Choose a random initial position
                                int width = canvas.getWidth();
                                int height = canvas.getHeight();
                                int xpos = rng.nextInt(width-diameter);
                                int ypos = rng.nextInt(height-diameter);
                                //  Choose a color with random values for red, green, blue
                                Color color=new Color(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));

                                Ball b = new SquareBall(canvas,  xpos, ypos,
                                                xvelocity, yvelocity, diameter, color);
                                ballDispatcher.register(b);
                        }
                });

It adds a new Square Ball.



Exercise 9

Make some similar extensions of the Ball class, and make a class MultiBallWorld with extra add buttons so that you can launch them all.







How to submit your homework

Create a folder on your computer and name it 'CS053-Spring10' (if you do not have it already). Create another folder and name it 'lab10'. In the newly created 'lab10' folder copy your solutions to the exercises: Exercise1.java, Exercise2.java, Exercise3.java, and so on...

Next, go back up to your folder 'CS053-Spring10', right click on your folder 'lab10' and compress it either in a or file.

Your newly compressed file should be named:

  • FirstName_LastName_Lab_10.zip or
  • FirstName_LastName_Lab_10.rar

After you have compressed your homework, then proceed to submit it via Blackboard.

Got problems? If you have any problems make sure you clear them with your lab instructor because if you do not follow these requirements for submission your lab homework submissions will not be accepted and you will get zero points.