Initial commit

This commit is contained in:
Sagi Dayan 2016-01-23 12:53:19 +02:00
commit 7860ceefe9
20 changed files with 769 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
.idea/*
*.iml
out/*

View file

@ -0,0 +1,106 @@
package Engine;
import Sprites.Sprite;
import java.awt.*;
import java.awt.image.PixelGrabber;
/**
* Created by sagi on 12/19/15.
*/
public class CollisionUtil {
/**
* Checks if a Sprite is colliding with another Sprite.
* @param otherSprite The Sprite to check a collission with.
* @param thisSprite another sprite
* @return true if the Sprites collide, otherwise false.
*/
public static boolean collidesWith(Sprite thisSprite, Sprite otherSprite) {
boolean isColliding=false;
Rectangle r1 = thisSprite.getBounds();
Rectangle r2 = otherSprite.getBounds();
r1.intersection(r2);
if(intersection(r1, r2)) {
isColliding = pixelPerfectCollision(thisSprite, otherSprite, r1, r2);
}
return isColliding;
}
private static boolean intersection(Rectangle r, Rectangle d) {
int rect1x = r.x;
int rect1y = r.y;
int rect1w = r.width;
int rect1h = r.height;
int rect2x = d.x;
int rect2y = d.y;
int rect2w = d.width;
int rect2h = d.height;
return (rect1x + rect1w >= rect2x &&
rect1y + rect1h >= rect2y &&
rect1x <= rect2x + rect2w &&
rect1y <= rect2y + rect2h);
}
/*
* pixelPerfectCollision(); first determines the area where the sprites collides
* AKA the collision-rectangle. It then grabs the pixels from both sprites
* which are inside the rectangle. It then checks every pixel from the arrays
* given by grabPixels();, and if 2 pixels at the same position are opaque,
* (alpha value over 0) it will return true. Otherwise it will return false.
*/
private static boolean pixelPerfectCollision(Sprite sprite1,Sprite sprite2, Rectangle r1, Rectangle r2) {
/*
* Get the X-values and Y-values for the two coordinates where the sprites collide
*/
int cornerTopX = (r1.x>r2.x)?r1.x:r2.x;
int cornerBottomX = ((r1.x+r1.width) < (r2.x+r2.width))?(r1.x+r1.width):(r2.x+r2.width);
int cornerTopY = (r1.y>r2.y)?r1.y:r2.y;
int cornerBottomY = ((r1.y+r1.height) < (r2.y+r2.height))?(r1.y+r1.height):(r2.y+r2.height);
//Determine the width and height of the collision rectangle
int width=cornerBottomX-cornerTopX;
int height=cornerBottomY-cornerTopY;
//Create arrays to hold the pixels
int[] pixels1 = new int[width*height];
int[] pixels2 = new int[width*height];
//Create the pixelgrabber and fill the arrays
PixelGrabber pg1 = new PixelGrabber(sprite1.getbImage(), cornerTopX-(int)sprite1.getLocX(), cornerTopY-(int)sprite1.getLocY(), width, height, pixels1, 0, width);
PixelGrabber pg2 = new PixelGrabber(sprite2.getbImage(), cornerTopX-(int)sprite2.getLocX(), cornerTopY-(int)sprite2.getLocY(), width, height, pixels2, 0, width);
//Grab the pixels
try {
pg1.grabPixels();
pg2.grabPixels();
} catch (InterruptedException ex) {
//Logger.getLogger(Sprite.class.getName()).log(Level.SEVERE, null, ex);
}
//Check if pixels at the same spot from both arrays are not transparent.
for(int i=0;i<pixels1.length;i++) {
int a = (pixels1[i] >>> 24) & 0xff;
int a2 = (pixels2[i] >>> 24) & 0xff;
/* Awesome, we found two pixels in the same spot that aren't
* completely transparent! Thus the sprites are colliding!
*/
if(a > 0 && a2 > 0) return true;
}
return false;
}
}

243
src/Engine/GameEngine.java Normal file
View file

