CS 124, Spring 2014
Lab 11: Sprites

In this lab, you will work with an API for programming with "sprites." A sprite is a graphical element, like an icon, that can be placed on the screen, and possibly move around on the screen. There are classes to represent several kinds of sprites, and a class to represent the panel where the sprites are displayed. All these classes are defined in a Java package named sprite. You will create several subclasses of these classes. In addition, the API includes some basic support for responding to keyboard events. That is, you can program responses that will happen when the user presses certain keys on the keyboard.

You should create a lab11 project in Eclipse. Open the folder /classes/cs124/lab11-files in a file browser. Select all four items, copy them, and paste them into the src folder of your lab11 project. You will have files named SpriteDemo1.java and SpriteDemo2.java in the default package and folders named "sprite" and "images." The sprite folder contains the .java files for the package named sprite. (You can also browse the files on-line or download a zip archive of all the files.)

You should not modify any of the .java files that you have just added to the project in any way. You can copy files, if you want, and work on the copies, and you can make new files. But I will not print out or grade any work that you do in files that already exist.

The lab has three exercises. The first two are meant to be fairly quick, and there are detailed instructions for how to do them. The third exercise is longer and open-ended and asks you to design and write a simple game that uses sprites.

This lab is officially due at the start of lab next week, but it will not be collected until noon on the following Thursday, April 24.

About Sprites and SpritePanel

The sprite API is relatively complex, compared to other things you have worked with in this course. I have generated the documentation in JavaDoc format, which you can browse online. However, I include some basic information here.

The two main classes are Sprite.java and SpritePanel.java. But to use these classes, you really have to define subclasses of them. To have actual Sprites, you need to have subclasses of Sprite. I have provided a variety of such subclasses, each representing a different kind of sprite. For example, OvalSprite represents a simple oval-shaped sprite, a Bullet is a kind of sprite that can kill other sprites by hitting them, and aSimpleGun is a kind of "gun" that can fire Bullets.

A SpritePanel is where sprites live. A SpritePanel has a method addSprite(s) which takes a sprite, s, and adds it to the panel. The panel keeps a list of sprites that it contains (actually two lists, since Bullet sprites are treated in a special way and are kept in a separate list from other sprites). Sprites can "die", and when they die they are removed from the list and no longer appear in the panel.

A SpritePanel can run an animation. A sprite has a velocity and moves during the animation according to its velocity. However, the animation doesn't start automatically. To start it, you have to call the panel's go() method. This is done, for example, at the end of the main() program in SpriteDemo1.

The other two SpritePanel methods that you are most likely to use are installKeyPress and doKeyPress, which are designed to let the user interact with the program by pressing keys on the keyboard. Ordinarily, you will call installKeyPress, and you will override doKeyPress in a subclass of SpritePanel. These methods are discussed in Exercise 3, below.

The Sprite class defines many methods, and you should remember that these can be used with any subclass of Sprite. See Sprite.java or the Javadoc for details. But here are some important instance methods contained in any object of type Sprite (including subclasses of Sprite):

Exercise 1: A Simple Sprite Class

