Homework #4

CS302 in Summer 2012

  DUE by 11:59 PM on Sunday, August 5th, 2012

Overview

In this homework we're going to have some fun with object-oriented programming as we use it to complete a game like the classic game Space Invaders. You'll write an instantiable class to represent UFOs that, together with other classes we provide, will make a complete computer game. First we'll need to familiarize you with the code you'll use, and then you'll get a simple UFO class working in four steps. Once this part is completed you'll be challenged to extend and create different kinds of UFOs.

Getting started

Create a new project in Eclipse called Space. When creating the new project, remember to select "Use project folder as root for sources and class files" in the "Project layout" section. Next, download the following files to your Space folder:

Once you've gotten copies of these files:

  1. Unzip the SpaceImages file into your project folder (hint: drag the files out of the zip folder into your project),
  2. Add the SpaceCode.jar file to your project's build path (hint: right-click and select Build Path → Add to Build Path).

Now open UFO.java (in the default package), and notice the method stubs in this source file whose bodies have been completed with "dummy" code so that the program can be compiled. You'll be replacing those bodies with your own code as you work on this lab.

Understanding SpaceGame

Planet Earth is under attack by evil silicon-based life forms flying around in UFOs:

You are the only one who can possibly stem the tide in your fully armed lunar module, the Defender:

You move the Defender module by moving the mouse side to side (but you must keep the cursor in the game window!) and each time you click fires a Laser. If you disable all of the UFOs with your lasers, you win. If you get hit by three alien lasers, you lose. The UFO, Laser, and Defender objects are coordinated in the SpaceGame window, which is a class already written, so you just need to set it up and tell it to go.

Note that, while there are links to the Javadoc for the objects mentioned above, do not use any methods besides those that are specifically mentioned below. Otherwise, you may break the program.

The Defender, Laser, and UFO classes have the code that specify these objects. One thing they all need to know is where they're located in the SpaceGame window. Each object will do this by storing its position in the window using pixel coordinates (a pixel is the smallest dot in a picture that a computer can make and is about the size of the dot over an i on the screen). The coordinate system is counter-intuitive -- instead of having the origin of the coordinates at the lower-left corner of the display, Java puts it at the upper-left corner.

A game like this has essentially three main tasks that it repeatedly does:

  1. Check and handle the mouse input,
  2. Determine the position and status of the game objects, and
  3. Draw the objects in the window.

The game makes it appear that these happen at the same time using a programming technique called multithreading, which is beyond the scope of this course. We'll focus on determining position of UFO objects, and we'll enable the game to access information about them as specified in the UFO Javadoc.

Task 1. Making UFOs Appear - Constructor and Accessors (10%)

We have a few methods to implement to get our UFOs to appear on the screen. These methods allow the game to construct UFOs and access a UFO's position so that it can draw it in the right location on the screen.

We've initially specified that UFOs have three instance variables:

Start by completing the bodies the constructor and the accessor (getter) methods getXPosition(), getYPosition(), and getUFOType().

Once you have completed these, create a new class, named SpaceGameMain, in the source file named SpaceGameMain.java. This will be your main class. Add the following code to your SpaceGameMain.java file:

import java.util.Random;

public class SpaceGameMain {

	//for random numbers
	public static Random rng = new Random();

	public static void main(String[] args) {
		//create game
		SpaceGame theGame=new SpaceGame(800,800);

		//add UFOs
		theGame.addUFO(UFO.SIMPLE_SAUCER);
		theGame.addUFO(UFO.SIMPLE_SAUCER);
		theGame.addUFO(UFO.SIMPLE_SAUCER);

		//run game
		theGame.start();
	}
}

