diff --git a/img/enemy/slime/slimeDead.png b/img/enemy/slime/slimeDead.png new file mode 100644 index 0000000..fa64e72 Binary files /dev/null and b/img/enemy/slime/slimeDead.png differ diff --git a/img/enemy/slime/slimeWalk1.png b/img/enemy/slime/slimeWalk1.png new file mode 100644 index 0000000..fd38fb7 Binary files /dev/null and b/img/enemy/slime/slimeWalk1.png differ diff --git a/img/enemy/slime/slimeWalk2.png b/img/enemy/slime/slimeWalk2.png new file mode 100644 index 0000000..d2defb6 Binary files /dev/null and b/img/enemy/slime/slimeWalk2.png differ diff --git a/src/GamePanel.java b/src/GamePanel.java index bb788f6..5fba2bd 100644 --- a/src/GamePanel.java +++ b/src/GamePanel.java @@ -11,13 +11,9 @@ import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage; import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.FileNotFoundException; import java.io.IOException; import javax.imageio.ImageIO; import java.util.ArrayList; -import java.util.Scanner; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; import javax.swing.*; @@ -33,13 +29,16 @@ public class GamePanel extends JPanel implements Runnable, KeyListener{ public Graphics graphics; public Player player; public BackgroundImage background; - public int frame; + public int playerFrame, enemyFrame; // keeps track of how many ticks has elapsed since last frame change - public int frameCounter = 0; + public int playerFrameCounter = 0; + public int enemyFrameCounter = 0; - public BufferedImage[][][] spriteArray = new BufferedImage[2][2][11]; + public BufferedImage[][][] playerSpriteArray = new BufferedImage[2][2][11]; + public BufferedImage[][][] slimeSpriteArray = new BufferedImage[2][2][3]; public static ArrayListmap = new ArrayList(); + public static ArrayListenemy = new ArrayList(); public static Camera camera; @@ -51,19 +50,35 @@ public class GamePanel extends JPanel implements Runnable, KeyListener{ public GamePanel() throws IOException, SpriteException, UnsupportedAudioFileException, LineUnavailableException { camera = new Camera(0); background = new BackgroundImage(0, 0, backgroundImage, GAME_WIDTH, GAME_HEIGHT, 10); - for (int i = 0; i < 11; i++) { - try { + try { + // load player sprites from disk here + for (int i = 0; i < 11; i++) { BufferedImage sprite = getImage(String.format("img/walk/p1_walk%s.png", String.format("%1$2s", i+1).replace(' ', '0'))); - spriteArray[1][0][i] = sprite; - spriteArray[1][1][i] = sprite; - spriteArray[0][0][i] = flipImageHorizontally(sprite); - spriteArray[0][1][i] = flipImageHorizontally(sprite); - - } catch (IOException e) { - e.printStackTrace(); + playerSpriteArray[1][0][i] = sprite; + playerSpriteArray[1][1][i] = sprite; + playerSpriteArray[0][0][i] = flipImageHorizontally(sprite); + playerSpriteArray[0][1][i] = flipImageHorizontally(sprite); } + // load slime sprites from disk here + // these variables were not defined above because they are temporary variables + BufferedImage[] temporarySlimeArray = {getImage("img/enemy/slime/slimeWalk1.png"), + getImage("img/enemy/slime/slimeWalk2.png"), + getImage("img/enemy/slime/slimeDead.png")}; + BufferedImage[] flippedTemporarySlimeArray = {flipImageHorizontally(getImage("img/enemy/slime/slimeWalk1.png")), + flipImageHorizontally(getImage("img/enemy/slime/slimeWalk2.png")), + flipImageHorizontally(getImage("img/enemy/slime/slimeDead.png"))}; + // please note that these sprites are reversed compared to the player sprites + slimeSpriteArray[0][0] = temporarySlimeArray; + slimeSpriteArray[0][1] = temporarySlimeArray; + slimeSpriteArray[1][0] = flippedTemporarySlimeArray; + slimeSpriteArray[1][1] = flippedTemporarySlimeArray; + } catch (IOException e) { + e.printStackTrace(); } + player = new Player(GAME_WIDTH/2, GAME_HEIGHT/2, 'W', 'A', 'S', 'D', playerSpriteArray); //create a player controlled player, set start location to middle of screenk + enemy.add(new NonPlayer(1200, 100, slimeSpriteArray, 50, 28)); + enemy.add(new NonPlayer(0, 100, slimeSpriteArray, 50, 28)); player = new Player(GAME_WIDTH/2, GAME_HEIGHT/2, 'W', 'A', 'S', 'D', spriteArray); //create a player controlled player, set start location to middle of screenk // the height of 35 is set because it is half of the original tile height (i.e., 70px) this.setFocusable(true); //make everything in this class appear on the screen @@ -87,19 +102,22 @@ public class GamePanel extends JPanel implements Runnable, KeyListener{ //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, frame);//update the positions of everything on the screen + draw(graphics, playerFrame, enemyFrame);//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, int frame){ + public void draw(Graphics g, int playerFrame, int enemyFrame){ background.draw(g); - for(int i=0; i= GAME_HEIGHT - Player.PLAYER_HEIGHT) { - player.y = GAME_HEIGHT - Player.PLAYER_HEIGHT; - player.yVelocity = 0; - player.isGrounded = true; - } - if (player.x <= 0) { - player.x = 0; - } - if (player.x <= 0) { - player.x = 0; - } - if (player.x + Player.PLAYER_WIDTH >= GAME_WIDTH) { - player.x = GAME_WIDTH - Player.PLAYER_WIDTH; + player.y = GAME_HEIGHT - Player.PLAYER_HEIGHT; + player.yVelocity = 0; + player.isGrounded = true; + } + if (player.x <= 0) { + player.x = 0; + } + if (player.x + Player.PLAYER_WIDTH >= GAME_WIDTH) { + player.x = GAME_WIDTH - Player.PLAYER_WIDTH; + } + + for (NonPlayer n : enemy) { + if (n.y >= GAME_HEIGHT - n.npcHeight) { + n.y = GAME_HEIGHT - n.npcHeight; + n.yVelocity = 0; + n.isGrounded = true; } } + } //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 @@ -161,10 +189,15 @@ public class GamePanel extends JPanel implements Runnable, KeyListener{ move(); checkCollision(); repaint(); - if (frameCounter > 5) { + if (playerFrameCounter > 5) { // increment sprite image to be used and keeps it below 12 - frame = (frame + 1) % 11; - frameCounter -= 5; + playerFrame += 1; + playerFrameCounter -= 5; + } + if (enemyFrameCounter > 5) { + // increment sprite image to be used and keeps it below 12 + enemyFrame += 1; + enemyFrameCounter -= 5; } delta--; } diff --git a/src/GenericSprite.java b/src/GenericSprite.java index ce3c20d..f5c13a2 100644 --- a/src/GenericSprite.java +++ b/src/GenericSprite.java @@ -13,16 +13,19 @@ public class GenericSprite extends Rectangle{ public double xVelocity; public final double SPEED = 20; //movement speed of ball public final double speedCap = 5; //Speed cap of ball - public int WIDTH = 20; //size of ball - public int HEIGHT = 20; //size of ball + public int WIDTH; //size of ball + public int HEIGHT; //size of ball public boolean rightPressed = false; public boolean leftPressed = false; public boolean upPressed= false; public boolean downPressed = false; public boolean isGrounded = false; //constructor creates ball at given location with given dimensions + // TODO: reverse order of height and width public GenericSprite(int x, int y, int height, int width){ - super(x, y, height, width);WIDTH = width; HEIGHT = height; + super(x, y, width, height); + WIDTH = width; + HEIGHT = height; } //called from GamePanel when any keyboard input is detected @@ -41,12 +44,12 @@ public class GenericSprite extends Rectangle{ //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(); + } public void move(){ + } public void capSpeed(){ @@ -61,8 +64,8 @@ public class GenericSprite extends Rectangle{ //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, WIDTH, HEIGHT); + + } } \ No newline at end of file diff --git a/src/NonPlayer.java b/src/NonPlayer.java new file mode 100644 index 0000000..d2818b9 --- /dev/null +++ b/src/NonPlayer.java @@ -0,0 +1,56 @@ +/* Eric Li, ICS4U, Completed 5/29/2022 + +Paddle class defines behaviours for the left and right player-controlled paddles */ + +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.UnsupportedAudioFileException; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.IOException; + +public class NonPlayer extends GenericSprite { + public final int SPEED = 3; + // please note that these are not static, in contrast to the player class, as different enemies will have different heights + public int npcWidth; + public int npcHeight; + public int currentXDirection, currentYDirection; + public boolean isDead; + + // private final Sound bump; + + public BufferedImage[][][] spriteArray; + public NonPlayer(int x, int y, BufferedImage[][][] sprites, int npcWidth, int npcHeight) throws UnsupportedAudioFileException, LineUnavailableException, IOException { + super(x, y, npcHeight, npcWidth); + // bump = new Sound("sound/bump.wav"); + spriteArray = sprites; + // TODO: remove + this.npcWidth = npcWidth; + WIDTH = npcWidth; + this.npcHeight = npcHeight; + HEIGHT = npcHeight; + + xVelocity = 3; + } + + public void move(){ + x += (int)xVelocity; + y += (int)yVelocity; + yVelocity+=0.3; + capSpeed(); + } + + public int draw(Graphics g, int frame) { + if (!isDead) { + // last frame is reserved for death animation + frame %= spriteArray[0][0].length - 1; + currentXDirection = (int)(Math.signum(xVelocity) + 1) / 2; + currentYDirection = (int)(Math.signum(yVelocity) + 1) / 2; + // x-GamePanel.camera.x is used as the camera doesn't follow NPCs + g.drawImage(spriteArray[currentXDirection][currentYDirection][frame], x-GamePanel.camera.x, y, null); + return 1; + } else { + g.drawImage(spriteArray[currentXDirection][currentYDirection][spriteArray[0][0].length-1], x-GamePanel.camera.x, y, null); + return 0; + } + } +} \ No newline at end of file diff --git a/src/Player.java b/src/Player.java index 5732256..5bef996 100644 --- a/src/Player.java +++ b/src/Player.java @@ -17,7 +17,7 @@ public class Player extends GenericSprite { public int lastXDirection, lastYDirection, lastFrame; public int upKey, downKey, rightKey, leftKey; - private Sound jump; + private final Sound jump; // sA[0] is -x, -y // sA[1] is x, -y // sA[2] is -x, y @@ -152,16 +152,8 @@ public class Player extends GenericSprite { capSpeed(); } - public void capSpeed(){ - if(xVelocity>speedCap){ - xVelocity = speedCap; - } else if(xVelocity<-1*speedCap) { - xVelocity = -1*speedCap; - } - } - public int draw(Graphics g, int frame) { - // g.setColor(Color.WHITE); + frame %= spriteArray[0][0].length; if (!upPressed && !downPressed && !leftPressed && !rightPressed) { g.drawImage(spriteArray[lastXDirection][lastYDirection][0], x, y, null); return 0;