(Note: You won't be able to test your work for this exercise until you at least get started on Exercise 2.)

There is already a class named OvalSprite. An OvalSprite is drawn as a colored oval. However, there is no class to represent a rectangle. The first exercise is to write such a class. Although you could do that by making a copy of OvalSprite.java, it would be preferable to work through the process of creating the class from scratch. Working through the instructions for this exercise will help you learn how to create a subclass of Sprite from scratch. You will probably want to take a look at OvalSprite.java before you start, and maybe copy some code from it. (Even if you do decide to start with a copy of OvalSprite.java, you will have to add some code to implement step 7, below.)

1. You want to a make a new class named RectSprite in the package sprite. To do this, right-click the sprite package in the Package Explorer view, on the left of the window. Select "New" / "Class" from the pop-up menu. In the dialog box, note that the package for the new class is already filled in as "sprite." When the class has been created, note that it says "package sprite;" at the top, to say that it is in the sprite package.

2. You want RectSprite to be a subclass of Sprite, so edit the first line of the class to read

public class RectSprite extends Sprite {

3. The class needs an instance variable to represent the color of the sprite. Add a private instance variable of type Color. You will get an error, because the Color class has not been imported. Add "import java.awt.Color;" to the file, or let Eclipse do it for you. Note that the import statement comes after the package statement.

4. Write getter and setter methods for the color variable. Better yet, let Eclipse do it for you: From the "Source" menu, select the command "Generate Getters and Setters". You will see a dialog box with a list of variables for which getters and setters can be generated. In this case, the only thing in the list is the color. Check the box next to the variable name, click OK, and the methods will be added to the class.

5. The class could use a constructor. It's not absolutely required, but a RectSprite needs a non-zero width and height for it to make sense. Add a constructor that specifies a width and height for the sprite. The width and height properties are actually defined in the sprite class. To set them, you can call the setter methods setSpriteWidth(w) and setSpriteHeight(h). (See the constructor in OvalSprite.java.)

6. One essential method in a sprite is draw, which is responsible for drawing the picture of the sprite. You need to provide a definition for draw that will draw the rectangle. The draw method has to start like:

public void draw(Graphics g) {

The method should draw the sprite with its center at the point whose coordinates are getX() and getY(). These methods are getter methods defined in the Sprite class that give the current location of the sprite. They return double values that have to be type-cast to int before you can use them for drawing. Don't forget to set the drawing color that you want to use for the sprite! Again, see the draw() in OvalSprite.java. (In this case, you are overriding a method that is already defined in the Sprite class. Again, you can Eclipse to help with this. From the "Source" menu, select "Override/Implement Methods". In the dialog box, select that method that you want to override, and click OK.)

7. Finally, to make things more interesting, make it possible to draw the rectangular sprite with a black border. Add an instance variable of type boolean to the RectSprint class to say whether or not the border should be drawn. You should provide getter and setter methods for the variable. Add code to the draw() method to draw a black, unfilled rectangle to act as a border for the filled rectangle that you have already drawn.

Exercise 2: A Simple Sprite Program

For the second exercise, you will create a simple program that uses sprites. In this case, you should begin by making a copy of the first demo program, SpriteDemo1.java. You could call the copy, for example, FirstSprites.

Note that the class is defined as a subclass of SpritePanel. That is, an object created from the class is a rectangular area in a window that can hold and display sprites and has methods for managing the sprites that it contains. (The class also has a main() routine, which makes it possible to run the class as a program.)

The goal of this exercise is to create a program that will allow the user to add sprites to the window by pressing keys on the keyboard. In particular, when the user presses the "R" key, the program should add a plain RectSprite to the window, and pressing the "B" key should add a RectSprite that has a border. The new sprite should have a random velocity. You can decide what its starting position should be. You will want to look at some of the code in the sample programs to see how to make a sprite and add it to the panel.

Pressing the "C" key should remove all sprites from the window. You can do that very easily by calling the clear() instance method, which is defined in class SpritePanel. You should program a few other keys, as well. You could add some other kinds of sprites, for example. (Try adding Explosion sprites at random positions!) Here are some more detailed instructions:

1. First, delete most of the code from the constructor. (Or comment it out, since you will want to imitate some of the things that are done there; however, don't leave the commented-out code in the program that you turn in.) In the original demo program, the constructor adds some sprites to the panel when the panel is first created. You do not want to do that in the constructor! You want to start with an empty panel and add sprites only when the user presses the appropriate keys.

2. You will need some "installKeyPress" commands in the constructor. For a key to have any effect, you must install it with this command. For example, to activate the "R" key, you need to say

installKeyPress("R");

This method is defined in the SpritePanel class. See the documentation for the method for more information.

3. Finally, you have to modify the doKeyPress method to program the responses to the "R", "C", and other keys. Note that you don't call this method anywhere in your program. It will be called by SpritePanel when the user presses a key that has been installed with installKeyPress(). When you write this method, your job is to say what will happen when the user presses the key.

Exercise 3: A Simple Game

For the third exercise, you should program an interactive game using sprites. You can begin the program in the same way that you started Exercise 2, by copying SpriteDemo1.java. Most likely the game will include a gun that the user can fire and sprites for the user to shoot at. You can make something very similar to SpriteDemo3, but for full credit, you should make some changes or add some new features. (I didn't give you the source code for SpriteDemo3, but there is a .jar file in /classes/cs124 that you can run.)

To have a gun, you want to add a sprite of type SimpleGun to the panel. You need to do that in the constructor, since the gun is a permanent part of the game, and you need an instance variable to represent the gun. A gun is already programed with the ability to fire bullets that can kill other sprites; you just have to call its fire() method. (If the variable gun refers to the gun, that would really mean calling gun.fire(). SprintDemo3 fires the gun when the user presses the up-arrow key.) To change the direction that the gun points, you can use its getAngle() method to get the current angle, and then use its setAngle method to point it in a different direction. In SpriteDemo3, this is done in response to the left and right arrow keys.

The SimpleDemo3 constructor also adds a bunch of sprites to the panel to serve as targets for the gun. They are given random positions and random horizontal velocities. Their vertical velocities are zero.

There are many ways that you could change or enhance the game, and you should try to be creative if you can come up with ideas of your own. You might even create an entirely new kind of sprite to use in the game. (However, not everything that you might want to do will be possible with the sprite API.) Here are some ideas: