Assignment 3
Zapem!: An Animated Game

In lecture, we discussed & illustrated the use of inheritance & polymorphism. In this programming assignment, we store a list of shapes, called Critter objects, in an indentifier typed as a List & implemented as an ArrayList. An animation is achieved by periodically moving & drawing each element of the list, polymorphically. This programming assignment, beyond using features & classes that you have used before, gives you Java programming experience with the following:

Specification

The Zapem! Game Description

The object of the game is to zap critters that are moving in a square 2D field. The time taken to zap all the critters is displayed at the bottom of the screen. The critter field, in fact, is a 2D torus (a donut): Its east border connects to its west border & it south border connects to its north border. The application's layout consists of the critter field positioned above a control panel comprising 3 GUI items:

Below are 2 screenshots, Fig. 1 is of a game that is in progress; Fig. 2 displays the application after a game was completed, indicating is duration.

JFrame display of Zapem game

Fig. 1: JFrame display of Zapen game that is in progress.

JFrame display a completed Zapem game

Fig. 2: JFrame display a completed Zapem game, indicating the duration of the game.

The overall application consists of 4 classes:

These components are specified in more detail below.

The App Class

This class is given in its entirety. Please study it & investigate the program & organization choices that it reflects.


App.java
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JFrame;

/**
 *
 * @author Pete Cappello
 */
public class App extends JFrame
{
    private final ControlPanel controlPanel;
    private final Game game;
    private final View view;
    
    App() 
    {        
        game = new Game();
        view = new View( game, game.getImage() );
        controlPanel = new ControlPanel( view, game ); 
        
        setTitle( "Zap 'em" );
        setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
                      
        add( view, BorderLayout.CENTER );
        add( controlPanel, BorderLayout.SOUTH );
        
        Dimension dimension = new Dimension( Game.IMAGE_SIZE, Game.IMAGE_SIZE + controlPanel.getHeight() );
        setSize( dimension  );
        setPreferredSize( dimension );
        setVisible( true );
    }
    
    /**
     * Run the Graphics Virtual Machine application.
     * @param args unused 
     */
    public static void main( String[] args ) { App app = new App(); }
}


The View Class

The class extends javax.swing.JPanel, has 2 attributes, a Game object & a java.awt.Image object, both of which are set in its sole constructor, & are unchanged thereafter. Its 1 method is paintComponent, a JPanel method that it overrides. This method's body performs 2 actions:
  1. Invoke the paintComponent method of its superclass;
  2. invoke the graphics.drawImage method in order to display the Game object's entire Image, to which its Image reference is set.

The ControlPanel Class

This class has several attributes:

The View & Game attributes are set by the sole constructor, & are unchanged thereafter. The GUI componends comprise the control panel. Use a GridLayoutManager comprising 1 row & 3 columns to lay them out. This makes them uniformly sized & spaced. Since we have not really covered GUI, this layout code is given.

The animation is controlled by a Timer object, which causes window action events to occur every n milliseconds, where n is set at the time the Timer object is constructed. To have the ControlPanel use this Timer, 2 things are required:

The latter is the last statement of the initialize method; the former is the implmeentation of the overriden actionPerformed method (see code below). Please see the textbook, Java tutorial, or Java 7 API to see details & examples of using a Swing Timer.


ControlPanel.java
// put import statements here

/**
 *
 * @author Pete Cappello
 */
public class ControlPanel extends JPanel implements ActionListener, MouseListener
{
    private final static int PAUSE = 150; // milliseconds
    
    private final View view;
    private final Game game;
    
    private final JButton newGameButton = new JButton( "New Game" );
    private final JLabel  gameDurationLabel = new JLabel( "  Game duraion in seconds:" );
    private final JTextField gameDurationTextField = new JTextField();
    
    private final Timer animationTimer;
    private long gameStartTime;
    
    ControlPanel( View view, Game game ) 
    {
        this.view = view;
        this.game = game;
     
        setLayout( new GridLayout( 1, 3 ) );
        add( newGameButton );
        add( gameDurationLabel );
        add( gameDurationTextField );

        animationTimer = new Timer( PAUSE, this );
        gameDurationTextField.setEditable( false );
        initialize();
    }

    private void initialize() 
    {
        //------------------------------------------
        // contoller TEMPLATE CODE for each action
        //------------------------------------------
        // If you are running Java 8, use lambda expressions
//        newGameButton.addActionListener( 
//            ( ActionEvent actionEvent ) -> { newGameButtonActionPerformed( actionEvent ); } 
//        );
        
        // If you are not running Java 8, uncomment the code below   
        newGameButton.addActionListener( new ActionListener() 
        {
            @Override
            public void actionPerformed( ActionEvent actionEvent ) 
            {
                newGameButtonActionPerformed( actionEvent );
            }
        });
        
        // register this as the listener for mouse events in the View JPanel
        view.addMouseListener( this );
    }

    // _____________________________
    //  controller for each action
    // _____________________________
    private void newGameButtonActionPerformed( ActionEvent actionEvent ) 
    {
        // set the text field to the empty string;
        // record the start time of the game;
        // restart the Timer
        // start the game. 
    }
    /**
     * Implementation of ActionListener of Timer
     * @param e unused 
     */
    @Override
    public void actionPerformed( ActionEvent e) 
    {
        // 1. move and draw the critters (after blanking their previous positions)
        // 2. repaint the View
        // 3. if the game is over, 
        //    a. stop the Timer
        //    b. get the current time, using System.currentTimeMillis
        //    c. compute the duration of the game
        //    d. set the TextField to this value, in seconds, with 3 significant digits to the right of the decimal point. 
    }

    // implementation of MouseListener
    @Override
    public void mouseClicked( MouseEvent event ) 
    {
        // your implementation of a listener for mouse click events goes here:
        // 1. stop the timer;
        // 2. invoke the Game.processClick method with the x & y coordinates of the mouse click;
        // 3. start the timer;
        // 4. repaint the view component.
    }

    @Override
    public void mousePressed(MouseEvent e) {}

    @Override
    public void mouseReleased(MouseEvent e) {}

    @Override
    public void mouseEntered(MouseEvent e) {}
    
    @Override
    public void mouseExited(MouseEvent e) {}
}


Game

This class has at least the following attributes:

You may wish to add more attributes.

Inside the class skeleton given below, comments indicate where your code should go & what it should do. Although not shown in the skeleton, you need to supply an Image getter. You also may need other methods. I did. But, I want you to think about it. You will need some way to answer the question "Do the mouse click coordinates lie within the body of this Critter object?"

Inside the Game class, declare a private Critter class, to be used only be the Game class. It has the following attributes:

Its constructor constructs a random color, & random coordinates on the image for its starting position. It also selects a random x delta & a different random y delta, subject to the following constraints: For all these random selections, use the java.lang.Math.random method. For this assignment, all Critter objects are either squares or circles. Their edge length is always 60 pixels.

Critter's makeCritter (factory) method constructs & returns a Critter object, randomly selected from the 3 subclasses: SquareCritter, RoundCritter, & WowCritter.


Game.java
// imports go here

/**
 * Zapem game model
 *
 * @author Peter Cappello
 */
public class Game 
{
    public final static int IMAGE_SIZE = 600;
    // other symbolic program constants go here
    
    private final Image image;
    private final Critter critterFactory;
    
    private List<Critter> critterList;
    private boolean isOver;
        
    Game() 
    {         
        // * construct the Image object, of size IMAGE_SIZE
        // * paint the image all white for its initial display
        // * instantiate critterFactory, a Critter [subclass] object, that is used as a "factory" to create Critter objects.
    }
    
    
    public void start()
    {
        // 1. put the game in the state that it is not over.
        // 2. construct an ArrayList of 3 new Critters. 
        //    For each Critter that you create, use the critterFactory to randomly select a subclass of Critter to instantiate.
    }
        
    public void draw()
    {       
        // 1. blank the previous image (otherwise, you get a "trail")
        // 2. move then draw each critter.
    }
        
    public void processClick( int x, int y )
    {
        // for each critter, if the mouse click is "in" the critter, remove it from the critter list.
        //                   if such a removel makes the list empty, put the game in a "game over" state. 
    }
    
    abstract private class Critter
    {
        // declare its attributes
        
        Critter makeCritter() 
        {
            // construct a critter, selected randomly from the 3 Critter subclasses 
        }
        
        private Critter()
        {
            // set attributes with suitably random values.
        }
        
        
        abstract void draw( Graphics graphics );
        
        void move()
        {
            // Move the object by applying deltas to its coordinates.
            // Be careful to implement the wrap-around properly 
            // (consider what happens when applying the % operator to a negative number).
        }
        
    }
    
    // Subclass Critter with a square critter
    private class SquareCritter extends Critter
    {
        // your implementation goes here.
    }
    
    // Subclass Critter with a round critter
    private class RoundCritter extends Critter
    {
        // your implementation goes here.
    }
    
    // Subclass Critter with a round critter whose color is randomly chosen each time it is drawn
    private class WowCritter extends Critter
    {
        // your implementation goes here.
    }
}


Feel free to add helper methods to your classes that enhance their readability.

We will test your application by playing your game.

Possible Application Enhancements

Design Notes

Rubric

ValueAspect
1Compiles & executes.
5Overall control is correct; Critters are constructed, moved, & displayed correctly; .
1Javadoc is complete, generated, & adheres to Javadoc guidelines.
3Style
10Total

For style, we want:


 cappello@cs.ucsb.edu © Copyright 2014 Peter Cappello                                           2014.04.25