master
John 2022-06-03 17:33:21 -04:00 committed by John
parent 1421495745
commit f66fcb5ca2
8 changed files with 154 additions and 56 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

View File

@ -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 ArrayList<Tile>map = new ArrayList<Tile>();
public static ArrayList<NonPlayer>enemy = new ArrayList<NonPlayer>();
public static Camera camera;
@ -51,21 +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 {
// 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);
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', 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)
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));
map.add(new SingleTile(1000, 500, box));
map.add(new SingleTile(700, 400, boxCoin));
map.add(new SingleTile(1000, 300, boxCoin));
@ -91,15 +104,19 @@ 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);
frameCounter += player.draw(g, frame);
playerFrameCounter += player.draw(g, playerFrame);
for (NonPlayer n: enemy) {
// this is a feature; the more enemies there are, the faster the enemies will (appear) to move
enemyFrameCounter += n.draw(g, enemyFrame);
}
for(Tile i: map){
i.draw(g);
}
@ -110,14 +127,22 @@ public class GamePanel extends JPanel implements Runnable, KeyListener{
//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(){
player.move();
for (NonPlayer n: enemy) {
n.move();
}
}
//handles all collision detection and responds accordingly
public void checkCollision() {
player.isGrounded = false;
for (NonPlayer n: enemy) {
n.isGrounded = false;
}
for (Tile i : map) {
i.collide(player);
i.playerCollide(player);
for (NonPlayer n: enemy) {
i.npcCollide(n);
}
}
//force player to remain on screen
if (player.y <= 0) {
@ -127,9 +152,6 @@ public class GamePanel extends JPanel implements Runnable, KeyListener{
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;
@ -137,6 +159,14 @@ public class GamePanel extends JPanel implements Runnable, KeyListener{
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;
}
}
}
@ -164,10 +194,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--;
}

View File

@ -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);
}
}

56
src/NonPlayer.java Normal file
View File

@ -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;
}
}
}

View File

@ -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
@ -76,9 +76,8 @@ public class Player extends GenericSprite {
}
}
// calls parent
public void move(){
y = y + (int)yVelocity;
y += (int)yVelocity;
GamePanel.camera.x = GamePanel.camera.x + (int)xVelocity;
if(rightPressed){
xVelocity+=1;
@ -95,16 +94,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;

View File

@ -10,8 +10,8 @@ public class Tile {
this.y = y;
}
//Actions when tile interacts with sprites
public void collide(GenericSprite s){
//Actions when tile interacts with players
public void playerCollide(GenericSprite s){
int realX = x-GamePanel.camera.x;
if(s.x+s.WIDTH>realX&s.x<realX+length&&s.y+s.HEIGHT>=y&&s.y<=y+length){
if(s.x+s.WIDTH>realX+20&&s.x<realX+length-20&&y-s.y-s.HEIGHT<4){
@ -26,6 +26,19 @@ public class Tile {
}
}
}
public void npcCollide(GenericSprite s){
int realX = x-GamePanel.camera.x;
if(s.x+s.WIDTH>realX&s.x<realX+length&&s.y+s.HEIGHT>=y&&s.y<=y+length){
if(s.x+s.WIDTH>realX+20&&s.x<realX+length-20&&y-s.y-s.HEIGHT<4){
s.isGrounded = true;
s.yVelocity = 0;
s.y = y-s.HEIGHT;
}
if(s.x+s.WIDTH<realX+length/2 || s.x>realX+length-length/2){
s.xVelocity *= -1;
}
}
}
public void draw(Graphics g){
g.setColor(Color.black);
g.fillRect(x-GamePanel.camera.x, y, length, length);