CIT 590 Assignment 12: Snipe
Spring 2013, David Matuszek

Purposes of this assignment

General idea of the assignment

Implement a simple game based on the Bouncing Ball animation. You can use this code as a starting point for your game.

You are in control of a single ball on the screen. Your job is to manuver your ball to hit every other ball on the screen in as short a time as possible.

To simplify the description, several numbers are specified below--the size of the window, the number, sizes and speeds of the balls, etc. In all cases, those numbers should be defined constants (final variables), so that they can be changed simply by changing the value of the constant in one place and recompiling. Start with these numbers, but you can adjust them to improve game play if you like.

The playing field (a JPanel) should be about 800 by 800 pixels. Balls should be about 10 pixels in diameter.

At the start of the game, your "ball" (which is a black square) is stationary in the center of the playing field. Seven other balls, of varying colors (but not black or white), are moving randomly about the playing field. You manuver your ball so as to collide with each of the other balls. When you collide with a ball, it stops moving and remains in place, and gradually "fades away". When all the balls (except yours) are stationary, the game is over.

While you play, the elapsed time is displayed in the top right corner of the playing field. When the game ends, the elapsed time stops advancing, so that you can read how well you did. Shorter times are better.

Details:

The animation will proceed at a rate of 25 frames/second.

Each ball (except yours) starts in a random location on the screen, and moves with a random velocity. The velocity is determined by two quantities: deltaX, which is the amount to add to the x position for each new frame, and deltaY, which is the amount to add to the y position for each new frame. The values deltaX and deltaY are randomly chosen integers between -8 and +8, inclusive, except that at least one of them must be nonzero.

Your black ball starts in the center of the screen, with deltaX = deltaY = 0. You control the ball by hitting keys which increment or decrement deltaX and deltaY, as follows:

Key typed Key codes What to do Where to get key code
w or i or VK_W, VK_I, VK_UP   Subtract 1 from deltaY When a key is pressed, a KeyEvent occurs. You can capture this event with a KeyListener or KeyAdapter and override its public void keyPressed(KeyEvent e) method. Then use the event's getKeyCode() method to get the (integer) key code.
a or j or VK_A, VK_J, VK_LEFT   Subtract 1 from deltaX
s or k or VK_S, VK_K, VK_DOWN   Add 1 to deltaY
d or l or VK_D, VK_L, VK_RIGHT   Add 1 to deltaX

By hitting the same key repeatedly, you can increase the speed of your ball up to a maximum speed of 30 pixels/frame (or -30).

Your ball will bounce when it hits the "walls" (the borders of the window). When the left or right wall is hit, the  sign of deltaX changes; when the top or bottom wall is hit, the sign of deltaY changes. Other balls will wrap--when a ball disappears off one edge, it reappears on the other edge, with deltaX and deltaY unchanged. For example, when a ball moves off the top of the window, it reappears in approximately the same x position at the bottom of the window, moving in the same direction.

Your ball and another ball "collide" when the distance between their (x, y) positions is less than or equal to the ball diameter. When this happens, your ball is unaffected, but the other ball stops moving (its deltaX and deltaY values are set to zero), and it is considered to be "dead."

When the ball dies, it should "fade away"--you can do this by subtracting 5 from each of its RGB values (to a minimum of (0, 0, 0)) each time the ball is drawn. When the ball fades away completely, you can (optionally) remove it.

Other balls do not collide with one another. They can pass through one another without any effect. Only test for collisions with your black ball.

Display the elapsed time in the top right corner of the playing field, to the nearest tenth of a second, and update it every time you display a new frame (25 times a second). (Use Calendar's getTimeInMillis() method). Balls can pass through the time display with no effect--but you should display the time after displaying all the balls, so that if a ball is stopped in that location, the time is drawn over the ball, rather than being obscured by the ball.

Put four buttons at the bottom of the screen:

Use the MVC design pattern. Name your project Snipe, and your package snipe. Name your main class Snipe; this class will display the GUI and act as the "controller." You should have (at least) three other classes, called Model, View, and Ball.

Starting the game should start a Timer to perform a TimerTask every 1/25 of a second. The TimerTask should call the Model to update the positions of all the "live" balls, the colors of all the "dead" balls, and the elapsed time. When the model is finished and all changes have been made, it should notify its observers. (Don't notify observers after every little change!) The View is an Observer which calls repaint() to display the balls and the elapsed time on the screen.

Display a congratulatory message dialog (containing the elapsed time) at the end of a game, rather than just have the program stop. (If you want to show off your finished game to others, a display of the best scores so far, and who made them, adds a lot of appeal to your game--but that's not a requirement.)

There will be methods in your Model class that deserve JUnit testing. All methods should be properly commented.

Due date:

Submit your zipped project to Canvas before 6am Friday April 19. No other form of submission will be accepted.