commit 7860ceefe9f6de0ae98ad0b3daac0d0276d2bd0c Author: Sagi Dayan Date: Sat Jan 23 12:53:19 2016 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96cd793 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/* +*.iml +out/* \ No newline at end of file diff --git a/src/Engine/CollisionUtil.java b/src/Engine/CollisionUtil.java new file mode 100644 index 0000000..0cffff6 --- /dev/null +++ b/src/Engine/CollisionUtil.java @@ -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>> 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; + } + + + +} diff --git a/src/Engine/GameEngine.java b/src/Engine/GameEngine.java new file mode 100644 index 0000000..fab845e --- /dev/null +++ b/src/Engine/GameEngine.java @@ -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 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 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= 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 -10 ) { + acceleration--; + } + locY -= acceleration; + if(angle <= 45) + angle += 5; + } + + public void jump() { + this.acceleration = 10; + angle = -45; + } + +} diff --git a/src/Sprites/Pipe.java b/src/Sprites/Pipe.java new file mode 100644 index 0000000..757e926 --- /dev/null +++ b/src/Sprites/Pipe.java @@ -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; + } +} diff --git a/src/Sprites/SideScollerBackground.java b/src/Sprites/SideScollerBackground.java new file mode 100644 index 0000000..bee410a --- /dev/null +++ b/src/Sprites/SideScollerBackground.java @@ -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); + } + + +} diff --git a/src/Sprites/Sprite.java b/src/Sprites/Sprite.java new file mode 100644 index 0000000..60bcdf3 --- /dev/null +++ b/src/Sprites/Sprite.java @@ -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)); + + } + +}