Lab: Sprites

Download and unzip spritelab.zip.

You might review these notes on inheritance.


Exercise 0: World's run Method

Open Sprite.java. The Sprite class represents a single visual object in an animation or game. Each Sprite knows its position in a 2-dimensional world. (Specifically, a Sprite knows the x-coordinate of its left edge and the y-coordinate of its top edge.) A Sprite also knows its width and height (in pixels), along with the image file used to display the Sprite on screen.

Now open World.java. The World class represents the 2-dimensional stage where our sprites will appear. By default, it is 500 pixels wide and 500 pixels high. In this world, x-coordinates increase from 0 at the left edge to 500 at the right edge, and y-coordinates increase from 0 at the top edge to 500 at the bottom edge. The World has a field which stores the list of Sprite objects to draw. Compile World.java and call its run method (or its main method). You should see a world with a couple of sprites in it.

You should not write any code for this exercise.


Exercise 1: MobileSprite's Header

Open MobileSprite.java. A MobileSprite is exactly like a Sprite, except that a MobileSprite can move, and must therefore keep track of its velocity. Insert code in MobileSprite's header so that it inherits from the Sprite class. (Your code will not compile yet.)


Exercise 2: MobileSprite's Constructor

A MobileSprite must keep track of two pieces of information: vx (its velocity in the x direction) and vy (its velocity in the y direction). For example, a vx of +2.5 and a vy of -0.5 represent a sprite that is moving to the right (increasing x) and slightly up (decreasing y).

Insert code in MobileSprite's constructor so that it saves the x- and y-components of its velocity in the appropriate instance variables. (Your code will not compile yet.)


Exercise 3: MobileSprite's getVY and setVY methods

Insert code in MobileSprite's getVY and setVY methods to get and set the y-component of its velocity. (Your code should compile now.)


Exercise 4: Adding MobileSprites

In World.java, uncomment the lines of code in World's constructor that add MobileSprite objects to the world's list of sprites. Although your MobileSprites will not move yet, you can still test that they appear.


Exercise 5: Sprite's step method

Open Sprite.java and look at the step method. A Sprite and its subclasses can be asked to step. By default, asking a Sprite to step does nothing. We will never modify Sprite's step method. But a subclass can choose to override the step method. (Note that the step method takes in a World object. This allows a sprite to interact with other sprites in the world.)

Do not write any code in this exercise.


Exercise 6: MobileSprite's step method

MobileSprite overrides Sprite's step method so that a MobileSprite can change position when asked to step. Specifically, when a MobileSprite is asked to step, it will add vx to its x-coordinate and vy to its y-coordinate. For example, if a MobileSprite is initially at position (100, 100), and vx is +2.5, and vy is -0.5, then calling step will move the MobileSprite to position (102.5, 99.5). This could be accomplished by writing something like:

left = left + vx;

top = top + vy;

Alas, MobileSprite does not have direct access to private instance variables left and top declared in the Sprite class, so you must find another way to accomplish this.

Insert code in MobileSprite's step method so that it moves in this way. Note that code has already been provided to direct a MobileSprite to bounce off the edges of the world. Do not modify this code.

(Your MobileSprites will not actually move yet.)


Exercise 7: stepAll

Look at the stepAll method in World.java. (By default, this method is called 100 times every second.) Insert code in this method to ask each sprite to step. (When asked to pass a World to the step method, pass this.)

Test that your non-mobile sprites stay still, while your mobile sprites now move. (They will bounce off the bottom, left, and right edges of the screen, and they will drift off the top edge.)


Exercise 8: HeavySprite's Header

Open HeavySprite.java. A HeavySprite is exactly like a MobileSprite, except that when a HeavySprite is asked to step, it will also experience gravity (eventually). Insert code in HeavySprite's header so that it inherits from the MobileSprite class.


Exercise 9: Adding HeavySprites

In World.java, uncomment the lines of code in World's constructor that add HeavySprite objects to the world's list of sprites. Although your HeavySprites will not experience gravity yet, you can still test that they appear and move.


Exercise 10: HeavySprite's step method

Insert code in HeavySprite to override the step method, so that, in addition to

(1) moving exactly like a MobileSprite,

a HeavySprite also

(2) adds 0.1 to the y-component of its velocity (something like: vy = vy + 0.1).

Make sure your step method takes in the correct parameter(s). Your method should contain exactly two lines of code.

Test that HeavySprites fall in the expected manner and then bounce back up. (Note that over time, they will bounce higher and higher. We will not attempt to fix this bug.)


Suggestions for Further Exploration

Rather than simply implementing a number of unrelated behaviors one after the other, you are strongly encouraged to work toward a particular physical simulation or video game and implement the relevant behaviors below.

Alive
Modify Sprite so that it keeps track of whether it is alive or dead. Modify World's stepAll method to remove any dead sprites from the list. (Must make use of this functionality.)

Lifespan
Create a new kind of sprite that dies at a specified time (when it reaches the edge of the screen, or after a specified number of steps). (Complete Alive first.)

Generator
Create a new kind of sprite that generates other sprites (e.g. fires projectiles). (Complete Lifespan if you want these projectiles to disappear eventually.)

Overlap
Write a method in Sprite that takes in another Sprite and returns true if they overlap. Specifically, the rectangular boundaries of two sprites A and B overlap if A's left is left of B's right, B's left is left of A's right, A's top is above B's bottom, and B's top is above A's bottom. (Must make use of this functionality.)

Killing
Create a new kind of sprite that kills or dies when it collides with another sprite. (Complete Alive and Overlap first.)

Landing
Create a new kind of sprite that behaves exactly like a HeavySprite, except that it stops falling when it lands on another sprite. (Hopefully it doesn't stop inside the other sprite.) (It may help to have completed Overlap first, although you might not use that exact code here.)

Contains
Write a method in Sprite that takes in an x and y and determines if that point lies inside the rectangular boundary of the sprite. (Must make use of this functionality.)

Clicking
Modify World's mouseClicked method so that it creates a new sprite wherever you click, or (alternatively) interacts with (e.g. kills) whatever sprite you clicked on. (To determine what sprite you clicked on, complete Contains first.)

Keyboard
Modify World's keyPressed method so that it interacts with a particular sprite (e.g. changes its motion) whenever you press keys with particular key codes. (First, press keys and note what codes are printed.) You may also choose to modify the keyReleased method.

Jumping
Change the velocity of a particular sprite whenever a certain key is pressed so that it appears to jump. (Complete Landing and Keyboard first.)

Bouncing
Create a new kind of sprite that bounces off other sprites it comes in contact with, by changing direction (according to some rule you devise) without changing speed.

The following code segments can help you convert between vx/vy and speed/direction.

// Given speed and direction in degrees, find vx and vy.

double vx = speed * Math.cos(Math.toRadians(direction));

double vy = speed * Math.sin(Math.toRadians(direction));


// Given vx and vy, find speed and direction.

double speed = Math.sqrt(vx * vx + vy * vy); //pythagorean theorem

double direction = Math.toDegrees(Math.atan2(vy, vx)); //atan2 returns angle in radians