@ -0,0 +1,243 @@
package Engine;
import Sprites.*;
import javax.swing.*;
import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.Random;
import java.util.Vector;
/**
* Created by sagi on 12/18/15.
*/
public class GameEngine extends MouseAdapter {
private final int PIPE_ACC = 10, PIPE_WIDTH = 90;
public boolean gameOn , gameOver, isFirstGame, canScore;
private Bird bird;
private Vector<Sprite> pipes; //will save all laser shots and asteroids which are currently on the screen.
private int pWidth, pHeight; //panel dimensions
private Timer pipeTimer;
private Random r;
private int score;
private BufferedImage sceneImage;
private Vector<SideScollerBackground> backgrounds;
private AudioClip themeAudioClip, jumpAudioClip;
private final URL jumpURL= getClass().getResource("/Sounds/jump.wav");
private final URL themeURL= getClass().getResource("/Sounds/theme.wav");
public GameEngine(int width, int height){
//initialize variables and load audio\image files.
this.canScore = true;
this.isFirstGame = true;
this.gameOver = true;
this.pWidth = width;
this.pHeight = height;
try {
jumpAudioClip = Applet.newAudioClip(jumpURL);
themeAudioClip = Applet.newAudioClip(themeURL);
}catch (Exception e){
jumpAudioClip = null;
themeAudioClip = null;
}
if(themeAudioClip != null)
themeAudioClip.loop();
r = new Random();
sceneImage = new BufferedImage(width, height, Image.SCALE_SMOOTH);
startNewGame();
}
/**
* initialize and reset vars and timers to "new game" configuration.
*/
private void startNewGame(){
this.gameOn = true;
pipeTimer = new Timer(2000, new PipeTimerListener());
backgrounds = new Vector<>();
initBackgrounds();
initGame();
}
/**
* Setup all actors in the game to a new game - reset timer
*/
private void initGame(){
pipes = new Vector<>();
this.bird = new Bird(100, this.pWidth, this.pHeight, 45);
this.score = 0;
gameOn = true;
pipeTimer.start();
}
private void initBackgrounds(){
backgrounds.add(new SideScollerBackground(pWidth, pHeight, 2, "skyLine.png", pWidth, pHeight));
backgrounds.add(new SideScollerBackground(pWidth, pHeight, 5, "trees.png", pWidth + 50, pHeight));
backgrounds.add(new SideScollerBackground(pWidth, pHeight, 10, "ground.png", pWidth, 45));
}
/**
* returns score
* @return
* int
*/
public int getScore(){
return score;
}
/**
* returns gameOver flag
* @return
* boolean
*/
public boolean isGameOver(){
return this.gameOver;
}
/**
* Create a new Pipe on a random position.
*/
private void createPipe(){
int pipeLoc = (r.nextInt(pHeight-40)+40) * -1;
pipes.add(new Pipe(pipeLoc, pWidth, pHeight, PIPE_ACC, PIPE_WIDTH, pHeight * 2));
}
/**
* Update all sprites, including collision handling.
*/
public void update(){
if(!gameOver) {
bird.update();
for(int i=0; i<pipes.size(); i++){
pipes.elementAt(i).update();
}
for(int i = 0 ; i < backgrounds.size() ; i++){
backgrounds.elementAt(i).update();
}
collisionHandler();
}
}
/**
* Checks if a collision has occurred.
* in case of missile hitting an asteroid - they are both removed.
* in case of asteroid hitting the ship - round over.
*/
private void collisionHandler() {
//Ikaros - dont fly too high, dont fly to low.
if (bird.getLocY() >= pHeight - ( bird.getSWidth() + backgrounds.lastElement().getsHeight())|| bird.getLocY() <= 0) {
pipeTimer.stop();
gameOver = true;
}
//pipe out of screen
if (!pipes.isEmpty() && pipes.elementAt(0).getLocX() + PIPE_WIDTH < 0) {
canScore = true;
pipes.remove(0);
}
//pipe vs. bird
if (!pipes.isEmpty() && CollisionUtil.collidesWith(bird, pipes.elementAt(0))){
gameOver = true;
pipeTimer.stop();
}
if(canScore && !pipes.isEmpty() && bird.getLocX() >= pipes.elementAt(0).getLocX() + pipes.elementAt(0).getSWidth()) {
score++;
canScore = false;
}
}
/**
* render buffered image.
* @param panel
* JPanel
*/
public void render(JPanel panel){
sceneImage = new BufferedImage(this.pWidth, this.pHeight, Image.SCALE_FAST); // Empty Scene
renderScene(sceneImage.getGraphics(), panel); // Paint new Scene
}
/**
* Draws all sprites
* @param g
* Graphics
* @param panel
* Jpanel
*/
public void renderScene(Graphics g, JPanel panel){
backgrounds.elementAt(0).drawSprite(g, panel);
backgrounds.elementAt(1).drawSprite(g, panel);
bird.drawSprite(g, panel);
for(int i=0; i<pipes.size(); i++){
pipes.elementAt(i).drawSprite(g, panel);
}
backgrounds.elementAt(2).drawSprite(g, panel);
}
/**
* creates an new asteroid each 5 seconds *
*/
private class PipeTimerListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent actionEvent) {
createPipe();
}
}
public void mouseClicked(MouseEvent e){
if(gameOver){
// gameOn = true;
this.isFirstGame = false;
gameOver = false;
initGame();
} else {
bird.jump();
jumpAudioClip.play();
}
}
/**
* returns sceneImage
* @return
* BufferedImage
*/
public BufferedImage getScene(){
return this.sceneImage;
}
}

