CPSC 124 (Winter 1998): Lab 6
Applets and Objects II


IN THE SIXTH LAB for Computer Science 124, you will write a class from scratch, and you'll create an applet that processes keyboard events.

You'll need the project folder named "Lab 6 Starter" from the cpsc124 folder on the network. For the second exercise in the lab, you'll need a copy of the "Keyboard Applet Starter" folder. Copy these folders into the java folder on your M drive.

(By the way, you might want to do some cleaning up of your M drive. The computers in the lab don't have a "Recycle Bin" where you can throw unwanted folders and files. But you can still delete items. One way to do this is to right-click on the item you want to delete and select the "Delete" command from the pop-up menu that appears. Alternatively, you can hilite the items you want to delete and press the Delete key.)

The exercises at the end of the lab are due in class on the Monday following the lab.


Outline of the Lab


Creating a Class to Order

If you have not already copied the "Lab 6 Starter" folder into the java folder on your M drive, do so now. Open your copy of the folder and click on the .dsw file to open the project. You will see a main program called "BankMain." However, you will not be able to run this program because it uses a class called "BankAccount," and no definition has been provided for this class.

Your first exercise for the lab is to write a BankAccount class that can be used with the existing main program. You should not make any changes to the main program. Just read it, so that you know what is required of the class you have to write. (A copy that you can read on-line is here.)

To begin working on your class, create a new file where you can type it. To do this, select the "New" command from Developer Studio's "File" menu. In the dialog box that appears, select "Java Source File" as the type of file that you want to create. Type in the name of the file, which must be "BankAccount.java". Make sure that the checkbox labeled "Add to Project" is checked. Then hit the "OK" button. An empty file will be created and added to the list of files in the project.

Your assignment is to write a BankAccount class. An object of this class keeps track of the balance in a bank account. It also knows the interest rate for the account. (This tells you what instance variables the class must have.) The class must define the following instance methods:

The BankAccount class also must define a constructor. The constructor takes two parameters: The initial opening balance of the account and the annual interest rate for the account. (The monthly interest rate used in the addMonthlyInterest method is defined to be the annual rate divided by 12.)

Once you have correctly defined the BankAccount class, you should be able to compile and run the BankMain program. (Make sure that the computations done by the class are semantically correct.)

Note that the BankAccount class that you write for this exercise is not meant to be interesting or useful. For example, you should not worry about possible errors in the use of the class, such as trying to deposit a negative amount of money. The point is simply to write a complete class definition from scratch. The methods in your class will be very short -- only about one or two lines each.


The Asynchronous Mindset

In the second exercise of the lab, you'll be writing an applet that processes keyboard events. A keyboard event is generated each time the user presses a key on the keyboard. These events occur asynchronously. An applet handles asynchronous events by providing event-handler methods that the system calls whenever there is an event to be processed.

An applet can have event-handlers for keyboard events, mouse events, and other types of events, as discussed in Section 5.2 of the text. The problem is that these events can arrive at any time, even while other activities are in progress. The event-handler methods are called by a "user interface thread," which is created and run automatically by the system. However, the applet can create other threads that can run concurrently, and it is important that all these different threads not interfere with each other.

Keyboard events present an additional complication, since in order to receive keyboard events, an object must have the input focus. In Java, an object is notified when it gets or loses the input focus. When it has the focus, it should ideally be hilited or otherwise marked, so that the user can be sure where his or her keystrokes are going.

All these complications can be difficult to deal with, so I have provided a "Keyboard Applet Starter" that does a lot of the dirty work. If you have not already copied the "Keyboard Applet Starter" folder onto your M drive, do so now. Open your copy of the folder and double-click the .dsw file to open the project. Read the comments in the KeyboardApplet file. It is not necessary, or even a good idea, to read anything past the "pubic void init()" method. (A copy that you can read on-line is here.)

The idea in this applet is to have a "board" that displays some on-going action (as long as the applet has the input focus). At the same time, the user's keyboard events can also change what is shown on the board. To keep things organized, all redrawing of the board is done in a method called redrawBoard(). This method is called over and over by the applet's run() method. Complete information about the contents of the board is stored in the applet's instance variables. The redrawBoard() method uses these variables to decide what to draw. The keyboard event handler, keyDown(), need only make changes to the instance variables that describe the contents of the board. The next time the board is redrawn, these changes will automatically appear on the screen.

(By the way, the board in this applet is an off-screen image. This board is redrawn in the computer's memory, not on the screen, so the process is not visible to the user. Then the complete board is quickly copied onto the screen. This is an important technique in computer graphics, since it cuts down on the annoying flickering that can occur if the screen is redrawn directly. See the last part of Section 5.4. You don't have to understand how off-screen images work. You just have to use the instance variable named "osg" to draw to the board.)

You'll notice that most of the methods in the KeyboardApplet class are declared to be synchronized. This is necessary to "synchronize" the activities of two threads, the user interface thread and the applet's run() thread. The effect is to stop the two threads from accessing the same resources at the same time. For example, we don't want the paint() method to try to copy the board onto the screen while the redrawBoard() method is in the middle of redrawing it. The screen would show an incomplete board! This possibility is prevented by declaring both the paint() method and the redrawBoard() method to be synchronized. This is one of the complexities that have already been taken care of for you in the Keyboard Applet Starter.


However, you still have a fairly complex task ahead of you. You have to program a non-trivial applet, and you have to do it in an asynchronous, multi-threaded environment. To be successful at this, you have to warp your mindset a bit to fit the environment. The basic rule to follow is: Make sure every synchronized method runs only briefly when it is called. Otherwise, you can end up with one thread blocked from executing because it needs a resource that is in use by another thread. If the user interface thread is blocked, then the applet will stop responding to the user. If the applet's run() thread is blocked, then the action on the screen will stop.

What this means for you in this lab is that you should not write any for loops or while loops, even though you will be tempted by your traditional mindset to do so. Instead, you should use variables to keep track of the state of the applet. For example to show a ball falling down the screen, you might be tempted to write something like:

                 ballTop = 50;  // starting height of ball
                 while (ballTop < board_height){
                     ... // draw the ball at height ballTop
                     ... // put in some delay so it doesn't move too fast
                     ballTop += 10;
                 }

Don't do it! This loop will lock up the applet so that nothing else will happen except for the falling ball. So instead of the loop, you might have a boolean instance variable called ballIsFalling. When you want the ball to start falling -- for example, because the user has pressed a key -- you can set

                 ballIsFalling = true;
                 ballTop = 50;

The redrawBoard() method can then include something like this:

                 if (ballIsFalling) {
                    ... // draw the ball at height ballTop
                    ballTop += 10;
                    if (ballTop >= board_height)
                        ballIsFalling = false;
                 }

As the redrawBoard() method is called over and over, the ball will gradually move down the board. There is no need to insert a delay, because the applet already inserts a delay between calls to drawBoard(). In the meantime, all kinds of other things can be happening.


An Applet with Keyboard Events

The applet that you will write for the second exercise of the lab is a simplified (but still typically violent) action game in which the user controls a boat that tries to blow up submarines by dropping depth charges. A sample of the completed applet can be found on a separate page. You are free to be creative about the details, but your applet should have the same sort of functionality as this one.

Note that there is only one boat, one depth charge, and one submarine at any given time. You can use instance variables to store the positions of these objects.

Because the object of the exercise is not fancy graphics, I suggest that you use simple shapes to represent the boat and other objects. You can use these methods with the instance variable osg:

You should write the applet in stages, testing each stage. Here are my suggestions:

The boat:

The boat moves back and forth near the top of the board. You need an instance variable to give its horizontal position. The value of this variable changes when the user presses the left or right arrow key. But be careful that the boat cannot move off the left or right edge of the board. Note that the boat, like everything else, should actually be drawn in the redrawBoard() method.

The depth charge:

The depth charge is sometimes attached to the bottom of the boat, and at other times it is falling. You will need an instance variable to distinguish between these two possibilities. (See the discussion of falling balls, above.) The depth charge starts falling when the user hits the down arrow key. If and when it reaches the bottom of the board, it reappears attached to the boat. (You can think of this as being a new depth charge, but the program should think of it as being the same old depth charge in a new position.)

Hits and misses:

You have to keep track of and display the number of hits and misses that the user makes on the submarine. Add instance variables to keep track of this information. Whenever the falling depth charge reaches the bottom of the board, the number of misses goes up by one. (You can't do hits until you have a submarine.)

The submarine:

Add a submarine moving back and forth near the bottom of the board. You will need an instance variable to give its horizontal position. You'll need another instance variable to say whether it is moving left or right. If it gets all the way to the left edge of the board, it should start moving right. If it gets all the way to the right edge, it should start moving left. To make things more interesting, it should also have some small probability of changing direction spontaneously. Remember that the redrawBoard() routine only moves the submarine a short distance each time it is called.

Detecting hits:

The subroutine blows up if a falling depth charge gets close enough to it. You will have to devise some test for this. The test is made in the redrawBoard() method. If the sub blows up, the number of hits goes up by one and the depth charge becomes reattached to the boat.

Explosions:

You might want to add some sort of visual explosion when a submarine is blown up. The explosion will have to take place over a sequence of several calls to redrawBoard(). You'll need a boolean instance variable to indicate whether an explosion is in progress. You'll need another variable to count off the number of calls to redrawBoard() since the explosion started. While the explosion is in progress, the depth charge should probably not be attached to the boat; it should not even be drawn. In my applet, the explosion changes appearance as it progresses. You might not want to make it that complicated.

As a final step, you should publish your completed applet on the Web.


Exercises to Turn in

Exercise 1. Turn in a printout of your BankAccount class, as described above. The class will be fairly short, but it should include a comment describing the class and a short descriptive comment for each variable and method in the class. (This exercise is for ten points.)

Exercise 2. Turn in a printout of your Keyboard Applet, as described above. (If you prefer, you can turn in a printout just of the parts of the file that you write or modify.) You do not have to change the comments that were in the file you used as a starter. You do have to include a comment for each variable you declare. If you add methods to the applet, they should also be commented, and you might want some comments on the code you write for the redrawBoard() method. In addition to turning in a printout, you should publish your applet on the Web. Tell me the URL where your applet can be accessed. (This exercise is for fifteen points.)


[ Lab Index | Online Notes ]