init refactoring exercise

This commit is contained in:
2024-11-27 13:09:22 +01:00
parent 51f58d19bc
commit e31f2e3544
134 changed files with 3057 additions and 61 deletions

View File

@@ -0,0 +1,53 @@
package com.example.flappybird; /**
* Animation.java
* Creates animation with array of sprites
*
* @author Paul Krishnamurthy
*/
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.geom.AffineTransform;
public class Animation {
// Start at first frame
private static double currentFrame = 0;
/**
* Creates an animation with an array of sprites
*
* @param sprites Array of BufferedImages
* @param x x-coordinate
* @param y y-coordinate
* @param speed Speed of animation
* @param angle Angle to rotate sprite
*/
public static void animate (Graphics g, BufferedImage[] sprites, int x, int y, double speed, double angle) {
Graphics2D g2d = (Graphics2D) g;
AffineTransform trans = g2d.getTransform();
AffineTransform at = new AffineTransform();
// Number of frames
int count = sprites.length;
// Rotate the image
at.rotate(angle, x + 25, y + 25);
g2d.transform(at);
// Draw the current rotated frame
g2d.drawImage(sprites[(int) (Math.round(currentFrame))], x, y, null);
g2d.setTransform(trans);
// Switch animation frames
if (currentFrame >= count - 1) {
currentFrame = 0;
} else currentFrame += speed;
}
}

View File

@@ -0,0 +1,56 @@
package com.example.flappybird; /**
* Audio.java
* Plays all sound effects
*
* @author Paul Krishnamurthy
*/
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
public class Audio {
private AudioInputStream audioInputStream;
private Clip clip;
private void playSound (String sound) {
// Path to sound file
String soundURL = "/res/sound/" + sound + ".wav";
// Try to load and play sound
try {
audioInputStream = AudioSystem.getAudioInputStream(this.getClass().getResource(soundURL));
clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.start();
} catch (Exception e) {
e.printStackTrace();
System.out.printf("Count not load %s.wav!\n", sound);
}
}
/**
* Public method for bird jump sound
*/
public void jump () {
playSound("jump");
}
/**
* Public method for point sound
*/
public void point () {
playSound("point");
}
/**
* Public method for collision/death sound
*/
public void hit () {
playSound("hit");
}
}

View File

@@ -0,0 +1,164 @@
package com.example.flappybird; /**
* Bird.java
* Handles bird's state and actions
*
* @author Paul Krishnamurthy
*/
import javax.swing.JPanel;
import java.awt.image.BufferedImage;
import java.awt.Graphics;
public class Bird extends JPanel {
// Bird attributes
public String color;
private int x, y;
private boolean isAlive = true;
// Bird constants
private int FLOAT_MULTIPLIER = -1;
public final int BIRD_WIDTH = 44;
public final int BIRD_HEIGHT = 31;
private final int BASE_COLLISION = 521 - BIRD_HEIGHT - 5;
private final int SHIFT = 10;
private final int STARTING_BIRD_X = 90;
private final int STARTING_BIRD_Y = 343;
// Physics variables
private double velocity = 0;
private double gravity = .41;
private double delay = 0;
private double rotation = 0;
// Bird sprites
private BufferedImage[] sprites;
public Bird (String color, int x, int y, BufferedImage[] s) {
this.color = color;
this.x = x;
this.y = y;
this.sprites = s;
}
/**
* @return Bird's x-coordinate
*/
public int getX () {
return x;
}
/**
* @return Bird's y-coordinate
*/
public int getY () {
return y;
}
/**
* @return If bird is alive
*/
public boolean isAlive () {
return isAlive;
}
/**
* Kills bird
*/
public void kill () {
isAlive = false;
}
/**
* Set new coordinates when starting game
*/
public void setGameStartPos () {
x = STARTING_BIRD_X;
y = STARTING_BIRD_Y;
}
/**
* Floating bird effect on menu screen
*/
public void menuFloat () {
y += FLOAT_MULTIPLIER;
// Change direction within floating range
if (y < 220) {
FLOAT_MULTIPLIER *= -1;
} else if (y > 280) {
FLOAT_MULTIPLIER *= -1;
}
}
/**
* Bird jump
*/
public void jump () {
if (delay < 1) {
velocity = -SHIFT;
delay = SHIFT;
}
}
/**
* Bird movement during the game
*/
public void inGame () {
// If the bird did not hit the base, lower it
if (y < BASE_COLLISION) {
// Change and velocity
velocity += gravity;
// Lower delay if possible
if (delay > 0) { delay--; }
// Add rounded velocity to y-coordinate
y += (int) velocity;
} else {
// Play audio and set state to dead
GamePanel.audio.hit();
isAlive = false;
}
}
/**
* Renders bird
*/
public void renderBird (Graphics g) {
// Calculate angle to rotate bird based on y-velocity
rotation = ((90 * (velocity + 25) / 25) - 90) * Math.PI / 180;
// Divide for clean jump
rotation /= 2;
// Handle rotation offset
rotation = rotation > Math.PI / 2 ? Math.PI / 2 : rotation;
if (!isAlive()) {
// Drop bird on death
if (y < BASE_COLLISION - 10) {
velocity += gravity;
y += (int) velocity;
}
}
// Create bird animation and pass in rotation angle
Animation.animate(g, sprites, x, y, .09, rotation);
}
}

View File

@@ -0,0 +1,59 @@
package com.example.flappybird; /**
* FlappyBird.java
* Main game class
*
* @author Paul Krishnamurthy
*/
import javax.swing.*;
import java.awt.event.*;
import java.awt.Toolkit;
public class FlappyBird extends JFrame implements ActionListener {
GamePanel game;
Timer gameTimer;
// Game setup constants
public static final int WIDTH = 375;
public static final int HEIGHT = 667;
private static final int DELAY = 12;
public FlappyBird () {
super("Flappy Bird");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(WIDTH, HEIGHT);
// Game timer
gameTimer = new Timer(DELAY, this);
gameTimer.start();
// Add Panel to Frame
game = new GamePanel();
add(game);
// Set game icon
setIconImage(Toolkit.getDefaultToolkit().getImage("res/img/icons.png"));
setResizable(false);
setVisible(true);
}
public void actionPerformed (ActionEvent e) {
if (game != null && game.ready) {
game.repaint();
}
}
public static void main(String[] args) {
FlappyBird game = new FlappyBird();
}
}

View File

@@ -0,0 +1,616 @@
package com.example.flappybird; /**
* GamePanel.java
* Main game panel
*
* @author Paul Krishnamurthy
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
import java.util.ArrayList;
import java.util.HashMap;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.awt.image.BufferedImage;
import java.util.Calendar;
public class GamePanel extends JPanel implements KeyListener, MouseListener {
private Random rand;
private Calendar cal;
////////////////////
// Game variables //
////////////////////
// Fonts
private Font flappyFontBase,
flappyFontReal,
flappyScoreFont,
flappyMiniFont = null;
// Textures
public static HashMap<String, Texture> textures = new Sprites().getGameTextures();
// Moving base effect
private static int baseSpeed = 2;
private static int[] baseCoords = { 0, 435 };
// Game states
final static int MENU = 0;
final static int GAME = 1;
private int gameState = MENU;
private int score; // Player score
private int pipeDistTracker; // Distance between pipes
public boolean ready = false; // If game has loaded
private boolean inStartGameState = false; // To show instructions scren
private Point clickedPoint = new Point(-1, -1); // Store point when player clicks
private boolean scoreWasGreater; // If score was greater than previous highscore
private boolean darkTheme; // Boolean to show dark or light screen
private String randomBird; // Random bird color
private String medal; // Medal to be awarded after each game
public ArrayList<Pipe> pipes; // Arraylist of Pipe objects
private Bird gameBird;
private Highscore highscore = new Highscore();
public static Audio audio = new Audio();
public GamePanel () {
rand = new Random();
// Try to load ttf file
try {
InputStream is = new BufferedInputStream(this.getClass().getResourceAsStream("/res/fonts/flappy-font.ttf"));
flappyFontBase = Font.createFont(Font.TRUETYPE_FONT, is);
// Header and sub-header fonts
flappyScoreFont = flappyFontBase.deriveFont(Font.PLAIN, 50);
flappyFontReal = flappyFontBase.deriveFont(Font.PLAIN, 20);
flappyMiniFont = flappyFontBase.deriveFont(Font.PLAIN, 15);
} catch (Exception ex) {
// Exit is font cannot be loaded
ex.printStackTrace();
System.err.println("Could not load Flappy Font!");
System.exit(-1);
}
restart(); // Reset game variables
// Input listeners
addKeyListener(this);
addMouseListener(this);
}
/**
* To start game after everything has been loaded
*/
public void addNotify() {
super.addNotify();
requestFocus();
ready = true;
}
/**
* Restarts game by resetting game variables
*/
public void restart () {
// Reset game statistics
score = 0;
pipeDistTracker = 0;
scoreWasGreater = false;
// Get current hour with Calendar
// If it is past noon, use the dark theme
cal = Calendar.getInstance();
int currentHour = cal.get(Calendar.HOUR_OF_DAY);
// Array of bird colors
String[] birds = new String[] {
"yellow",
"blue",
"red"
};
// Set random scene assets
darkTheme = currentHour > 12; // If we should use the dark theme
randomBird = birds[rand.nextInt(3)]; // Random bird color
// Game bird
gameBird = new Bird(randomBird, 172, 250, new BufferedImage[] {
textures.get(randomBird + "Bird1").getImage(),
textures.get(randomBird + "Bird2").getImage(),
textures.get(randomBird + "Bird3").getImage()
});
// Remove old pipes
pipes = new ArrayList<Pipe>();
}
/**
* Checks if point is in rectangle
*
* @param r Rectangle
* @return Boolean if point collides with rectangle
*/
private boolean isTouching (Rectangle r) {
return r.contains(clickedPoint);
}
@Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
// Set font and color
g.setFont(flappyFontReal);
g.setColor(Color.white);
// Only move screen if bird is alive
if (gameBird.isAlive()) {
// Move base
baseCoords[0] = baseCoords[0] - baseSpeed < -435 ? 435 : baseCoords[0] - baseSpeed;
baseCoords[1] = baseCoords[1] - baseSpeed < -435 ? 435 : baseCoords[1] - baseSpeed;
}
// Background
g.drawImage(darkTheme ? textures.get("background2").getImage() :
textures.get("background1").getImage(), 0, 0, null);
// Draw bird
gameBird.renderBird(g);
switch (gameState) {
case MENU:
drawBase(g);
drawMenu(g);
gameBird.menuFloat();
break;
case GAME:
if (gameBird.isAlive()) {
// Start at instructions state
if (inStartGameState) {
startGameScreen(g);
} else {
// Start game
pipeHandler(g);
gameBird.inGame();
}
drawBase(g); // Draw base over pipes
drawScore(g, score, false, 0, 0); // Draw player score
} else {
pipeHandler(g);
drawBase(g);
// Draw game over assets
gameOver(g);
}
break;
}
}
/////////////////////////
// All drawing methods //
/////////////////////////
/**
* Draws a string centered based on given restrictions
*
* @param s String to be drawn
* @param w Constraining width
* @param h Constraining height
* @param y Fixed y-coordiate
*/
public void drawCentered (String s, int w, int h, int y, Graphics g) {
FontMetrics fm = g.getFontMetrics();
// Calculate x-coordinate based on string length and width
int x = (w - fm.stringWidth(s)) / 2;
g.drawString(s, x, y);
}
/**
* Needs to be called differently based on screen
*/
public void drawBase (Graphics g) {
// Moving base effect
g.drawImage(textures.get("base").getImage(), baseCoords[0], textures.get("base").getY(), null);
g.drawImage(textures.get("base").getImage(), baseCoords[1], textures.get("base").getY(), null);
}
////////////////
// Menuscreen //
////////////////
private void drawMenu (Graphics g) {
// Title
g.drawImage(textures.get("titleText").getImage(),
textures.get("titleText").getX(),
textures.get("titleText").getY(), null);
// Buttons
g.drawImage(textures.get("playButton").getImage(),
textures.get("playButton").getX(),
textures.get("playButton").getY(), null);
g.drawImage(textures.get("leaderboard").getImage(),
textures.get("leaderboard").getX(),
textures.get("leaderboard").getY(), null);
g.drawImage(textures.get("rateButton").getImage(),
textures.get("rateButton").getX(),
textures.get("rateButton").getY(), null);
// Credits :p
drawCentered("Created by Paul Krishnamurthy", FlappyBird.WIDTH, FlappyBird.HEIGHT, 600, g);
g.setFont(flappyMiniFont); // Change font
drawCentered("www.PaulKr.com", FlappyBird.WIDTH, FlappyBird.HEIGHT, 630, g);
}
/////////////////
// Game screen //
/////////////////
public void startGameScreen (Graphics g) {
// Set bird's new position
gameBird.setGameStartPos();
// Get ready text
g.drawImage(textures.get("getReadyText").getImage(),
textures.get("getReadyText").getX(),
textures.get("getReadyText").getY(), null);
// Instructions image
g.drawImage(textures.get("instructions").getImage(),
textures.get("instructions").getX(),
textures.get("instructions").getY(), null);
}
/**
* Aligns and draws score using image textures
*
* @param mini Boolean for drawing small or large numbers
* @param x X-coordinate to draw for mini numbers
*/
public void drawScore (Graphics g, int drawNum, boolean mini, int x, int y) {
// Char array of digits
char[] digits = ("" + drawNum).toCharArray();
int digitCount = digits.length;
// Calculate width for numeric textures
int takeUp = 0;
for (char digit : digits) {
// Size to add varies based on texture
if (mini) {
takeUp += 18;
} else {
takeUp += digit == '1' ? 25 : 35;
}
}
// Calculate x-coordinate
int drawScoreX = mini ? (x - takeUp) : (FlappyBird.WIDTH / 2 - takeUp / 2);
// Draw every digit
for (int i = 0; i < digitCount; i++) {
g.drawImage(textures.get((mini ? "mini-score-" : "score-") + digits[i]).getImage(), drawScoreX, (mini ? y : 60), null);
// Size to add varies based on texture
if (mini) {
drawScoreX += 18;
} else {
drawScoreX += digits[i] == '1' ? 25 : 35;
}
}
}
/**
* Moves and repositions pipes
*/
public void pipeHandler (Graphics g) {
// Decrease distance between pipes
if (gameBird.isAlive()) {
pipeDistTracker --;
}
// Initialize pipes as null
Pipe topPipe = null;
Pipe bottomPipe = null;
// If there is no distance,
// a new pipe is needed
if (pipeDistTracker < 0) {
// Reset distance
pipeDistTracker = Pipe.PIPE_DISTANCE;
for (Pipe p : pipes) {
// If pipe is out of screen
if (p.getX() < 0) {
if (topPipe == null) {
topPipe = p;
topPipe.canAwardPoint = true;
}
else if (bottomPipe == null) {
bottomPipe = p;
topPipe.canAwardPoint = true;
}
}
}
Pipe currentPipe; // New pipe object for top and bottom pipes
// Move and handle initial creation of top and bottom pipes
if (topPipe == null) {
currentPipe = new Pipe("top");
topPipe = currentPipe;
pipes.add(topPipe);
} else {
topPipe.reset();
}
if (bottomPipe == null) {
currentPipe = new Pipe("bottom");
bottomPipe = currentPipe;
pipes.add(bottomPipe);
// Avoid doubling points when passing initial pipes
bottomPipe.canAwardPoint = false;
} else {
bottomPipe.reset();
}
// Set y-coordinate of bottom pipe based on
// y-coordinate of top pipe
bottomPipe.setY(topPipe.getY() + Pipe.PIPE_SPACING);
}
// Move and draw each pipe
for (Pipe p : pipes) {
// Move the pipe
if (gameBird.isAlive()) {
p.move();
}
// Draw the top and bottom pipes
if (p.getY() <= 0) {
g.drawImage(textures.get("pipe-top").getImage(), p.getX(), p.getY(), null);
} else {
g.drawImage(textures.get("pipe-bottom").getImage(), p.getX(), p.getY(), null);
}
// Check if bird hits pipes
if (gameBird.isAlive()) {
if (p.collide(
gameBird.getX(),
gameBird.getY(),
gameBird.BIRD_WIDTH,
gameBird.BIRD_HEIGHT
)) {
// Kill bird and play sound
gameBird.kill();
audio.hit();
} else {
// Checks if bird passes a pipe
if (gameBird.getX() >= p.getX() + p.WIDTH / 2) {
// Increase score and play sound
if (p.canAwardPoint) {
audio.point();
score ++;
p.canAwardPoint = false;
}
}
}
}
}
}
public void gameOver (Graphics g) {
// Game over text
g.drawImage(textures.get("gameOverText").getImage(),
textures.get("gameOverText").getX(),
textures.get("gameOverText").getY(), null);
// Scorecard
g.drawImage(textures.get("scoreCard").getImage(),
textures.get("scoreCard").getX(),
textures.get("scoreCard").getY(), null);
// New highscore image
if (scoreWasGreater) {
g.drawImage(textures.get("newHighscore").getImage(),
textures.get("newHighscore").getX(),
textures.get("newHighscore").getY(), null);
}
// Draw mini fonts for current and best scores
drawScore(g, score, true, 303, 276);
drawScore(g, highscore.bestScore(), true, 303, 330);
// Handle highscore
if (score > highscore.bestScore()) {
// New best score
scoreWasGreater = true;
highscore.setNewBest(score); // Set in data file
}
// Medal
if (score >= 10) { medal = "bronze"; }
if (score >= 20) { medal = "silver"; }
if (score >= 30) { medal = "gold"; }
if (score >= 40) { medal = "platinum"; }
// Only award a medal if they deserve it
if (score > 9) {
g.drawImage(textures.get(medal).getImage(),
textures.get(medal).getX(),
textures.get(medal).getY(), null);
}
// Buttons
g.drawImage(textures.get("playButton").getImage(),
textures.get("playButton").getX(),
textures.get("playButton").getY(), null);
g.drawImage(textures.get("leaderboard").getImage(),
textures.get("leaderboard").getX(),
textures.get("leaderboard").getY(), null);
}
//////////////////////
// Keyboard actions //
//////////////////////
public void keyTyped (KeyEvent e) {}
public void keyReleased (KeyEvent e) {}
public void keyPressed (KeyEvent e) {
int keyCode = e.getKeyCode();
if (gameState == MENU) {
// Start game on 'enter' key
if (keyCode == KeyEvent.VK_ENTER) {
gameState = GAME;
inStartGameState = true;
}
} else if (gameState == GAME && gameBird.isAlive()) {
if (keyCode == KeyEvent.VK_SPACE) {
// Exit instructions state
if (inStartGameState) {
inStartGameState = false;
}
// Jump and play audio even if in instructions state
gameBird.jump();
audio.jump();
}
}
}
///////////////////
// Mouse actions //
///////////////////
public void mouseExited (MouseEvent e) {}
public void mouseEntered (MouseEvent e) {}
public void mouseReleased (MouseEvent e) {}
public void mouseClicked (MouseEvent e) {}
public void mousePressed (MouseEvent e) {
// Save clicked point
clickedPoint = e.getPoint();
if (gameState == MENU) {
if (isTouching(textures.get("playButton").getRect())) {
gameState = GAME;
inStartGameState = true;
} else if (isTouching(textures.get("leaderboard").getRect())) {
// Dummy message
JOptionPane.showMessageDialog(this,
"We can't access the leaderboard right now!",
"Oops!",
JOptionPane.ERROR_MESSAGE);
}
if (gameBird.isAlive()) {
if (isTouching(textures.get("rateButton").getRect())) {
Helper.openURL("http://paulkr.com"); // Open website
}
}
} else if (gameState == GAME) {
if (gameBird.isAlive()) {
// Allow jump with clicks
if (inStartGameState) {
inStartGameState = false;
}
// Jump and play sound
gameBird.jump();
audio.jump();
} else {
// On game over screen, allow restart and leaderboard buttons
if (isTouching(textures.get("playButton").getRect())) {
inStartGameState = true;
gameState = GAME;
restart();
gameBird.setGameStartPos();
} else if (isTouching(textures.get("leaderboard").getRect())) {
// Dummy message
JOptionPane.showMessageDialog(this,
"We can't access the leaderboard right now!",
"Oops!",
JOptionPane.ERROR_MESSAGE);
}
}
}
}
}