View file

@ -0,0 +1,127 @@
package GamePanels;
import Engine.*;
import javax.swing.*;
import java.awt.*;
//import java.awt.image.BufferedImage;
import java.net.URL;
/**
* Created by sagi on 12/18/15.
*/
public class GamePanel extends JPanel implements Runnable{
private final URL bgURL= getClass().getResource("/Images/bg.jpg");
private final URL gameOverURL= getClass().getResource("/Images/gameOver.png");
private final URL startURL= getClass().getResource("/Images/start.png");
// private int width, height;
private GameEngine engine;
private JLabel lbl_score, lbl_gameOver, lbl_bg, lbl_start;
private ImageIcon img_bg, img_go, img_start;
private Image bg_image;
/**
*
* Constructor.
* initializes all objects on panel and creates gameEngine
*/
public GamePanel(int width, int height){
this.setLayout(null);
this.engine = new GameEngine(width, height);
this.addMouseListener(engine);
this.setFocusable(true);
this.requestFocus();
//set score label at top left corner
this.lbl_score = new JLabel("");
lbl_score.setBounds(15,15,width,30);
//set Static background
img_bg = new ImageIcon(bgURL);
Image tmp_BG = img_bg.getImage();
bg_image = tmp_BG.getScaledInstance(width, height, Image.SCALE_SMOOTH);
img_bg = new ImageIcon(bg_image);
lbl_bg = new JLabel(img_bg);
lbl_bg.setBounds(0,0,width,height);
img_start = new ImageIcon(startURL);
lbl_start = new JLabel(img_start);
lbl_start.setVisible(true);
lbl_start.setBounds(0,0,width,height);
this.add(lbl_start);
img_go = new ImageIcon(gameOverURL);
lbl_gameOver = new JLabel(img_go);
lbl_gameOver.setVisible(false);
lbl_gameOver.setBounds(0,0,width,height);
this.add(lbl_gameOver);
lbl_score.setFont(new Font("Ariel", Font.BOLD, 24));
lbl_score.setForeground(Color.BLACK);
this.add(lbl_score);
repaint();
}
@Override
public void run() {
while(engine.gameOn){
engine.update();
//check if game is over and draw labels accordingly
if(engine.isGameOver()){
if(!engine.isFirstGame)
this.lbl_gameOver.setVisible(true);
}else{
this.lbl_gameOver.setVisible(false);
lbl_start.setVisible(false);
}
//render graphics
engine.render(this);
repaint();
//sleep for other processes to take control
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void addNotify(){
super.addNotify();
(new Thread(this)).start();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g.drawImage(bg_image, 0, 0, this); //draw the background
g2d.drawImage(engine.getScene(),0,0,this); //Draw the scene
//Some labels - Score & countDown
lbl_score.setText("SCORE : " + engine.getScore());
}
}

BIN
src/Images/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
src/Images/birdSprite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
src/Images/gameOver.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
src/Images/ground.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/Images/pipe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
src/Images/skyLine.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
src/Images/start.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/Images/trees.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

3
src/META-INF/MANIFEST.MF Normal file
View file

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: Main

18
src/Main.java Normal file
View file

@ -0,0 +1,18 @@
import javax.swing.*;
import GamePanels.*;
/**
* Created by sagi on 12/18/15.
*/
public class Main {
public static void main(String args[]){
JFrame frame = new JFrame("Flappy Angry Birds");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1200, 600);
frame.setResizable(false);
frame.add(new GamePanel(1200, 600));
frame.setVisible(true);
}
}

BIN
src/Sounds/jump.wav Normal file

Binary file not shown.

BIN
src/Sounds/theme.wav Normal file

Binary file not shown.

31
src/Sprites/Bird.java Normal file
View file

@ -0,0 +1,31 @@
package Sprites;
/**
* Created by sagi on 22/01/2016.
*/
public class Bird extends Sprite {
public Bird(int xPosition, int pWidth, int pHeight, int size){
super(xPosition, pHeight/2, pWidth, pHeight, 0, "birdSprite.png", 0 ,size, size);
}
@Override
public void update() {
if(acceleration > -10 ) {
acceleration--;
}
locY -= acceleration;
if(angle <= 45)
angle += 5;
}
public void jump() {
this.acceleration = 10;
angle = -45;
}
}

18
src/Sprites/Pipe.java Normal file
View file

@ -0,0 +1,18 @@
package Sprites;
/**
* Created by sagi on 23/01/2016.
*/
public class Pipe extends Sprite {
public Pipe(int y, int w, int h, int acc, int sWidth, int sHeight) {
super(w + sWidth, y, w, h, acc, "pipe.png", 0, sWidth, sHeight);
}
@Override
public void update() {
locX -= acceleration;
}
}

View file

@ -0,0 +1,61 @@
package Sprites;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
/**
* Created by sagi on 23/01/2016.
*/
public class SideScollerBackground extends Sprite {
private int locXCopy;
public SideScollerBackground(int w, int h, int acc, String imgName, int sWidth, int sHeight) {
super(0, 0, w, h, acc, imgName, 0, sWidth, sHeight);
locXCopy = pWidth;
if(sHeight != pHeight){
locY = pHeight-sHeight;
}
}
/*
* resizes image to a set size
*/
@Override
protected void setImageDimensions()
{
Image tmp = bImage.getScaledInstance(sWidth, sHeight, Image.SCALE_SMOOTH);
BufferedImage bi = new BufferedImage(sWidth, sHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.drawImage(tmp,0,0,null);
g2d.dispose();
bImage = bi;
}
@Override
public void update() {
locX -= acceleration;
locXCopy -= acceleration;
if(locX+pWidth < 0)
locX = pWidth;
if(locXCopy+pWidth < 0)
locXCopy = pWidth;
}
@Override
public void drawSprite(Graphics g, JPanel p){
super.drawSprite(g,p);
g.drawImage(bImage, locXCopy, locY, p);
}
}

159
src/Sprites/Sprite.java Normal file
View file

@ -0,0 +1,159 @@
package Sprites;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
public abstract class Sprite {
protected BufferedImage bImage;
protected int imageWidth, imageHeight; // image dimensions
protected URL imagePath;
protected int locX, locY;
protected int acceleration;
protected int pWidth, pHeight; // panel's dimensions
protected int sWidth, sHeight;
protected double angle;
public Sprite(int x, int y, int w, int h, int acc, String imgName, double angle, int sWidth, int sHeight)
{
this.imagePath = getClass().getResource("/Images/"+imgName);
this.sWidth = sWidth;
this.sHeight = sHeight;
locX = x;
locY = y;
acceleration = acc;
pWidth = w;
pHeight = h;
this.angle = angle;
//load image from source files
try {
bImage = ImageIO.read(imagePath);
}catch (IOException pin){
pin.printStackTrace();
bImage = null;
}
setImageDimensions();
}
/*
* resizes image to a set size
*/
protected void setImageDimensions()
{
Image tmp = bImage.getScaledInstance(sWidth, sHeight, Image.SCALE_SMOOTH);
BufferedImage bi = new BufferedImage(sWidth, sHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.drawImage(tmp,0,0,null);
g2d.dispose();
bImage = bi;
}
/**
* Abstract method to update sprite.
*/
public abstract void update();
/**
* returns sprite x position
* @return
* double
*/
public double getLocX() {return locX;}
/**
* returns sprite y position
* @return
* double
*/
public double getLocY() {return locY;}
public int getSWidth() {return sWidth;}
public int getsHeight() {return sHeight;}
/**
* returns sprite acceleration
* @return
* int
*/
public int getAcceleration() {return acceleration;}
/**
* returns sprite size
* @return
* int
*/
public BufferedImage getbImage() {return bImage;}
/**
* returns image width
* @return
* int
*/
public int getImageWidth() {return imageWidth;}
/**
* returns image height
* @return
* int
*/
public int getImageHeight() {return imageHeight;}
/**
* returns sprite angle
* @return
* double
*/
public double getAngle() {return angle;}
/**
* returns shape location and dimensions as a Rectangle.
* @return
* Rectangle
*/
public Rectangle getBounds() {
return new Rectangle((int)locX, (int)locY, sWidth, sHeight);
}
/**
* its not a bug it's a feature. actually it just moves a shape that goes beyond the screen to the other side.
*/
protected void outOfScreeFix(){
if(locX < 0 - sWidth)
locX = pWidth;
else if (locX > pWidth+sWidth)
locX = 0-sWidth;
if(locY < 0 - sHeight)
locY = pHeight;
else if(locY > pHeight+sHeight)
locY = 0-sHeight ;
}
/**
* abstract method for drawing sprite.
* @param g
* @param p
*/
public void drawSprite(Graphics g, JPanel p){
Graphics2D g2d = (Graphics2D)g;
g2d.rotate(Math.toRadians(angle), locX + (bImage.getWidth()/2), locY + (bImage.getHeight()/2));
g.drawImage(bImage, locX, locY, p);
g2d.rotate(-1*Math.toRadians(angle), locX + (bImage.getWidth()/2), locY + (bImage.getHeight()/2));
}
}