Now go ahead and run it (note it will crash if you haven't modified the UFO class as described above). If you wrote the methods correctly, you'll see the Defender module is ready to defend, but the UFO objects merely hang in various locations on the screen. They need some enhancement, specifically:

  1. They should move.
  2. They should fire lasers.
  3. They should be hurt when they're hit by lasers.

In the next three tasks we'll implement these enhancements.

Task 2. UFO Movement - takeOneStep (15%)

We make it appear that the UFO is smoothly moving by drawing it on the screen, then changing its position by a little bit and redrawing it on the screen at the new position. By repeating this at a fast rate, our eyes interpret it as continuous movement, which is an essential idea behind animation as seen in low-tech but high-fun flip books. The time between each screen redraw we call a tick. Once every tick, the game program we've already coded repeatedly calls takeOneStep(), a mutator (setter) method that updates the UFO object's position. Change the body of the takeOneStep method to this.xPosition+=3; and run the program (watch carefully since the ships will move off the window). Now every time the screen redraws itself the ship moves three pixels to the right; in other words, the ship moves to the right at a rate of three pixels per tick.

Next, modify the takeOneStep() method so the ship moves back and forth across the length of the screen at three pixels per tick. Here are some hints:

Task 3. UFO Firing - shootsThisTurn & fireWeapon (15%)

It's impossible to lose if the UFOs don't fire at you. Both the Defender and the UFO objects can fire Laser objects, but only the Defender has been programmed to work correctly. We've already implemented the Laser class, which you'll use in this task to make UFOs fire lasers. Two methods control the firing: shootsThisTurn() and fireWeapon() and both must be completed as described below for firing to work. During each tick of the game, our program will check each UFO object to see if it shoots this turn by calling shootsThisTurn(). If the UFO shoots, then our program will call fireWeapon().


public boolean shootsThisTurn()
returns true if the UFO is supposed to fire during this tick, false otherwise. Since this method is called by our code each tick, you'll want to keep a count of the tick and only fire when that count reaches a limit. I suggest that you have your UFO fire once every seventy ticks. Later, if you wish, you can make your method vary firing for more interesting game play.


public Laser fireWeapon()
returns a new Laser object that the UFO fires. This method simply makes a Laser object by using the the constructor we've already implemented in the Laser class:

public Laser(int startXPosition, int startYPosition, int xVelocity, int yVelocity)

where the first two ints specify the position of the center of the laser object and the second two indicate the x and y components of the velocity in pixels per tick. Create the new laser object at the same location as the UFO and have the laser object head straight down at four pixels per tick veolcity.


Here are some hints:

Task 4. Hitting a UFO - isHitByLaser, recordHit & removeMeFromGame (10%)

It's impossible to win if you can't hit the UFOs. We've programmed the Defender to be able to shoot, so you just need to check if your UFO object has been hit by a laser. If it has, you need to react appropriately. Three methods control how this works: isHitByLaser(Laser theLaser), recordHit(), and removeMeFromGame(). During each tick of the game, our program will check each UFO object to see if it was hit by a laser by calling isHitByLaser. If it was hit, our program will call the recordHit method so the UFO can record the hit, and then our program will call the removeMeFromGame to see if the UFO was destroyed and should be removed from the game.


public boolean isHitByLaser(Laser theLaser)
returns true if the laser is close enough to "hit" the UFO. Check if the center of the given Laser object and the center of the UFO object are a minimum distance apart. In order to do this you can get position of Laser objects using the accessor methods from the Laser class:


public void recordHit()
is a mutator method that tells the UFO that it was hit so that it can keep track of how many times it has been hit.


public boolean removeMeFromGame()
is an accessor method that returns true only when the UFO has been hit enough times to be destroyed.

Fill in the recordHit() and removeMeFromGame() methods so that the UFO is destroyed after it is hit twice.


Here some hints:

Task 5. creating different kinds of UFOs (10%)

The game is sort of neat, but all the UFOs are doing exactly the same thing. We can fix that! Let's use that uFOType variable that we've been ignoring. First, let's add more constants to the UFO class (add more constants if you wish):

public static final int FAST_SHIP = 1;
public static final int SUPER_SAUCER = 2;

Let's test it by modifying the SpaceGameMain class. Create several ships of each type and run the game. If you wrote your constructor and your getUFOType() method correctly, you should see several different UFO objects floating back and forth. That's a start, but they all still do the same thing. How can we change that?

Well, each ship has a UFO type. We can use conditional statements to check the type and execute different code based on the type. Here's a simple example that could go in the takeOneStep() method:

int pixelsPerTick;
if (this.uFOType == FAST_SHIP) {
	pixelsPerTick=6;
} else {
	pixelsPerTick=3;
}

If we move the ship based on pixelsPerTick, our FAST_SHIP UFOs will go twice as fast as the others. Keep in mind that a switch statement works extremely well here too:

switch(this.uFOType){
	case SIMPLE_SAUCER:
 		//your code here
		break;
	case FAST_SHIP:
 		//your code here
		break;
	case SUPER_SAUCER:
 		//your code here
		break;
	default:
		System.out.println("No such UFO type " + this.uFOType);
}

Task 6. equip different kinds of UFOs with different firing skills and patterns (40%)

So what makes a SUPER_SAUCER so super? Now it's your turn. Try to make several different types of UFOs that do different things. Have fun!

  • Make each UFO type a different image. There are eight images, numbered 0 through 7, so you can create up to 8 kinds of UFOs.
  • Make each UFO type go at different speeds.
  • Make a UFO type move vertically.
  • Make some UFO types fire more often than others.
  • Make a UFO type shoot diagonally.
  • Give the UFO types different amounts of life. Currently only two hits destroy a UFO.
  • Make a UFO type heal itself over time.
  • Make a "caffeinated" UFO type that moves a random number of pixels each turn.
  • Make a UFO type fire a random number of shots or at random intervals.
  • Make a UFO type fire in a random direction.

You might also use the Defender parameter passed to the UFO methods. The following must be done:

  • In the takeOneStep() method, make a UFO type chase the hero. Use the Defender's getXPosition() and getYPosition() to locate the defender.
  • In the firesThisTurn() method, make a UFO type fire only when close to being directly above the hero.
  • In the fireWeapon() method, make a UFO type aim the shot directly at the hero at any given time. Getting a general direction isn't too hard, but it may take a little vector arithmetic to do it well.

Something to Think About using inheritances and interfaces (this part is not a part of this HW but just for your reading)

One problem with our program is that the code for the UFO class can become awfully cumbersome as you add different kinds of UFOs. The problem is that our code jumbles together the different kinds of UFOs in a single class. Not only does this make the UFO class bulky, it is also hard to tell what any particular kind of UFO does. Worse, some of the more complicated kinds might have data members that are irrelevant to other kinds

Another problem with our program can be seen if you check the UFO, Defender, and Laser Javadocs. These classes share a lot of methods that have very similar code.

Can you think of better ways for handling these sorts of problems? It turns out that Java has better ways to handle these using interfaces and inheritance, which we learned recently.

Submission:

Submit the both of the following java source files to the dropbox for Homework #4 in Learn@UW.

  • UFO.java
  • SpaceGameMain.java