CSCI213 (aka ITCS907, alias MCS9213)
Autumn Session, 2007
Lab Exercise 2
This exercise is intended to improve your understanding of Java and gain a little more experience with the class libraries before you start Assignment 2.
The primary objective of Assignment 2 is to give you some experience of classes and inheritance. You will be creating classes that can be used with supplied framework code. Your classes will all implement a standard interface - they all do the same things, they just have their own individual ways of performing.
These preparatory exercises include:
Inheritance allows a programmer to exploit similarities in behaviours
to simplify a program's design. In the simplest case,
the programmer defines an interface that specifies
the common functionality and then creates a number of classes that all
implement this interface.
In more complex cases, you can have an elaborate hierarchy that may have an interface or an abstract base class specifying the essential commonalities together with subclasses and/or partially implemented abstract classes that have code implementing some or all of the inheritted functions and which define additional more specialized functionality.
The example for the exercise is a little program that maintains a "farmyard" where there may be many different kinds of animal. All animals eat, make noises, etc etc. The example here is a simplified version of the one used for Perl OO.
The essence of being an "animal" in this program is defined via the interface
animal:
public interface animal {
void getResources();
void identifySelf();
}
(An "animal" is something that can identify itself. There is also a getResources method; some animals used by the program need to do things like load sound files when they are created.)
Actually, this is NOT a good example of inheritance (sorry Perl guys, but you really have messed this up). The implementations defined for the different kinds animals are essentially all the same - there are no real differences in behaviour! Each kind of animal identifies itself in the same way - it plays a sound file (or prints a message if there is no sound file). Of course, the sounds differ - cows "moo", sheep "baah" (or vice versa if you get the sound files mixed up). But the code is essentially the same. These "classes" differ parametrically - they have different sound file parameters. One should NOT define different classes in such cases; a single class with some data members to hold the parametric data will suffice. However, if you were really trying to simulate a farmyard, then there would be many additional behaviours defined for the classes. These would have different implementations (think of behaviours - methods of the class - that express how the different kinds of animals eat or move).
The program is a little "menu select" style program offering commands that allow a user to add animals to the farmyard, or inspect the farmyard allowing animals to identify themselves. The "farmyard" is the heterogeneous collection of polymorphic references; it is simply a java.util.Vector holding references to different animals. The dynamic binding part in the "doVisit" function where each animal in the farmyard gets to identify itself.
The application is provided as a compressed NetBeans project
(in /share/cs-pub/csci213/Lab2). The main project folder includes
a "sounds" folder with a number of sounds in .au format.
(The AudioClip class in the Java libraries can be used to play
audio files in ".au", ".wav", ".midi", and ".aiff" sound formats.
It cannot play most of the other sound formats. With ".wav" files, it
is limited to simply "PCM" encoding - no compression, no stereo, no
high sampling rates. Most of the sound files that you find on
the Internet cannot be played. There are converters that will
convert a high quality sound file to (a much larger) ".au" file
that can be played.) The files making up the application are
shown in NetBeans Files view:
The main program is:
public class A2a {
private static Vector<Animal> theFarmYard;
public static AudioClip getSound(String name) {
// code discussed below
...
}
private static void doVisit() {
if(theFarmYard.size()==0) System.out.println("Silence, the farmyard is empty");
else {
System.out.println("Each animal in farm will identify itself and play sound");
System.out.println("Multiple sounds may play concurrently");
for(Animal a : theFarmYard ){
a.identifySelf();
}
}
}
(The highlighted loop in doVisit is where we get "polymorphism" and
"dynamic binding". The object reference variable Animal a
is polymorphic - many shaped - sometimes referencing a "Cow" object,
sometimes a "Rooster" object etc. The identifySelf method
call involves dynamic binding. At run-time, the Java system determines
whether to call Cow.identifySelf or Rooster.identifySelf.)
private static void doCreate(BufferedReader input) {
// code discussed below
}
private static void menuSelect(BufferedReader input) {
try {
System.out.println("Commands:" +
"\nc\tCreate animal from specified class" +
"\nv\tVisit farmyard" +
"\nq\tQuit");
for(;;){
System.out.println("->");
String line = input.readLine();
if("q".equals(line)) break;
else
if("v".equals(line)) doVisit();
else
if("c".equals(line)) doCreate(input);
else {
System.out.println("Don't understand, see commands as above");
}
}
}
catch(Exception e) {
System.out.println("Failed because " + e);
System.exit(1);
}
}
public static void main(String[] args) {
try {
BufferedReader input = new BufferedReader(
new InputStreamReader(System.in));
theFarmYard = new Vector<Animal>();
(Vector<Animal> theFarmYard is the heterogeneous collection;
it contains Animals of different (hetero) kinds (genus).)
menuSelect(input);
System.out.println("finished");
}
catch(Exception e) {
System.out.println("Failed because " + e);
}
}
}
Two Animal classes are defined in the NetBeans project supplied - Rooster and Cow. The Navigator/Inheritance view can show hierarchies - not that interesting in this trivial case:
As shown in the diagram, class Rooster inherits from the Animal interface and, as always, from java.lang.Object.
The Rooster class is:
public class Rooster implements Animal {
private static int counter = 0;
private static AudioClip theSharedSound;
private int myId;
/** Creates a new instance of Rooster */
public Rooster()
{
myId = ++counter;
}
public void getResources() {
if(theSharedSound==null)
theSharedSound = A2a.getSound("Rooster.au");
}
public void identifySelf() {
System.out.println("Rooster #" + myId + " crowing");
if(theSharedSound!=null)
theSharedSound.play();
}
}
Each rooster gets its own identifier. Only one copy of the sound resource gets loaded; these roosters all crow the same way.
The program has to be able to create animals that are instances of the different classes. Obviously, this part of the code has to know about the different possible classes. An initial version of the code is:
private static void doCreate(BufferedReader input) {
try {
// Modify this to create your animal types
System.out.print("Enter class of animal:");
String line = input.readLine().trim();
if(line.equals("Rooster")) {
Rooster aRooster = new Rooster();
aRooster.getResources();
theFarmYard.add(aRooster);
System.out.println("done");
}
else
if(line.equals("Cow")) {
Cow aCow = new Cow();
aCow.getResources();
theFarmYard.add(aCow);
System.out.println("done");
}
else System.out.println("Don't know how to create a " + line);
}
catch(Exception e) {
System.out.println("Failed because " + e);
System.exit(1);
}
}
This code can be extended as more animal sub-classes are defined.
Really it is better to separate such code out from the main program so that there are no explicit references to the different animal subclasses. This can be done by defining a separate helper "factory" class that has a static method like "createAnimal". This factory class would be something along the following lines:
public class AnimalFactory {
public static Animal createAnimal(BufferedReader input) { ... }
}
The main line code could simply make an invocation on this static class function.
Java can handle sound files in some formats. The code for loading sounds is all supplied by classes in the standard Java libraries. However, the required functions are distributed among a number of classes and the coding is a bit low level. The following fragment packages the code:
public static AudioClip getSound(String name) {
String baseName = System.getProperty("user.dir");
String fileURL = "file:" + baseName + "/sounds/" + name;
AudioClip theClip = null;
try {
theClip = Applet.newAudioClip(
new java.net.URL(fileURL));
}
catch(Exception e) {
System.out.println("Failed to load sound '"
+ name +"'");
System.out.println(e);
System.exit(1);
}
return theClip;
}
This static function loads a file from a "sounds" subdirectory of the current working directory (by default, the working directory of a NetBeans program is the main project directory). Although a function rather than a complete class, this is a fragment of reusable code. You can cut and paste it into some helper class in any Java program where you need to load sound files. Here, for convenience, it was added to the A2a main class.
Assignment 2 involves some graphical and, optionally, audio output. Instances of the classes that you develop for A2 have to display themselves. The different classes could define different display code, though in this year's assignment that part of the code is probably the same for all your classes (and so should be defined in a base class).
The lectures will not have started exploring the Java classes for constructing graphical user interfaces. That does not matter. All the complex parts of the graphical output are supplied for you.
But graphics applications do allow for simple experimentation with hierarchies. In this laboratory class, you should try defining a hierarchy of different shape classes whose instances can be displayed graphically. Your main code will work entirely in terms of collections of shapes; at run time you can have rectangle, circle, polygon etc shapes.
Java has two graphics libraries: java.awt (AWT) and javax.swing (Swing).
Both use the same Graphics class for application specific graphical
output. Both provide collections of standard graphical components (windows,
buttons, textfields, etc).
A program
using AWT may be a little faster; a Swing program probably
looks better. At the base level, java.awt.Component, they
share some commonalities. Many of their subclasses are also
similar; for example there is
a java.awt.TextField class for text input that matches with the javax.swing.JTextField
class.
You pick whichever library is more convenient (don't try to mix them, it
doesn't always work). Some exercises and assignments use AWT, some use Swing.
A graphical user interface (GUI) will consist of:
The hard part of writing a GUI is the creation of all the objects and allocation of specific visual areas to these objects. Integrated development environments (IDE) often have visual editors that allow you to construct the GUI using "point-and-click" style operations (the IDE then creates the necessary Java code). The actual layout of different elements within a window is the most tiresome part; you have to consider issues such as what happens when the window is resized (which, if any, of the elements should grow or shrink - and by how much).
Once a GUI has been constructed, it is easy to use. Does it have an "action button"? You don't need to be concerned about how or when the button gets drawn, or how it appears when the mouse cursor moves over it. All you need to do is arrange for an instance of one of your classes to be told to handle an "action event" that gets generated when there is a mouse-click on the button.
Do you have a text input field? Again, you don't need to be concerned about how it appears, how it handles input and editing of character data. Your code simply asks the textfield for the input text data when these data are needed.
The only point where you must write graphic related code is where your program
must display some application specific data. In these situations, a part of
the GUI display is reserved as an area where your data will be shown. If you are
using java.awt your GUI will have an instance of a specialized java.awt.Canvas; with
javax.swing your GUI will contain an instance of an application defined subclass
of javax.swing.JPanel. These specialized classes are quite standard in
their forms. The GUI object
(Canvas or JPanel) occupies a defined area of the window (relying on standard
behaviours as defined in the Java library code). It will have an extra data member
that is a reference to your "data object" and an extra function that allows your code
to set this data member. The paint function for the class is
overridden so that the request to perform the "painting" can be passed to your data object.
Actual output to the window ("painting") is done using an instance of the
class java.awt.Graphics.
The Graphics class defines a number of simple functions
for drawing lines, ovals, rectangles, and some complex functions
for drawing polygons and even images (read from "gif" or "jpg" files). These
functions include:
void drawString(String str, int x, int y); draws the
specified string starting at the specified x,y location.void drawLine(int x1, int y1, int x2, int y2); draws
a line between the two specified points.void drawOval(int x, int y, int width, int height); draws
the outline of an oval (ellipse) that fits within a rectangle defined
by the arguments.void fillRect(int x, int y, int width, int height); draws
a filled rectangle using the currently selected "color"void setColor(Color c); changes the currently selected
color.(Smart students will notice that java.awt.Graphics is an interface not a class. One cannot instantiate an interface. The methods declared in an interface are not defined. So, how can a Graphics object exist and draw rectangles etc? The way it works is that the Java AWT code linked into your program creates an instance of some class that implements Graphics. When running on a PC, you would get a "PCGraphics" object, on a Macintosh you would get a "MacGraphics", and you would get instance of other classes on Solaris or Linux systems.)
(The coordinate system for Graphics is not the same as that which your learnt at school! Coordinate 0,0 is the topleft corner, and the "y" value increases as you go down, not up.)
Colors (sic) are instances of the class java.awt.Color. A color is defined in terms of the intensities of its red, green, and blue components. The color (255,0,0) is pure red; the color (255, 165, 0) is orange; the color (255, 255, 224) is a light yellow; and color (65, 105, 225) is "royal blue".
The code supplied for this exercise builds a GUI that has two panels as shown below. There is a "data panel" for display of application data, and a "control panel" that contains an action button. Each time the action button is clicked, the data panel gets redrawn with the data slightly changed (the coloured rectangle changes its size and colour).
The code comprises the following classes:
class A2ex; the main program that constructs the GUI and
a "data object" (the code also includes helper functions to load
sounds and images - these functions are not used in the code supplied)class A2ExData; a very simple data class that has
a few data members and implements the paint function that draws
the image of the data;class A2ExGUI; builds the display with its two panels;class ControlPanel; the control panel, holds an action button;class DataPanel; occupies most of the window, linked to the
data object, arranges for the data object to paint itself.All display classes are specialized subclasses of javax.swing.JPanel. The control and data panels have links to the data object. The control panel needs a link to tell the data object to change itself whenever the action button is activated. The display panel needs a link to the data object so that it can forward the paint command.
The ControlPanel's "pedigree", as shown in the Navigator "inheritance view", is a little more interesting than the previous inheritance example:
As shown in the diagram:
(Single inheritance through the extends sequence, multiple interfaces - not the same as C++ multiple inheritance.)
If you want to know what any of the base classes or interfaces does, moving the mouse over the entry in the inheritance panel will cause a pop-up to appear with information.
The data object, instance of A2ExData, has to have a link back to the display panel. Whenever the data object is changed, it needs to request a "repaint" operation. (In normal usage, a data object requests a "repaint" operation and expects to receive a subsequent command to "paint"; the paint operation will be scheduled by code in the Java graphics libraries. It is only in special circumstances, such as animations, that a data object goes ahead and paints directly to the GUI.)
The code for these classes is:
class A2Ex
public class A2ex {
private static Toolkit toolkit;
private static Component displayer;
public static Image getImage(String name)
{
// Not used in the example
String baseName = System.getProperty("user.dir");
String fileName = baseName + "/images/" + name;
Image anImage = toolkit.getImage(fileName);
MediaTracker waitForIt = new MediaTracker(displayer);
waitForIt.addImage(anImage, 1);
try { waitForIt.waitForID(1); }
catch (InterruptedException ie) { }
return anImage;
}
public static AudioClip getSound(String name)
{
// Not used in the example
String baseName = System.getProperty("user.dir");
String fileURL = "file:" + baseName + "/sounds/" + name;
AudioClip theClip = null;
try {
theClip = Applet.newAudioClip(
new java.net.URL(fileURL));
}
catch(Exception e) {
System.out.println("Failed to load sound '"
+ name +"'");
System.out.println(e);
System.exit(1);
}
return theClip;
}
public static void main(String[] args)
{
try {
// Ignore this Java hacking that defines
// an "anonymous inner class". It is just
// a short cut way of generating code
// that handles "Window" events coming from
// the operating system.
WindowListener l = new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
};
// Create the "data object"
A2ExData someData = new A2ExData();
// Create the window
JFrame aFrame = new JFrame("A2Exercise");
aFrame.addWindowListener(l);
// Create the GUI display that goes in the window
// and link it to the data object
A2ExGUI displayComponent = new A2ExGUI(someData);
// set references needed by helper functions that
// can be used to load sound and image files
displayer = displayComponent;
toolkit = displayer.getToolkit();
// Organize the GUI
aFrame.getContentPane().add(displayComponent);
aFrame.setSize(new Dimension(640,640));
// Get it displayed
aFrame.setVisible(true);
// Main program now ends!
// Don't worry, there is a new thread running in
// the awt graphics library. it is this thread
// that keeps the program running.
}
catch(Exception e) {
System.out.println("Failed because : " + e) ;
}
}
}
class A2ExData
public class A2ExData {
// Data object - would normally own some
// collection of shapes; here there is only
// one shape - the colored rectangle defined
// by the private data members that set its (r,g,b) color
// etc
private DataPanel myDataPanel;
private int dx;
private int dy;
private int rcol;
private int gcol;
private int bcol;
private Random rgen;
public A2ExData()
{
// Create some data.
// In your "shapes" version, you should create a
// LinkedList<Shape> and then create
// a random collection of Shape objects of
// different kinds
rgen = new Random();
dx = 0;
dy = 0;
rcol = rgen.nextInt(255);
gcol = rgen.nextInt(255);
bcol = rgen.nextInt(255);
}
public void paint(Graphics g)
{
// In your "shapes" version, you will iterate through
// a collection of shapes telling each to paint itself
System.out.println("Painting the data object");
Dimension d = myDataPanel.getSize();
g.clearRect(0,0,(int)d.getWidth(),(int)d.getHeight());
g.setColor(new Color(rcol,gcol,bcol));
g.fillRect(40,40,60+dx,80+dy);
g.setColor(Color.BLACK);
}
public void setLinkToDataPanel(DataPanel dp)
{
// When the data are changed, they have to
// be redrawn; need to inform display - so
// first need a link to the display
myDataPanel = dp;
}
public void paintAgain()
{
// Change the data and request a repaint
//System.out.println("Told to change data and repaint");
dx++;
dy+=2;
rcol = rgen.nextInt(255);
gcol = rgen.nextInt(255);
bcol = rgen.nextInt(255);
myDataPanel.repaint(); // Request redraw after change
if(dx>400) {
dx = 0;
dy = 0;
}
// In your shapes version, you will modify individual
// shape objects or you could change the contents of
// the collection
}
}
class A2ExGUI
public class A2ExGUI extends JPanel {
// Define the contents of Graphical User Interface
// Placing a control panel on right and a larger data
// display panel on left
private DataPanel dp;
private ControlPanel cp;
private A2ExData myData;
public A2ExGUI(A2ExData someData)
{
myData = someData;
dp = new DataPanel(myData);
cp = new ControlPanel(myData);
cp.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
setLayout(gbl);
// Layout is 8x8; a 6x8 area for data display and a 2x8 area for controls
// Data gets all of the x- expansion
gbc.weightx = 100;
gbc.weighty = 100;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridheight = 8;
gbc.gridwidth = 6;
gbc.insets = new Insets(2,2,2,2);
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.BOTH;
add(dp, gbc);
gbc.weightx = 0;
gbc.weighty = 100;
gbc.gridx = 6;
gbc.gridy = 0;
gbc.gridheight = 8;
gbc.gridwidth = 2;
gbc.insets = new Insets(2,2,2,2);
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.BOTH;
add(cp, gbc);
}
}
class DataPanel
public class DataPanel extends JPanel {
private A2ExData myData;
public DataPanel(A2ExData theData) {
myData = theData;
myData.setLinkToDataPanel(this);
}
public void paint(Graphics g){
myData.paint(g);
}
}
class ControlPanel
public class ControlPanel extends JPanel implements ActionListener {
private A2ExData myData;
private JButton paintButton;
public ControlPanel(A2ExData theData) {
myData = theData;
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
setLayout(gbl);
// Column header
gbc.weightx = 0;
gbc.weighty = 0;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridheight = 1;
gbc.gridwidth = 1;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.NONE;
add(new JLabel("Control Panel"), gbc);
// action button
gbc.weightx = 0;
gbc.weighty = 0;
gbc.gridx = 0;
gbc.gridy = 1;
gbc.gridheight = 1;
gbc.gridwidth = 1;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.NONE;
paintButton = new JButton("Paint again");
add(paintButton, gbc);
paintButton.addActionListener(this);
// Filler panel to take space if display enlarged
gbc.weightx = 0;
gbc.weighty = 100;
gbc.gridx = 0;
gbc.gridy = 2;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.BOTH;
add(new JPanel(), gbc);
}
public void actionPerformed(ActionEvent aev) {
// User action - request a new set of data and new display
myData.paintAgain();
}
}
Basic graphic tasks
Shapes hierarchy
In this exercise you extend the graphics example.
paint and move
member
functions and a set of shape subclasses that can paint different shapes
(e.g. MyRect implements Shape, MyPoly implements Shape etc).
You will have met this example in lectures. You can play with my version:
java -jar SpaceInvaders.jar
You should find the SpaceInvaders.jar file in /share/cs-pub/csci213/Lab2.
Code for the framework parts is supplied as a Windows zip file ( available in /share/cs-pub/csci213/Lab2). This zip file contains the NetBeans project.
The supplied framework builds a graphical user interface (GUI) and organizes the mechanism for running the game. The GUI display is shown above. The display comprises a "game panel" and a "control panel". The game panel shows a "gun" that can be controlled by the player and the waves of attacking space invaders of various types. The control panel holds a couple of text output fields that contain regularly updated data with the players "score" and "status" and an action button that may be used to start the game, or restart the game after the player's "gun" has been destroyed by invaders.
The code comprises the following classes and interfaces:
class GameProgram; the main function that creates the
Game object, the GUI, links things up, and displays the interface. (The
class also defines static utility functions for loading sound and image files.)class A2GUI; this specialized subclass of javax.swing.JPanel
builds the overall GUI with its GamePanel and ControlPanel components.class ControlPanel; this specialized subclass of javax.swing.JPanel
holds the text output fields and the action button. It has application specific
member functions that handle mouse clicks on the action button, update notifications
from the Game (telling it that it needs to reset the contents of output textfields), etc.class GamePanel; another specialized subclass of javax.swing.JPanel. This
defines a display area where the Game will be drawn. It passes on paint requests to the
Game. It also arranges for the Game object to be notified of mouse movements (these
change the position of the "gun") and mouse click events (these represent triggering
fire from the "gun").class Game; during play, the game object will own a heterogeneous collection of
polymorphic references to various forms of space invader. It has a "run" function defined that
runs the game. This function has a loop that at regular intervals updates the position of
the gun, handles any gun fire action, allows the invaders to move, and which sorts out
minor issues like game score and status.class InvaderFactorythis class defines static methods that
are used by the Game object to obtain new invaders to replace any destroyed by gunfire.interface SpaceInvader; the interface defining the signatures of the
functions that are common to all class of SpaceInvader.SpaceInvader subclasses; one supplied, others to be written for this exercise.JavaDoc documentation has been generated for the supplied classes.
In this version of Invaders:
while(gun's status > 0){
checkInvaders - replace any invaders that were destroyed in last cycle
adjustGunLocation - based on mouse motion events recorded separately
fireGun - based on occurrence of mouse click recorded separately;
this operation may damage or destroy an invader located
above the gun (the first invader hit takes all damage and
shields other invaders);
for each invader in collection
invader move
check if invader is still "alive" - if not, remove it from
the collection (some invaders die on hitting the ground,
others may have finite lifetimes)
check whether gun destroyed by this invader, terminate main loop
as appropriate
get control panel to update score and status values
request a repaint of the game panel
delay a little before next cycle through this loop
}
Define, implement, and document some additional invader classes and integrate them with the framework code supplied.
The following are suggestions for possible invaders, but you can invent others with more complex behaviours:
GIF/PNG/JPG images can be loaded and used for invaders. Additional sounds can be loaded - only need to load each sound once - and used when invaders move. Don't overdo sound effects or your game will be intolerable. Invaders can obtain a reference to the GamePanel from the Game; this could be used if special graphical effects are required (effects similar to the gun fire effect). This reference to the GamePanel will be needed by any invader classes that choose to use "gif" or "jpeg" images rather than graphic drawing actions. Again, don't overdo the advanced graphical effects for they can slow your game or otherwise make it unplayable.
Your explorations of the Java libraries should have introduced you to
JavaDoc documentation. JavaDoc relies on a highly constrained
way of adding comments to your code; the javadoc program reads
your code, finds these special comments, and generates HMTL reference pages
using the same stylesheets as used for the Java libraries. JavaDoc is
tedious to use manually - but once again, an IDE can make things simple.
In the NetBeans/Project pane, right-click on the class that is to be documented and pick Tools/Auto comment from the pop-up menu. The pane used to display code will be hidden by a new pane that allows you to add comments to a class.
Normally, only public members (and public classes) are documented. The editing display should show the class and those members that require documentation. A red check mark by an entry means that it has no documentation; a yellow check mark indicates that documentation is in some way incomplete; a green check mark flags entries for which JavaDoc style comments have been added.
Pick each entry in turn and supply the documentation. The class entry should get a brief description of the role of the class. Functions should get documentation describing their role and detailing any input parameters and returned result. After selecting a function that is to be documented, use the AutoCorrect button to add @param and @return tags (initially empty).
Compose JavaDoc documentation describing your subclasses of SpaceInvader.
Once you have added comments to all your classes, you can generate the
HTML pages by selecting the project and using NetBean's Build/Generate JavaDoc
for ... menu option.