View File

@@ -0,0 +1,31 @@
package com.example.flappybird; /**
* Helper.java
* Helper class with various tools
*
* @author Paul Krishnamurthy
*/
import java.awt.Desktop;
import java.net.URI;
public class Helper {
/**
* Tries to open url in default web browser
*
* @param url Destination URL
*/
public static void openURL (String url) {
try {
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().browse(new URI(url));
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("Sorry could not open URL...");
}
}
}

View File

@@ -0,0 +1,83 @@
package com.example.flappybird; /**
* Highscore.java
* Handles setting and getting highscores
*
* @author Paul Krishnamurthy
*/
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
public class Highscore {
// Read / Write to file setup
private static final String FILE_PATH = "/res/data/highscore.dat";
private static URL dataURL = Highscore.class.getResource(FILE_PATH);
private static File dataFile;
static {
try {
dataFile = Paths.get(dataURL.toURI()).toFile();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
private static Scanner dataScanner = null;
private static PrintWriter dataWriter = null;
// Highscore
private int bestScore;
public Highscore () {
// Load scanner with data file
try {
dataScanner = new Scanner(dataFile);
} catch (IOException e) {
System.out.println("Cannot load highscore!");
}
// Store highscore
bestScore = Integer.parseInt(dataScanner.nextLine());
}
/**
* @return Player's highscore
*/
public int bestScore () {
return bestScore;
}
/**
* Sets new highscore in the data file
*
* @param newBest New score update
*/
public void setNewBest (int newBest) {
// Set new best score
bestScore = newBest;
try {
// Write new highscore to data file
dataWriter = new PrintWriter(FILE_PATH, "UTF-8");
dataWriter.println(Integer.toString(newBest));
dataWriter.close();
} catch (FileNotFoundException | UnsupportedEncodingException e) {
System.out.println("Could not set new highscore!");
}
}
}

View File

@@ -0,0 +1,99 @@
package com.example.flappybird;
/**
* Pipe.java
* Handles collisions and rendering for pipes
*
* @author Paul Krishnamurthy
*/
public class Pipe {
// Pipe coordinates
private int x = FlappyBird.WIDTH + 5;
private int y;
// Placement (top or bottom) of pipe
String location;
// Pipe constants
public static final int WIDTH = 67;
public static final int HEIGHT = 416;
public static final int PIPE_DISTANCE = 150; // Horizontal distance between pipes
public static final int PIPE_SPACING = HEIGHT + 170; // Vertical distance between pipes
private static final int SPEED = -2;
// If the bird can get a point passing this pipe
public boolean canAwardPoint = true;
public Pipe (String location) {
this.location = location;
reset();
}
public void reset () {
x = FlappyBird.WIDTH + 5; // Reset x-coordinate
// Set boundaries for top pipes
// This y-coordinte + PIPE_SPACING will be for the bottom pipe
if (location.equals("top")) {
y = - Math.max((int) (Math.random() * 320) + 30, 140);
}
}
/**
* Moves the pipe
*/
public void move () {
x += SPEED;
}
/**
* Checks for bird colliding with pipe
*
* @param nX Bird x-coordinate
* @param nY Bird y-coordinate
* @param nW Bird width
* @param nH Bird height
* @return If bird is colliding with the pipe
*/
public boolean collide (int nX, int nY, int nW, int nH) {
// Do not allow bird to jump over pipe
if (nX > x && nY < 0 && canAwardPoint) {
return true;
}
return nX < x + WIDTH &&
nX + nW > x &&
nY < y + HEIGHT &&
nY + nH > y;
}
/**
* @return Pipe's x-coordinate
*/
public int getX () {
return x;
}
/**
* @return Pipe's y-coordinate
*/
public int getY () {
return y;
}
/**
* Set's pipe's y-coordinate (for bottom pipes)
*
* @param newY New y-coordinate
*/
public void setY (int newY) {
y = newY;
}
}

View File

@@ -0,0 +1,135 @@
package com.example.flappybird; /**
* Sprites.java
* Cuts up the main sprite sheet
*
* @author Paul Krishnamurthy
*/
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
import java.io.IOException;
import java.io.File;
import javax.imageio.ImageIO;
import java.util.HashMap;
public class Sprites {
// Resize factor to match frame size
private static final double RESIZE_FACTOR = 2.605;
private static BufferedImage spriteSheet = null;
// HashMap of texture objects
private static HashMap<String, Texture> textures = new HashMap<String, Texture>();
public Sprites () {
// Try to load sprite sheet, exit program if cannot
try {
spriteSheet = ImageIO.read(this.getClass().getResource("/res/img/spriteSheet.png"));
} catch (IOException e) {
e.printStackTrace();
System.out.println("Could not load sprite sheet.");
System.exit(-1); // Exit program if file could not be found
return;
}
// Backgrounds
textures.put("background1", new Texture(resize(spriteSheet.getSubimage(0, 0, 144, 256)), 0, 0));
textures.put("background2", new Texture(resize(spriteSheet.getSubimage(146, 0, 144, 256)), 0, 0));
// Pipes
textures.put("pipe-top", new Texture(resize(spriteSheet.getSubimage(56, 323, 26, 160)), 0, 0));
textures.put("pipe-bottom", new Texture(resize(spriteSheet.getSubimage(84, 323, 26, 160)), 0, 0));
// Birds
textures.put("yellowBird1", new Texture(resize(spriteSheet.getSubimage(31, 491, 17, 12)), 172, 250));
textures.put("yellowBird2", new Texture(resize(spriteSheet.getSubimage(59, 491, 17, 12)), 172, 250));
textures.put("yellowBird3", new Texture(resize(spriteSheet.getSubimage(3, 491, 17, 12)), 172, 250));
textures.put("blueBird1", new Texture(resize(spriteSheet.getSubimage(115, 329, 17, 12)), 172, 250));
textures.put("blueBird2", new Texture(resize(spriteSheet.getSubimage(115, 355, 17, 12)), 172, 250));
textures.put("blueBird3", new Texture(resize(spriteSheet.getSubimage(87, 491, 17, 12)), 172, 250));
textures.put("redBird1", new Texture(resize(spriteSheet.getSubimage(115, 407, 17, 12)), 172, 250));
textures.put("redBird2", new Texture(resize(spriteSheet.getSubimage(115, 433, 17, 12)), 172, 250));
textures.put("redBird3", new Texture(resize(spriteSheet.getSubimage(115, 381, 17, 12)), 172, 250));
// Buttons
textures.put("playButton", new Texture(resize(spriteSheet.getSubimage(354, 118, 52, 29)), 34, 448));
textures.put("leaderboard", new Texture(resize(spriteSheet.getSubimage(414, 118, 52, 29)), 203, 448));
textures.put("rateButton", new Texture(resize(spriteSheet.getSubimage(465, 1, 31, 18)), 147, 355));
// Helpful / Text
textures.put("newHighscore", new Texture(resize(spriteSheet.getSubimage(112, 501, 16, 7)), 210, 305));
textures.put("titleText", new Texture(resize(spriteSheet.getSubimage(351, 91, 89, 24)), 72, 100));
textures.put("getReadyText", new Texture(resize(spriteSheet.getSubimage(295, 59, 92, 25)), 68, 180));
textures.put("gameOverText", new Texture(resize(spriteSheet.getSubimage(395, 59, 96, 21)), 62, 100));
textures.put("instructions", new Texture(resize(spriteSheet.getSubimage(292, 91, 57, 49)), 113, 300));
// SCORE IMAGES
// Large numbers
textures.put("score-0", new Texture(resize(spriteSheet.getSubimage(496, 60, 12, 18)), 0, 0));
textures.put("score-1", new Texture(resize(spriteSheet.getSubimage(136, 455, 8, 18)), 0, 0));
int score = 2;
for (int i = 292; i < 335; i += 14) {
textures.put("score-" + score, new Texture(resize(spriteSheet.getSubimage(i, 160, 12, 18)), 0, 0));
textures.put("score-" + (score + 4), new Texture(resize(spriteSheet.getSubimage(i, 184, 12, 18)), 0, 0));
score++;
}
// Mini numbers
score = 0;
for (int i = 323; score < 10; i += 9) {
textures.put("mini-score-" + score, new Texture(resize(spriteSheet.getSubimage(138, i, 10, 7)), 0, 0));
score ++;
if (score % 2 == 0) { i += 8; }
}
// Medals
textures.put("bronze", new Texture(resize(spriteSheet.getSubimage(112, 477, 22, 22)), 73, 285));
textures.put("silver", new Texture(resize(spriteSheet.getSubimage(112, 453, 22, 22)), 73, 285));
textures.put("gold", new Texture(resize(spriteSheet.getSubimage(121, 282, 22, 22)), 73, 285));
textures.put("platinum", new Texture(resize(spriteSheet.getSubimage(121, 258, 22, 22)), 73, 285));
// Other assets
textures.put("base", new Texture(resize(spriteSheet.getSubimage(292, 0, 168, 56)), 0, 521));
textures.put("scoreCard", new Texture(resize(spriteSheet.getSubimage(3, 259, 113, 57)), 40, 230));
}
/**
* Resizes a BufferedImage
*
* @param image BufferedImage object
* @return New resized image
*/
private static BufferedImage resize (BufferedImage image) {
// New width and height
int newWidth = (int) (image.getWidth() * RESIZE_FACTOR);
int newHeight = (int) (image.getHeight() * RESIZE_FACTOR);
// Create new BufferedImage with updated width and height
BufferedImage resizedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = resizedImage.createGraphics();
g.drawImage(image, 0, 0, newWidth, newHeight, null);
g.dispose();
return resizedImage;
}
/**
* Public getter method for Textures HashMap
*
* @return Texture
*/
public HashMap<String, Texture> getGameTextures () {
return textures;
}
}

View File

@@ -0,0 +1,57 @@
package com.example.flappybird; /**
* Texture.java
* Stores data for game textures
*
* @author Paul Krishnamurthy
*/
import java.awt.image.BufferedImage;
import java.awt.Rectangle;
public class Texture {
// Image attributes
private BufferedImage image;
private int x, y, width, height;
private Rectangle rect;
public Texture (BufferedImage image, int x, int y) {
this.image = image;
this.x = x;
this.y = y;
this.width = image.getWidth();
this.height = image.getHeight();
this.rect = new Rectangle(x, y, width, height);
}
////////////////////////////////////////////////
// Public getter methods for image attributes //
////////////////////////////////////////////////
public BufferedImage getImage () {
return image;
}
public int getX () {
return x;
}
public int getY () {
return y;
}
public int getWidth () {
return width;
}
public int getHeight () {
return height;
}
public Rectangle getRect () {
return rect;
}
}

View File

@@ -0,0 +1,3 @@
module com.example.flappybirdrefactoring {
requires java.desktop;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB