Revert changes
parent
772ac20d15
commit
e0352d0456
|
@ -1,21 +0,0 @@
|
||||||
/* Eric Li, ICS4U, Completed 5/29/2022
|
|
||||||
|
|
||||||
Ball class defines behaviours for the pong ball; it also adds no new functions and instead inherits its main functions from GenericObject */
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class Ball extends GenericObject {
|
|
||||||
public int INITIAL_SPEED = 6;
|
|
||||||
public static final int BALL_DIAMETER = 15;
|
|
||||||
public Ball(int x, int y, int xDirection) {
|
|
||||||
super(x, y, BALL_DIAMETER, BALL_DIAMETER);
|
|
||||||
// set initial speed
|
|
||||||
super.setXDirection(INITIAL_SPEED * xDirection);
|
|
||||||
}
|
|
||||||
public void draw(Graphics g) {
|
|
||||||
g.setColor(Color.YELLOW);
|
|
||||||
g.fillRect(x, y, BALL_DIAMETER, BALL_DIAMETER);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +1,25 @@
|
||||||
/* Eric Li, ICS4U, Completed 5/29/2022
|
/* GameFrame class establishes the frame (window) for the game
|
||||||
|
|
||||||
GameFrame class establishes the frame (window) for the game
|
|
||||||
It is a child of JFrame because JFrame manages frames
|
It is a child of JFrame because JFrame manages frames
|
||||||
Runs the constructor in GamePanel class */
|
Runs the constructor in GamePanel class
|
||||||
|
|
||||||
|
*/
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
public class GameFrame extends JFrame{
|
public class GameFrame extends JFrame{
|
||||||
|
|
||||||
GamePanel panel;
|
GamePanel panel;
|
||||||
|
|
||||||
|
public GameFrame(){
|
||||||
|
panel = new GamePanel(); //run GamePanel constructor
|
||||||
|
this.add(panel);
|
||||||
|
this.setTitle("GUI is cool!"); //set title for frame
|
||||||
|
this.setResizable(false); //frame can't change size
|
||||||
|
this.setBackground(Color.white);
|
||||||
|
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //X button will stop program execution
|
||||||
|
this.pack();//makes components fit in window - don't need to set JFrame size, as it will adjust accordingly
|
||||||
|
this.setVisible(true); //makes window visible to user
|
||||||
|
this.setLocationRelativeTo(null);//set window in middle of screen
|
||||||
|
}
|
||||||
|
|
||||||
public GameFrame(){
|
|
||||||
panel = new GamePanel(); //run GamePanel constructor
|
|
||||||
this.add(panel);
|
|
||||||
this.setTitle("Pong... 2.0!"); //set title for frame
|
|
||||||
this.setResizable(false); //frame can't change size
|
|
||||||
this.setBackground(Color.black);
|
|
||||||
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //X button will stop program execution
|
|
||||||
this.pack();//makes components fit in window - don't need to set JFrame size, as it will adjust accordingly
|
|
||||||
this.setVisible(true); //makes window visible to user
|
|
||||||
this.setLocationRelativeTo(null);//set window in middle of screen
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
/* Eric Li, ICS4U, Completed 5/29/2022
|
/* GamePanel class acts as the main "game loop" - continuously runs the game and calls whatever needs to be called
|
||||||
|
|
||||||
GamePanel class acts as the main "game loop" - continuously runs the game and calls whatever needs to be called
|
|
||||||
|
|
||||||
Child of JPanel because JPanel contains methods for drawing to the screen
|
Child of JPanel because JPanel contains methods for drawing to the screen
|
||||||
|
|
||||||
|
@ -11,298 +9,113 @@ Implements Runnable interface to use "threading" - let the game do two things at
|
||||||
*/
|
*/
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.io.*;
|
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
import java.util.Scanner;
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
public class GamePanel extends JPanel implements Runnable, KeyListener{
|
public class GamePanel extends JPanel implements Runnable, KeyListener{
|
||||||
|
|
||||||
//dimensions of window
|
//dimensions of window
|
||||||
public static final int GAME_WIDTH = 1200;
|
public static final int GAME_WIDTH = 1920;
|
||||||
public static final int GAME_HEIGHT = 600;
|
public static final int GAME_HEIGHT = 1080;
|
||||||
|
|
||||||
// score needed to win
|
public Thread gameThread;
|
||||||
public static final int WIN_SCORE = 5;
|
public Image image;
|
||||||
|
public Graphics graphics;
|
||||||
public Thread gameThread;
|
public PlayerBall ball;
|
||||||
public Image image;
|
|
||||||
public Graphics graphics;
|
|
||||||
public Ball ball;
|
|
||||||
public Paddle leftPaddle, rightPaddle;
|
|
||||||
public boolean leftWin, rightWin;
|
|
||||||
public int leftScore, rightScore;
|
|
||||||
public String saveFile = "save.txt";
|
|
||||||
public boolean resumeGame = false;
|
|
||||||
public int paintNumber = 0;
|
|
||||||
|
|
||||||
|
|
||||||
// constructor initializes one Ball, two Paddles, and starts the Thread
|
public GamePanel(){
|
||||||
public GamePanel(){
|
ball = new PlayerBall(GAME_WIDTH/2, GAME_HEIGHT/2); //create a player controlled ball, set start location to middle of screen
|
||||||
ball = new Ball((GAME_WIDTH-Ball.BALL_DIAMETER)/2, (GAME_HEIGHT-Ball.BALL_DIAMETER)/2, 1); //create a player controlled ball, set start location to middle of screen
|
this.setFocusable(true); //make everything in this class appear on the screen
|
||||||
// starting position of paddles are randomized
|
this.addKeyListener(this); //start listening for keyboard input
|
||||||
leftPaddle = new Paddle(10, (int)(Math.random() * GAME_HEIGHT), KeyEvent.VK_W, KeyEvent.VK_S);
|
|
||||||
rightPaddle = new Paddle(GAME_WIDTH - Paddle.PADDLE_WIDTH - 10, (int)(Math.random() * GAME_HEIGHT), KeyEvent.VK_UP, KeyEvent.VK_DOWN);
|
|
||||||
this.setFocusable(true); //make everything in this class appear on the screen
|
|
||||||
this.addKeyListener(this); //start listening for keyboard input
|
|
||||||
|
|
||||||
this.setPreferredSize(new Dimension(GAME_WIDTH, GAME_HEIGHT));
|
//add the MousePressed method from the MouseAdapter - by doing this we can listen for mouse input. We do this differently from the KeyListener because MouseAdapter has SEVEN mandatory methods - we only need one of them, and we don't want to make 6 empty methods
|
||||||
|
addMouseListener(new MouseAdapter() {
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
ball.mousePressed(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setPreferredSize(new Dimension(GAME_WIDTH, GAME_HEIGHT));
|
||||||
|
|
||||||
//make this class run at the same time as other classes (without this each class would "pause" while another class runs). By using threading we can remove lag, and also allows us to do features like display timers in real time!
|
//make this class run at the same time as other classes (without this each class would "pause" while another class runs). By using threading we can remove lag, and also allows us to do features like display timers in real time!
|
||||||
gameThread = new Thread(this);
|
gameThread = new Thread(this);
|
||||||
gameThread.start();
|
gameThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
//paint is a method in java.awt library that we are overriding. It is a special method - it is called automatically in the background in order to update what appears in the window. You NEVER call paint() yourself
|
||||||
|
public void paint(Graphics g){
|
||||||
|
//we are using "double buffering here" - if we draw images directly onto the screen, it takes time and the human eye can actually notice flashes of lag as each pixel on the screen is drawn one at a time. Instead, we are going to draw images OFF the screen, then simply move the image on screen as needed.
|
||||||
|
image = createImage(GAME_WIDTH, GAME_HEIGHT); //draw off screen
|
||||||
|
graphics = image.getGraphics();
|
||||||
|
draw(graphics);//update the positions of everything on the screen
|
||||||
|
g.drawImage(image, 0, 0, this); //move the image on the screen
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//call the draw methods in each class to update positions as things move
|
||||||
|
public void draw(Graphics g){
|
||||||
|
ball.draw(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
//call the move methods in other classes to update positions
|
||||||
|
//this method is constantly called from run(). By doing this, movements appear fluid and natural. If we take this out the movements appear sluggish and laggy
|
||||||
|
public void move(){
|
||||||
|
ball.move();
|
||||||
|
}
|
||||||
|
|
||||||
|
//handles all collision detection and responds accordingly
|
||||||
|
public void checkCollision(){
|
||||||
|
|
||||||
|
//force player to remain on screen
|
||||||
|
if(ball.y<= 0){
|
||||||
|
ball.y = 0;
|
||||||
}
|
}
|
||||||
|
if(ball.y >= GAME_HEIGHT - PlayerBall.BALL_DIAMETER){
|
||||||
//paint is a method in java.awt library that we are overriding. It is a special method - it is called automatically in the background in order to update what appears in the window. You NEVER call paint() yourself
|
ball.y = GAME_HEIGHT-PlayerBall.BALL_DIAMETER;
|
||||||
public void paint(Graphics g){
|
|
||||||
//we are using "double buffering here" - if we draw images directly onto the screen, it takes time and the human eye can actually notice flashes of lag as each pixel on the screen is drawn one at a time. Instead, we are going to draw images OFF the screen, then simply move the image on screen as needed.
|
|
||||||
image = createImage(GAME_WIDTH, GAME_HEIGHT); //draw off screen
|
|
||||||
graphics = image.getGraphics();
|
|
||||||
draw(graphics);//update the positions of everything on the screen
|
|
||||||
g.drawImage(image, 0, 0, this); //move the image on the screen
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
if(ball.x <= 0){
|
||||||
//call the draw methods in each class to update positions as things move
|
ball.x = 0;
|
||||||
public void draw(Graphics g){
|
|
||||||
// set style of line (i.e., dashed)
|
|
||||||
Stroke dashed = new BasicStroke(3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{9}, 0);
|
|
||||||
|
|
||||||
// call draw methods of two Paddles and Ball
|
|
||||||
ball.draw(g);
|
|
||||||
leftPaddle.draw(g);
|
|
||||||
rightPaddle.draw(g);
|
|
||||||
|
|
||||||
// if the game hasn't started yet, set color of middle line and score to gray
|
|
||||||
if (paintNumber <= 3) {
|
|
||||||
g.setColor(Color.gray);
|
|
||||||
}
|
|
||||||
// draw dashed line
|
|
||||||
((Graphics2D)g).setStroke(dashed);
|
|
||||||
g.drawLine(GAME_WIDTH/2, 0, GAME_WIDTH/2, GAME_HEIGHT);
|
|
||||||
|
|
||||||
// set font to Windows default (Segoe)
|
|
||||||
Font font = new Font("Segoe UI", Font.PLAIN, 64);
|
|
||||||
g.setFont(font);
|
|
||||||
|
|
||||||
// draw score
|
|
||||||
drawCenteredString(g, 100, 0, GAME_WIDTH/2, Integer.toString(leftScore));
|
|
||||||
drawCenteredString(g, 100, GAME_WIDTH/2, GAME_WIDTH/2, Integer.toString(rightScore));
|
|
||||||
|
|
||||||
// draw game over text on win
|
|
||||||
if (leftWin) {
|
|
||||||
drawCenteredString(g, 200, 0, GAME_WIDTH/2, "Winner");
|
|
||||||
drawCenteredString(g, 200, GAME_WIDTH/2, GAME_WIDTH/2, "Loser");
|
|
||||||
g.setFont(new Font("Segoe UI", Font.ITALIC, 24));
|
|
||||||
drawCenteredString(g, 400, 0, GAME_WIDTH, "Press ENTER to play again");
|
|
||||||
} else if (rightWin) {
|
|
||||||
drawCenteredString(g, 200, 0, GAME_WIDTH/2, "Loser");
|
|
||||||
drawCenteredString(g, 200, GAME_WIDTH/2, GAME_WIDTH/2, "Winner");
|
|
||||||
g.setFont(new Font("Segoe UI", Font.ITALIC, 24));
|
|
||||||
drawCenteredString(g, 400, 0, GAME_WIDTH, "Press ENTER to play again");
|
|
||||||
}
|
|
||||||
|
|
||||||
// if game hasn't started yet, draw game start text
|
|
||||||
g.setColor(Color.white);
|
|
||||||
g.setFont(new Font("Segoe UI", Font.BOLD, 64));
|
|
||||||
if (paintNumber == 0) {
|
|
||||||
if (resumeGame) {
|
|
||||||
drawCenteredString(g, 200, 0, GAME_WIDTH, "Resuming...");
|
|
||||||
} else {
|
|
||||||
drawCenteredString(g, 200, 0, GAME_WIDTH, "Starting in...");
|
|
||||||
}
|
|
||||||
} else if (paintNumber == 1) {
|
|
||||||
drawCenteredString(g, 200, 0, GAME_WIDTH, "3");
|
|
||||||
} else if (paintNumber == 2) {
|
|
||||||
drawCenteredString(g, 200, 0, GAME_WIDTH, "2");
|
|
||||||
} else if (paintNumber == 3) {
|
|
||||||
drawCenteredString(g, 200, 0, GAME_WIDTH, "1");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if(ball.x + PlayerBall.BALL_DIAMETER >= GAME_WIDTH){
|
||||||
// draws a centered string
|
ball.x = GAME_WIDTH-PlayerBall.BALL_DIAMETER;
|
||||||
private void drawCenteredString(Graphics g, int y, int xOffset, int xWidth, String text) {
|
|
||||||
int x;
|
|
||||||
// get font size
|
|
||||||
FontMetrics metrics = g.getFontMetrics();
|
|
||||||
// determine x for the text
|
|
||||||
x = xOffset + (xWidth - metrics.stringWidth(text)) / 2;
|
|
||||||
// draw centered string
|
|
||||||
g.drawString(text, x, y);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//call the move methods in other classes to update positions
|
//run() method is what makes the game continue running without end. It calls other methods to move objects, check for collision, and update the screen
|
||||||
//this method is constantly called from run(). By doing this, movements appear fluid and natural. If we take this out the movements appear sluggish and laggy
|
public void run(){
|
||||||
public void move(){
|
//the CPU runs our game code too quickly - we need to slow it down! The following lines of code "force" the computer to get stuck in a loop for short intervals between calling other methods to update the screen.
|
||||||
ball.move();
|
long lastTime = System.nanoTime();
|
||||||
leftPaddle.move();
|
double amountOfTicks = 60;
|
||||||
rightPaddle.move();
|
double ns = 1000000000/amountOfTicks;
|
||||||
}
|
double delta = 0;
|
||||||
|
long now;
|
||||||
|
|
||||||
//handles all collision detection and responds accordingly
|
while(true){ //this is the infinite game loop
|
||||||
public void checkCollision(){
|
now = System.nanoTime();
|
||||||
// reverses ball y direction if ball hits top or bottom of screen
|
delta = delta + (now-lastTime)/ns;
|
||||||
if(ball.y <= 0 || ball.y >= GAME_HEIGHT - Ball.BALL_DIAMETER){
|
lastTime = now;
|
||||||
ball.yVelocity *= -1;
|
|
||||||
}
|
|
||||||
// updates score if ball hits border of screen
|
|
||||||
if(ball.x <= 0){
|
|
||||||
// prevents ball from glitching through the paddles
|
|
||||||
// it doesn't look great but it prevents the player from being unhappy
|
|
||||||
wait(3, 0.0);
|
|
||||||
if (!leftPaddle.checkCollision(ball, 'L')) {
|
|
||||||
// increment score
|
|
||||||
rightScore += 1;
|
|
||||||
// write score to file to allow resuming
|
|
||||||
try {
|
|
||||||
writeScoreToFile();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
regenerateBall('L');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(ball.x + Ball.BALL_DIAMETER >= GAME_WIDTH){
|
|
||||||
wait(3, 0.0);
|
|
||||||
if (!rightPaddle.checkCollision(ball, 'R')) {
|
|
||||||
leftScore += 1;
|
|
||||||
try {
|
|
||||||
writeScoreToFile();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
regenerateBall('R');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// delegates paddle remaining paddle checks to
|
|
||||||
leftPaddle.checkCollision(ball, 'L');
|
|
||||||
rightPaddle.checkCollision(ball, 'R');
|
|
||||||
}
|
|
||||||
|
|
||||||
//run() method is what makes the game continue running without end. It calls other methods to move objects, check for collision, and update the screen
|
//only move objects around and update screen if enough time has passed
|
||||||
public void run(){
|
if(delta >= 1){
|
||||||
double delta = 0;
|
move();
|
||||||
Scanner fileReader;
|
checkCollision();
|
||||||
|
|
||||||
// load score of previous game if it exists
|
|
||||||
try {
|
|
||||||
File playedFile = new File(saveFile);
|
|
||||||
if (!playedFile.createNewFile()) {
|
|
||||||
fileReader = new Scanner(playedFile);
|
|
||||||
leftScore = Integer.parseInt(fileReader.next());
|
|
||||||
rightScore = Integer.parseInt(fileReader.next());
|
|
||||||
// display resume text only if both scores are not equal to zero
|
|
||||||
resumeGame = leftScore + rightScore != 0;
|
|
||||||
// fileReader is never used again, so it is closed here
|
|
||||||
fileReader.close();
|
|
||||||
}
|
|
||||||
} catch (IOException | NoSuchElementException | NumberFormatException ignored) {}
|
|
||||||
|
|
||||||
// text to be painted before game starts
|
|
||||||
repaint(); // Resuming/starting text
|
|
||||||
try {
|
|
||||||
Thread.sleep(2000);
|
|
||||||
} catch (InterruptedException ignored) {}
|
|
||||||
paintNumber++; // pN = 1
|
|
||||||
repaint(); // 3...
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException ignored) {}
|
|
||||||
paintNumber++; // pN = 2
|
|
||||||
repaint(); // 2...
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException ignored) {}
|
|
||||||
paintNumber++; // pN = 3
|
|
||||||
repaint(); // 1...
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException ignored) {}
|
|
||||||
paintNumber++; // pN no longer matters
|
|
||||||
repaint();
|
repaint();
|
||||||
try {
|
delta--;
|
||||||
Thread.sleep(100);
|
}
|
||||||
} catch (InterruptedException ignored) {}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
// wait 0.5 ticks per cycle to prevent the game from running too fast
|
|
||||||
// 0.5 was used instead of 1 to reduce lag
|
|
||||||
delta = wait(0.5, delta);
|
|
||||||
// only repaint if game is resumed after win
|
|
||||||
if (leftWin || rightWin) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
//only move objects around and update screen if enough time has passed
|
|
||||||
move();
|
|
||||||
checkCollision();
|
|
||||||
checkForWin();
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// sets leftWin or rightWin to true if their score is greater than WIN_SCORE
|
//if a key is pressed, we'll send it over to the PlayerBall class for processing
|
||||||
private void checkForWin() {
|
public void keyPressed(KeyEvent e){
|
||||||
if (leftScore >= WIN_SCORE) {
|
ball.keyPressed(e);
|
||||||
leftWin = true;
|
}
|
||||||
}
|
|
||||||
else if (rightScore >= WIN_SCORE) {
|
|
||||||
rightWin = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if a key is pressed, we'll send it over to the PlayerBall class for processing
|
//if a key is released, we'll send it over to the PlayerBall class for processing
|
||||||
public void keyPressed(KeyEvent e){
|
public void keyReleased(KeyEvent e){
|
||||||
// resumes game and resets win booleans and scores when enter is pressed
|
ball.keyReleased(e);
|
||||||
if (e.getKeyCode() == KeyEvent.VK_ENTER && (leftWin || rightWin)) {
|
}
|
||||||
leftWin = false;
|
|
||||||
leftScore = 0;
|
|
||||||
rightWin = false;
|
|
||||||
rightScore = 0;
|
|
||||||
}
|
|
||||||
leftPaddle.keyPressed(e);
|
|
||||||
rightPaddle.keyPressed(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
//if a key is released, we'll send it over to the PlayerBall class for processing
|
//left empty because we don't need it; must be here because it is required to be overridded by the KeyListener interface
|
||||||
public void keyReleased(KeyEvent e){
|
public void keyTyped(KeyEvent e){
|
||||||
leftPaddle.keyReleased(e);
|
|
||||||
rightPaddle.keyReleased(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
//left empty because we don't need it; must be here because it is required to be overridded by the KeyListener interface
|
}
|
||||||
public void keyTyped(KeyEvent e){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// regenerates ball after it scores
|
|
||||||
public void regenerateBall(char winner) {
|
|
||||||
// overwrite old ball object with new ball that starts in middle
|
|
||||||
ball = new Ball((GAME_WIDTH-Ball.BALL_DIAMETER)/2, (GAME_HEIGHT-Ball.BALL_DIAMETER)/2, winner == 'L' ? -1:1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// write score to file after each goal
|
|
||||||
public void writeScoreToFile() throws IOException {
|
|
||||||
File playedFile = new File(saveFile);
|
|
||||||
FileWriter fileWriter = new FileWriter(playedFile);
|
|
||||||
fileWriter.write(((leftScore < 5 && rightScore < 5) ? leftScore:0) + " " + ((leftScore < 5 && rightScore < 5) ? rightScore:0));
|
|
||||||
fileWriter.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// public static function that waits for deltaAmount - previousTime ticks
|
|
||||||
// returns additional ticks if wait() took longer than deltaAmount - previousTime
|
|
||||||
// this allows it to run faster in response to lag
|
|
||||||
public static double wait(double deltaAmount, double previousTime) {
|
|
||||||
//the CPU runs our game code too quickly - we need to slow it down! The following lines of code "force" the computer to get stuck in a loop for short intervals between calling other methods to update the screen.
|
|
||||||
long lastTime = System.nanoTime();
|
|
||||||
double amountOfTicks = 60;
|
|
||||||
double ns = 1000000000 / amountOfTicks;
|
|
||||||
double delta = previousTime;
|
|
||||||
long now;
|
|
||||||
|
|
||||||
while (true) { // this is the delay loop
|
|
||||||
now = System.nanoTime();
|
|
||||||
delta = delta + (now - lastTime) / ns;
|
|
||||||
lastTime = now;
|
|
||||||
|
|
||||||
if (delta >= deltaAmount) {
|
|
||||||
return delta - deltaAmount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,42 +0,0 @@
|
||||||
/* Eric Li, ICS4U, Completed 5/29/2022
|
|
||||||
|
|
||||||
GenericObject class provides template that Ball and Paddle inherits */
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
|
|
||||||
public class GenericObject extends Rectangle{
|
|
||||||
|
|
||||||
public int yVelocity;
|
|
||||||
public int xVelocity;
|
|
||||||
//constructor creates ball at given location with given dimensions
|
|
||||||
public GenericObject(int x, int y, int width, int height){
|
|
||||||
super(x, y, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void keyPressed (KeyEvent e) {}
|
|
||||||
|
|
||||||
public void keyReleased (KeyEvent e) {}
|
|
||||||
|
|
||||||
|
|
||||||
//called whenever the movement of the ball changes in the y-direction (up/down)
|
|
||||||
public void setYDirection(int yDirection){
|
|
||||||
yVelocity = yDirection;
|
|
||||||
}
|
|
||||||
|
|
||||||
//called whenever the movement of the ball changes in the x-direction (left/right)
|
|
||||||
public void setXDirection(int xDirection){
|
|
||||||
xVelocity = xDirection;
|
|
||||||
}
|
|
||||||
|
|
||||||
//called frequently from both PlayerBall class and GamePanel class
|
|
||||||
//updates the current location of the ball
|
|
||||||
public void move(){
|
|
||||||
y = y + yVelocity;
|
|
||||||
x = x + xVelocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
//called frequently from the GamePanel class
|
|
||||||
//draws the current location of the ball to the screen
|
|
||||||
public void draw(Graphics g) {}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,14 +1,13 @@
|
||||||
/* Eric Li, ICS4U, Completed 5/29/2022
|
/* Main class starts the game
|
||||||
|
|
||||||
Main class starts the game
|
|
||||||
All it does is run the constructor in GameFrame class
|
All it does is run the constructor in GameFrame class
|
||||||
|
|
||||||
This is a common technique among coders to keep things organized (and handy when coding in repl.it since we're forced to call a class Main, which isn't always very descriptive)
|
This is a common technique among coders to keep things organized (and handy when coding in repl.it since we're forced to call a class Main, which isn't always very descriptive)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Main {
|
class Main {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
new GameFrame();
|
|
||||||
|
|
||||||
}
|
new GameFrame();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/* PlayerBall class defines behaviours for the player-controlled ball
|
||||||
|
|
||||||
|
child of Rectangle because that makes it easy to draw and check for collision
|
||||||
|
|
||||||
|
In 2D GUI, basically everything is a rectangle even if it doesn't look like it!
|
||||||
|
*/
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
|
||||||
|
public class PlayerBall extends Rectangle{
|
||||||
|
|
||||||
|
public int yVelocity;
|
||||||
|
public int xVelocity;
|
||||||
|
public final int SPEED = 20; //movement speed of ball
|
||||||
|
public static final int BALL_DIAMETER = 20; //size of ball
|
||||||
|
|
||||||
|
//constructor creates ball at given location with given dimensions
|
||||||
|
public PlayerBall(int x, int y){
|
||||||
|
super(x, y, BALL_DIAMETER, BALL_DIAMETER);
|
||||||
|
}
|
||||||
|
|
||||||
|
//called from GamePanel when any keyboard input is detected
|
||||||
|
//updates the direction of the ball based on user input
|
||||||
|
//if the keyboard input isn't any of the options (d, a, w, s), then nothing happens
|
||||||
|
public void keyPressed(KeyEvent e){
|
||||||
|
if(e.getKeyChar() == 'd'){
|
||||||
|
setXDirection(SPEED);
|
||||||
|
move();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e.getKeyChar() == 'a'){
|
||||||
|
setXDirection(SPEED*-1);
|
||||||
|
move();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e.getKeyChar() == 'w'){
|
||||||
|
setYDirection(SPEED*-1);
|
||||||
|
move();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e.getKeyChar() == 's'){
|
||||||
|
setYDirection(SPEED);
|
||||||
|
move();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//called from GamePanel when any key is released (no longer being pressed down)
|
||||||
|
//Makes the ball stop moving in that direction
|
||||||
|
public void keyReleased(KeyEvent e){
|
||||||
|
if(e.getKeyChar() == 'd'){
|
||||||
|
setXDirection(0);
|
||||||
|
move();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e.getKeyChar() == 'a'){
|
||||||
|
setXDirection(0);
|
||||||
|
move();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e.getKeyChar() == 'w'){
|
||||||
|
setYDirection(0);
|
||||||
|
move();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e.getKeyChar() == 's'){
|
||||||
|
setYDirection(0);
|
||||||
|
move();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//called from GamePanel whenever a mouse click is detected
|
||||||
|
//changes the current location of the ball to be wherever the mouse is located on the screen
|
||||||
|
public void mousePressed(MouseEvent e){
|
||||||
|
x = e.getX();
|
||||||
|
y = e.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
//called whenever the movement of the ball changes in the y-direction (up/down)
|
||||||
|
public void setYDirection(int yDirection){
|
||||||
|
yVelocity = yDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
//called whenever the movement of the ball changes in the x-direction (left/right)
|
||||||
|
public void setXDirection(int xDirection){
|
||||||
|
xVelocity = xDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
//called frequently from both PlayerBall class and GamePanel class
|
||||||
|
//updates the current location of the ball
|
||||||
|
public void move(){
|
||||||
|
y = y + yVelocity;
|
||||||
|
x = x + xVelocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
//called frequently from the GamePanel class
|
||||||
|
//draws the current location of the ball to the screen
|
||||||
|
public void draw(Graphics g){
|
||||||
|
g.setColor(Color.black);
|
||||||
|
g.fillOval(x, y, BALL_DIAMETER, BALL_DIAMETER);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue