I would like help with the bottom program in JAVA. I will award full points and
ID: 666524 • Letter: I
Question
I would like help with the bottom program in JAVA. I will award full points and rate to the one that meets all the requirments.
Introduction
The purpose of this week’s programming assignment is to learn how to deal with events from multiple sources. In addition, we will learn how to use some of Java’s graphics capabilities for drawing shapes in a display area. The programming assignment for this week is to implement a simple paddle ball game.
Paddle Ball Game Overview
The paddle ball game is a simplification of the Pong game. In the Pong game, a ball is moving around the display, bouncing off walls. The player moves a paddle in one dimension, trying to hit the ball whenever possible. If the ball hits the paddle, it bounces off and continues on its way. If the paddle misses the ball, the ball goes off the end of the display area and the player loses a point.
In our paddle ball game, the ball moves around the display, bouncing off walls just as in the Pong game. The player controls a paddle trying to hit the ball just like in the Pong game. If the ball hits the paddle, it bounces off and continues on its way. The main difference in our game is that if the player misses the ball, the ball simply bounces off the wall behind the paddle and continues to travel. There is no scoring in this game.
Object Oriented Design
If we analyze the game description we can see there are several different objects which are interacting. There is a ball object which is traveling around the display. There is a paddle object which is being moved by the player. There is a display which is presenting the graphical representation of the game to the user. These objects are readily apparent from the description of the game.
The ball object has a size and a position within the field of play. It also has a direction of travel in both the X and Y dimensions. The ball must be able to update its position when needed. Since the ball normally bounces off walls, it needs to know the length and width of the field of play when updating its position. The ball must also be able to draw itself.
The paddle has a size and a position within the field of play. The paddle must be able to update its position within the field of play. The paddle must also be able to draw itself.
The display has a display area which has a length and width. It has a means for drawing graphical shapes within the display area. The display area must be able to update the display area the position of the ball or paddle changes. The display itself has no concept of the game or its objects, so it will need to interact with some other object to draw all the game objects.
At this point, we have a ball, a paddle, and a display object. What is missing is some object which provides the game context. The ball has no awareness of the paddle. The paddle has no awareness of the ball. The display has no awareness of either the ball or paddle. We need an object which enforces the rules and concepts of the game and manages the behaviors of the other objects involved in the game.
Let’s call this the controller object. The controller has a ball, a paddle, and a display object. The controller manages game events. It gets mouse events and tells the paddle to update its position based on the position of the mouse cursor. It gets timer events and tells the ball to update its position based on the size of the display area which it gets from the display. It tells the display when to redraw the display area. It also provides a method which the display object uses to request the controller to draw the current state of the game. The controller is responsible for telling the ball and paddle to draw themselves.
The controller is also responsible for detecting when game objects are interacting. Specifically, it must be able to determine when the ball has made contact with the paddle. This requires that the controller is able to determine if a specific surface of the ball is in contact with a specific surface of the paddle. When this is detected, the controller tells the ball to reverse the direction of travel in the X or Y direction. This places a requirement on the ball and paddle to provide methods allowing the controller to determine the X or Y position of any given surface of the ball or paddle.
The Ball Class
Given that the ball has a size, a position, and moves a specific amount on each update, the Ball class will need member variables for all of these attributes. The size of the ball (diameter) and the number of pixels to move per update should be established when a ball object is created (explicit value constructor). To simplify things, the distance to move (increment) will initially be the same in the X and Y directions. The X and Y position members need to be set to some starting position by the constructor. The X, Y position of the ball is the upper left corner of the minimal box which contains the ball.
When the X increment is positive, the ball is traveling to the right. When it is negative it is traveling to the left. When the Y increment is positive the ball is traveling down. When it is negative it is traveling up.
According to the description in the previous section, the ball must provide the position of its edges to the controller. To do that, the Ball class provides the following methods:
getTop, getBottom, getLeft, getRight
The first two methods return the Y coordinate of the top/bottom edge of the ball. The other two methods return the X coordinate of the left/right edge of the ball. These values can easily be calculated from the X, Y position and the diameter of the ball. The Ball class must provide a method which takes a Graphics object parameter and uses it to draw the ball at its current position.
When the controller detects that the ball has hit the paddle, the controller must tell the ball to reverse its direction of travel in either the X or Y direction. To be flexible, the Ball class should provide two methods, one to reverse the X direction and one to reverse the Y direction. To the ball, reversing direction simply means negating the value of the X or Y increment.
The controller also must know whether the center of the ball is within the boundaries of the paddle in order to detect contact. This means the Ball class must provide methods to report its horizontal or vertical center. This can be easily computed from the current position and the diameter.
Finally, the Ball class provides a method which the controller calls any time the ball needs to change position. This method adds the X increment to the current X position, and the Y increment to the current Y position. The Ball class is responsible for detecting when it has encountered a wall. So, this method needs to know where the edges of the game area are. This method must be given the length and height of the game area as parameters. The following conditions must be checked for detecting contact with a wall:
Top edge of ball <= 0 then reverse Y travel direction
Bottom edge of ball >= height then reverse Y travel direction
Left edge of ball <= 0 then reverse X travel direction
Right edge of ball >= length then reverse X travel direction
The Paddle Class
The Paddle class has a length and a position. Both of these should be initialized by an explicit value constructor. That allows the controller to determine the paddle length and the position of the paddle relative to the wall. We will simplify things by stating that the paddle will have a horizontal orientation and will move left to right only. The paddle cannot move in the vertical direction once it has been created. The X and Y position of the paddle should represent the center of the top surface of the paddle. This will support the correlation of the mouse cursor position to the center of the paddle.
The draw method of the Paddle class takes a Graphics object parameter and draws a filled rectangle based on the current position of the paddle. Drawing a rectangle is based on the upper left corner of the rectangle as its reference position. Calculation of the reference position is easily done using the X and Y position of the paddle and the length of the paddle. The Paddle class controls what the width of the paddle will be.
To support the controller, the Paddle class must have methods which return the position of the top, bottom, left, and right edges of the paddle. The controller needs this information for detecting when the ball and paddle come into contact. The getTop and getBottom methods return a Y position. The getLeft and getRight methods return an X position. These values are easily calculated from the X and Y postion, and the length and width of the paddle.
Finally, a method must be provided to allow the controller to change the position of the paddle based on the X and Y coordinates of the mouse cursor. This method must restrict the movement of the paddle to changes to the X coordinate only.
The Display Class
The Display class provides a window to present the visual rendering of the state of the game. It also must interact with the controller. The easiest way to create a Display class which can draw various graphics is to have the class extend the JPanel class and override the paintComponent method. This method should simply fill a rectangle with some background color. The size of the rectangle is simply the size of the panel which is acquired from the getWidth and getHeight methods of the JPanel class. After drawing the background, the display object must pass its Graphics object to the controller, enabling the controller to arrange for the other game components to draw themselves.
The only other method needed in this class is an explicit value constructor which takes a controller object and stores it into a member variable. The constructor also creates a JFrame object storing it into a member variable. The display object adds itself to the JFrame. The frame initialization should be completed, including setting the initial size of the window which will display the game.
The Controller Class
The controller manages all aspects of the game including various game parameters. It determines the diameter of the ball and the distance the ball travels with each move. The distance travelled should be less than the diameter. The speed of ball movement is controlled by a combination of distance travelled and frequency of timer events. Timer events should be set up to occur around every 25 milliseconds. The length of the paddle should be at least twice the diameter of the ball. The game is to be set up so the paddle moves horizontally near the top of the display area. There should be at least 5% of the display height between the paddle and the top of the display area.
The Controller class contains a ball object, a paddle object, and a display object. In addition, the controller is responsible for managing the events that drive the game. Therefore, this class must implement the ActionListener interface to deal with Timer events which drive the ball. It must also implement the MouseMotionListener interface to cause the paddle to move based on mouse movement.
The constructor of this class creates the three objects mentioned above. It also must create a Timer object which will fire every 25 milliseconds and send ActionEvents to the controller object’s actionPerformed method. The last thing the constructor needs to do is to register the controller object as a mouseMotionListener on the display object. So, when the mouse cursor moves across the display area, MouseEvents will be delivered to the mouseDragged or mouseMoved methods of the controller object.
The controller provides a draw method which takes a Graphics object parameter and is called by the display object. This method should check to see if the ball and paddle objects exist, and if they do, ask each object to draw itself, passing the Graphics object into the draw methods of those objects.
The mouseDragged and mouseMoved methods are responsible for managing the padddle position. When a MouseEvent occurs and one of these methods executes, the method uses its MouseEvent parameter to get the current X and Y coordinates of the mouse cursor. These coordinates are given to the update method of the paddle which adjusts the paddle position based on these new coordinates. Once the paddle has been updated, the controller calls the repaint method on the display object to cause the display to reflect the new position of the paddle.
The controller’s actionPerformed method is responsible for causing the ball to move and detecting whether the ball is in contact with the paddle. It calls the update method on the ball, passing in the width and height of the display which it gets from the display object.
To detect whether the ball has just contacted the paddle, the following steps should be executed:
Get the Y coordinates of the top of the ball and the bottom of the paddle.
For contact to have just occurred, the top of the ball must be less than or equal to the bottom of the paddle, and it must also be greater than or equal to the bottom of the paddle minus the amount the ball just moved.
If step 2 is true, the horizontal center of the ball must be between the left and right ends of the paddle.
If step 3 is true, tell the ball to reverse its vertical direction.
Finally, the actionPerformed method invokes the repaint method to draw the display with the new position of the ball.
Play the Game
Create a test application that creates a controller object and play the game! Experiment with changing the game parameters and observe how the game reacts. When the game works, take a screen shot of the game. Turn in ALL your code along with the screen shot. Make sure that your code is fully commented!
Explanation / Answer
The requirement of the game is massive. I have created the game.
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class paddlegame extends GraphicsProgram {
/** Width and height of application window in pixels */
public static final int APPLICATION_WIDTH = 400;
public static final int APPLICATION_HEIGHT = 600;
/** Dimensions of game board (usually the same) */
private static final int WIDTH = APPLICATION_WIDTH;
private static final int HEIGHT = APPLICATION_HEIGHT;
/** Dimensions of the paddle */
private static final int PADDLE_WIDTH = 60;
private static final int PADDLE_HEIGHT = 10;
/** Offset of the paddle up from the bottom */
private static final int PADDLE_Y_OFFSET = 60;
/** Number of bricks per row */
private static final int NBRICKS_PER_ROW = 10;
/** Number of rows of bricks */
private static final int NBRICK_ROWS = 10;
/** Separation between bricks */
private static final int BRICK_SEP = 4;
/** Width of a brick */
private static final int BRICK_WIDTH =
(WIDTH - (NBRICKS_PER_ROW - 1) * BRICK_SEP) / NBRICKS_PER_ROW;
/** Height of a brick */
private static final int BRICK_HEIGHT = 8;
/** Radius of the ball in pixels */
private static final int BALL_RADIUS = 10;
/** Offset of the top brick row from the top */
private static final int BRICK_Y_OFFSET = 70;
/** .JAR main method */
public static void main(String[] args) {
new paddlegame().start(args);
}
/** Runs the paddlegame program. */
public void run() {
loader(); // load sound, event listeners, set black canvas
letsPlay(); // draw game and begin play
}
/** Draws the board and starts the game */
private void letsPlay() {
// draw board
drawBricks();
drawPaddle();
// start up info
startup();
// draw ball
drawBall();
// run game
runBall();
}
/** Loads sound files, event listeners, and sets canvas to black */
private void loader() {
// load sound files and start music
loadSound();
// add event listeners
addKeyListeners();
addMouseListeners();
setBackground(Color.black); // black canvas
}
/** Load sound files and start music */
public void loadSound(){
// load sfx
gameOver = MediaTools.loadAudioClip("sound/gameover.au");
winClip = MediaTools.loadAudioClip("sound/win.au");
musicClip = MediaTools.loadAudioClip("sound/superfly.au");
bottomClip = MediaTools.loadAudioClip("sound/bottom.au");
bounceClip = MediaTools.loadAudioClip("sound/bounce.au");
bounceClip2 = MediaTools.loadAudioClip("sound/jump.au");
music(); // start music loop
}
/** Game start banners */
public void startup(){
drawInfo();
waitForClick(); // click to start game
pause(500);
removeInfo();
}
/** clean up game start banners */
private void removeInfo(){
remove(welcome);
remove(key);
remove(cheat);
remove(rules);
remove(musicBanner);
remove(pauseBanner);
}
/** Draw game start banners */
private void drawInfo(){
welcome = new GLabel("***CLICK ANYWHERE TO START***", WIDTH/2, HEIGHT/2-20);
welcome.setFont(new Font("Sans-Serif", Font.BOLD, 22));
welcome.move(-welcome.getWidth()/2, -20);
welcome.setColor(Color.green);
add(welcome);
rules = new GLabel("--THE PADDLE FOLLOWS YOUR MOUSE", 10,HEIGHT/2);
rules.setFont(new Font("Sans-Serif", Font.BOLD, 18));
rules.setColor(Color.cyan);
add(rules);
cheat = new GLabel("--HIT ENTER TO TOGGLE CHEESEY MODE", 10,HEIGHT/2+25);
cheat.setFont(new Font("Sans-Serif", Font.BOLD, 18));
cheat.setColor(Color.red);
add(cheat);
key = new GLabel("--HIT UP/DOWN TO CONTROL BALL SPEED", 10,HEIGHT/2+50);
key.setFont(new Font("Sans-Serif", Font.BOLD, 18));
key.setColor(Color.yellow);
add(key);
pauseBanner = new GLabel("--HIT P TO PAUSE", 10,HEIGHT/2+75);
pauseBanner.setFont(new Font("Sans-Serif", Font.BOLD, 18));
pauseBanner.setColor(Color.green);
add(pauseBanner);
musicBanner = new GLabel("--HIT SPACEBAR TO TOGGLE MUSIC", 10,HEIGHT/2+100);
musicBanner.setFont(new Font("Sans-Serif", Font.BOLD, 18));
musicBanner.setColor(Color.orange);
add(musicBanner);
}
/** Draw bricks and set count to zero*/
private void drawBricks() {
// draw bricks
for(int i = 0; i < NBRICK_ROWS; i++) {
for(int j = 0; j < NBRICKS_PER_ROW; j++) {
// calculate x/y coordinates
int y_coordinate = (i * (BRICK_HEIGHT + BRICK_SEP)) + BRICK_Y_OFFSET;
int x_coordinate = j * (BRICK_WIDTH + BRICK_SEP);
brick = new GRect(x_coordinate, y_coordinate, BRICK_WIDTH, BRICK_HEIGHT);
brick.setFilled(true);
// set row color, every 2 rows
int row_num = i % 10;
if (row_num < 2)
brick.setColor(Color.red);
if (row_num >= 2 && row_num < 4)
brick.setColor(Color.orange);
if (row_num >= 4 && row_num < 6)
brick.setColor(Color.yellow);
if (row_num >= 6 && row_num < 8)
brick.setColor(Color.green);
if (row_num >= 8)
brick.setColor(Color.cyan);
// add brick to canvas
add(brick);
count = 0; // # of bricks broken
}
}
}
/** Draw ball */
private void drawBall() {
ball = new GOval((WIDTH/2)-(BALL_RADIUS/2),(HEIGHT/2)-(BALL_RADIUS/2),BALL_RADIUS,BALL_RADIUS);
ball.setFilled(true);
ball.setColor(Color.white);
add(ball);
}
/** Ball physics and animation */
private void runBall() {
// set x,y velocity
vy = 3;
vx = rgen.nextDouble(1.0, 3.0);
// random angle for game start
if (rgen.nextBoolean(0.5))
vx = -vx;
// loop for ball engine
while(true) {
// bounce off x-axis
if (ball.getX() < 0 || ball.getX()+BALL_RADIUS > WIDTH-BALL_RADIUS/2)
vx = -vx;
// bounce off y-axis
if (ball.getY() < 0)
vy = -vy;
// cheat mode; bounce off bottom
if (cheatOn == true) {
if (ball.getY() >= HEIGHT-BALL_RADIUS*3)
vy = -vy;
}
// if ball touches bottom, lose a turn, reset ball
if (ball.getY() > HEIGHT+BALL_RADIUS) {
bottomClip.play(); // bounce sound effect
if (turns < 0) {
// game over label
GLabel over = new GLabel("GAME OVER!!!", WIDTH/2, HEIGHT/2);
over.move(-over.getWidth()/2, 0);
over.setColor(Color.red);
add(over);
musicClip.stop();
gameOver.play();
pause(8000);
removeAll();
count = 0; // reset bricks broken count
turns = 3; // reset turns
if (playMusic)
musicClip.loop();
letsPlay(); // restart game
}
// next turn banner
GLabel restart = new GLabel("GET READY... " + turns + " TURNS REMAINING", WIDTH/2, HEIGHT/2);
if (turns == 1) {
restart.setLabel("GET READY... " + turns + " TURN REMAINING");
}
restart.move(-restart.getWidth()/2, 0);
restart.setColor(Color.green);
add(restart);
turns--; // lose a turn
pause(3000);
remove(restart); // remove banner
// set ball to center location
ball.setLocation((WIDTH/2)-(BALL_RADIUS/2),(HEIGHT/2)-(BALL_RADIUS/2));
}
else {
// check for collisions with bricks & paddle
collision();
}
ball.move(vx, vy);
pause(hold);
}
}
/** Test for collisions with bricks & paddle */
private void collision() {
// Get ball points to test; UpperLeft/UpperRight/LowerRight/LowerLeft
GObject colliderUL = getElementAt(ball.getX(), ball.getY());
GObject colliderUR = getElementAt(ball.getX()+BALL_RADIUS, ball.getY());
GObject colliderLR = getElementAt(ball.getX()+BALL_RADIUS, ball.getY()+BALL_RADIUS);
GObject colliderLL = getElementAt(ball.getX(), ball.getY()+BALL_RADIUS);
// bounce off paddle
if (colliderUL == paddle || colliderUR == paddle ||
colliderLR == paddle || colliderLL == paddle) {
// set angles for trajectory
double offset = ball.getX() - (paddle.getX() + (PADDLE_WIDTH/2));
if (offset <= -30)
vx = -3;
else if (offset <=-20)
vx = -2;
else if (offset <= -10)
vx = -1;
else if (offset == 0)
vx = -vx;
else if (offset < 10)
vx = 1;
else if (offset < 20)
vx = 2;
else if (offset <= 30)
vx = 3;
vy = -vy;
bounceClip2.play(); // paddle sound
}
// remove bricks
else if (colliderUL != null) {
remove(colliderUL);
vy = -vy;
bounceClip.play();
count++;
}
else if (colliderUR != null) {
remove(colliderUR);
vy = -vy;
bounceClip.play();
count++;
}
else if (colliderLR != null) {
remove(colliderLR);
vy = -vy;
bounceClip.play();
count++;
}
else if (colliderLL != null) {
remove(colliderLL);
vy = -vy;
bounceClip.play();
count++;
}
// check for winning game
if (count == 100) {
musicClip.stop();
winClip.play();
GLabel done = new GLabel("YOU WIN!", WIDTH/2,HEIGHT/2);
done.move(-done.getWidth()/2,0);
done.setColor(Color.cyan);
add(done);
pause(5000);
removeAll();
count = 0; // reset bricks broken count
turns = 3; // reset turns
if (playMusic)
musicClip.loop();
letsPlay(); // restart game
}
}
/** Draw paddle */
private void drawPaddle() {
int x_paddle = (APPLICATION_WIDTH / 2) - (PADDLE_WIDTH / 2);
paddle = new GRect(x_paddle, HEIGHT - PADDLE_Y_OFFSET, PADDLE_WIDTH, PADDLE_HEIGHT);
paddle.setColor(Color.white);
paddle.setFilled(true);
add(paddle);
}
/** Mouse event handler */
public void mouseMoved(MouseEvent e) {
int ex = e.getX();
// paddle follows mouse
if (ex >= PADDLE_WIDTH / 2 && ex <= WIDTH - (PADDLE_WIDTH / 2) )
paddle.setLocation((ex - (PADDLE_WIDTH / 2)), HEIGHT - PADDLE_Y_OFFSET);
else if (ex >= 0 && ex < PADDLE_WIDTH / 2)
paddle.setLocation(0, HEIGHT - PADDLE_Y_OFFSET);
else if (ex < WIDTH && ex >= WIDTH - (PADDLE_WIDTH / 2))
paddle.setLocation(WIDTH - PADDLE_WIDTH, HEIGHT - PADDLE_Y_OFFSET);
}
/** Keyboard event handler */
public void keyPressed(KeyEvent e) {
int ex = e.getKeyCode(); // get key pressed
// speed up ball
if (ex == KeyEvent.VK_UP && hold > 2) {
hold -= 2;
}
//slow down ball
if (ex == KeyEvent.VK_DOWN) {
hold += 2;
}
// cheat on/off
if (ex == KeyEvent.VK_ENTER) {
cheatOn = !cheatOn;
}
// sound on/off
if (ex == KeyEvent.VK_SPACE) {
if (playMusic) {
musicClip.stop();
playMusic = false;
}
else if (!playMusic){
musicClip.loop();
playMusic = true;
}
}
// pause game
if (ex == KeyEvent.VK_P) {
paused = !paused;
pauseGame();
}
}
/** Pause game by stopping ball */
public void pauseGame(){
if (paused) {
xtmp = vx;
ytmp = vy;
vx = 0;
vy = 0;
}
else {
vx = xtmp;
vy = ytmp;
}
}
/** Game music loop */
public void music() {
if (playMusic)
musicClip.loop();
}
/** Temp velocity value storage for game pause */
private double xtmp = 0;
private double ytmp = 0;
/** Keyboard listener states*/
private boolean paused = false;
private boolean playMusic = true;
private boolean cheatOn = false;
/** Player turns remaining */
private int turns = 3;
/** Game speed*/
private int hold = 10;
/** Player score; # of bricks broken */
private int count;
/** Sound files */
private AudioClip bounceClip, bounceClip2, winClip, musicClip, bottomClip, gameOver;
/** Brick */
private GRect brick;
/** Ball velocity */
private double vx, vy;
/** Paddle, Ball */
private GRect paddle;
private GOval ball;
/** Random # generator for ball initial velocity */
private RandomGenerator rgen = RandomGenerator.getInstance();
/** Game start banners */
private GLabel welcome;
private GLabel key;
private GLabel cheat;
private GLabel rules;
private GLabel musicBanner;
private GLabel pauseBanner;
}