Add NPCs
parent
1421495745
commit
f66fcb5ca2
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 |
|
@ -11,13 +11,9 @@ import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Scanner;
|
|
||||||
import javax.sound.sampled.LineUnavailableException;
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
@ -33,13 +29,16 @@ public class GamePanel extends JPanel implements Runnable, KeyListener{
|
||||||
public Graphics graphics;
|
public Graphics graphics;
|
||||||
public Player player;
|
public Player player;
|
||||||
public BackgroundImage background;
|
public BackgroundImage background;
|
||||||
public int frame;
|
public int playerFrame, enemyFrame;
|
||||||
// keeps track of how many ticks has elapsed since last frame change
|
// 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<Tile>map = new ArrayList<Tile>();
|
||||||
|
public static ArrayList<NonPlayer>enemy = new ArrayList<NonPlayer>();
|
||||||
|
|
||||||
public static Camera camera;
|
public static Camera camera;
|
||||||
|
|
||||||
|
@ -51,21 +50,35 @@ public class GamePanel extends JPanel implements Runnable, KeyListener{
|
||||||
public GamePanel() throws IOException, SpriteException, UnsupportedAudioFileException, LineUnavailableException {
|
public GamePanel() throws IOException, SpriteException, UnsupportedAudioFileException, LineUnavailableException {
|
||||||
camera = new Camera(0);
|
camera = new Camera(0);
|
||||||
background = new BackgroundImage(0, 0, backgroundImage, GAME_WIDTH, GAME_HEIGHT, 10);
|
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')));
|
BufferedImage sprite = getImage(String.format("img/walk/p1_walk%s.png", String.format("%1$2s", i+1).replace(' ', '0')));
|
||||||
|
|
||||||
spriteArray[1][0][i] = sprite;
|
playerSpriteArray[1][0][i] = sprite;
|
||||||
spriteArray[1][1][i] = sprite;
|
playerSpriteArray[1][1][i] = sprite;
|
||||||
spriteArray[0][0][i] = flipImageHorizontally(sprite);
|
playerSpriteArray[0][0][i] = flipImageHorizontally(sprite);
|
||||||
spriteArray[0][1][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) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
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
|
||||||
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
|
enemy.add(new NonPlayer(1200, 100, slimeSpriteArray, 50, 28));
|
||||||
// the height of 35 is set because it is half of the original tile height (i.e., 70px)
|
enemy.add(new NonPlayer(0, 100, slimeSpriteArray, 50, 28));
|
||||||
map.add(new SingleTile(1000, 500, box));
|
map.add(new SingleTile(1000, 500, box));
|
||||||
map.add(new SingleTile(700, 400, boxCoin));
|
map.add(new SingleTile(700, 400, boxCoin));
|
||||||
map.add(new SingleTile(1000, 300, 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.
|
//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
|
image = createImage(GAME_WIDTH, GAME_HEIGHT); //draw off screen
|
||||||
graphics = image.getGraphics();
|
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
|
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
|
//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);
|
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){
|
for(Tile i: map){
|
||||||
i.draw(g);
|
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
|
//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(){
|
public void move(){
|
||||||
player.move();
|
player.move();
|
||||||
|
for (NonPlayer n: enemy) {
|
||||||
|
n.move();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//handles all collision detection and responds accordingly
|
//handles all collision detection and responds accordingly
|
||||||
public void checkCollision() {
|
public void checkCollision() {
|
||||||
player.isGrounded = false;
|
player.isGrounded = false;
|
||||||
|
for (NonPlayer n: enemy) {
|
||||||
|
n.isGrounded = false;
|
||||||
|
}
|
||||||
for (Tile i : map) {
|
for (Tile i : map) {
|
||||||
i.collide(player);
|
i.playerCollide(player);
|
||||||
|
for (NonPlayer n: enemy) {
|
||||||
|
i.npcCollide(n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//force player to remain on screen
|
//force player to remain on screen
|
||||||
if (player.y <= 0) {
|
if (player.y <= 0) {
|
||||||
|
@ -127,9 +152,6 @@ public class GamePanel extends JPanel implements Runnable, KeyListener{
|
||||||
player.y = GAME_HEIGHT - Player.PLAYER_HEIGHT;
|
player.y = GAME_HEIGHT - Player.PLAYER_HEIGHT;
|
||||||
player.yVelocity = 0;
|
player.yVelocity = 0;
|
||||||
player.isGrounded = true;
|
player.isGrounded = true;
|
||||||
}
|
|
||||||
if (player.x <= 0) {
|
|
||||||
player.x = 0;
|
|
||||||
}
|
}
|
||||||
if (player.x <= 0) {
|
if (player.x <= 0) {
|
||||||
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) {
|
if (player.x + Player.PLAYER_WIDTH >= GAME_WIDTH) {
|
||||||
player.x = GAME_WIDTH - Player.PLAYER_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();
|
move();
|
||||||
checkCollision();
|
checkCollision();
|
||||||
repaint();
|
repaint();
|
||||||
if (frameCounter > 5) {
|
if (playerFrameCounter > 5) {
|
||||||
// increment sprite image to be used and keeps it below 12
|
// increment sprite image to be used and keeps it below 12
|
||||||
frame = (frame + 1) % 11;
|
playerFrame += 1;
|
||||||
frameCounter -= 5;
|
playerFrameCounter -= 5;
|
||||||
|
}
|
||||||
|
if (enemyFrameCounter > 5) {
|
||||||
|
// increment sprite image to be used and keeps it below 12
|
||||||
|
enemyFrame += 1;
|
||||||
|
enemyFrameCounter -= 5;
|
||||||
}
|
}
|
||||||
delta--;
|
delta--;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,16 +13,19 @@ public class GenericSprite extends Rectangle{
|
||||||
public double xVelocity;
|
public double xVelocity;
|
||||||
public final double SPEED = 20; //movement speed of ball
|
public final double SPEED = 20; //movement speed of ball
|
||||||
public final double speedCap = 5; //Speed cap of ball
|
public final double speedCap = 5; //Speed cap of ball
|
||||||
public int WIDTH = 20; //size of ball
|
public int WIDTH; //size of ball
|
||||||
public int HEIGHT = 20; //size of ball
|
public int HEIGHT; //size of ball
|
||||||
public boolean rightPressed = false;
|
public boolean rightPressed = false;
|
||||||
public boolean leftPressed = false;
|
public boolean leftPressed = false;
|
||||||
public boolean upPressed= false;
|
public boolean upPressed= false;
|
||||||
public boolean downPressed = false;
|
public boolean downPressed = false;
|
||||||
public boolean isGrounded = false;
|
public boolean isGrounded = false;
|
||||||
//constructor creates ball at given location with given dimensions
|
//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){
|
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
|
//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
|
//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
|
//changes the current location of the ball to be wherever the mouse is located on the screen
|
||||||
public void mousePressed(MouseEvent e){
|
public void mousePressed(MouseEvent e){
|
||||||
//x = e.getX();
|
|
||||||
// y = e.getY();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void move(){
|
public void move(){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void capSpeed(){
|
public void capSpeed(){
|
||||||
|
@ -61,8 +64,8 @@ public class GenericSprite extends Rectangle{
|
||||||
//called frequently from the GamePanel class
|
//called frequently from the GamePanel class
|
||||||
//draws the current location of the ball to the screen
|
//draws the current location of the ball to the screen
|
||||||
public void draw(Graphics g){
|
public void draw(Graphics g){
|
||||||
g.setColor(Color.black);
|
|
||||||
g.fillOval(x, y, WIDTH, HEIGHT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ public class Player extends GenericSprite {
|
||||||
public int lastXDirection, lastYDirection, lastFrame;
|
public int lastXDirection, lastYDirection, lastFrame;
|
||||||
public int upKey, downKey, rightKey, leftKey;
|
public int upKey, downKey, rightKey, leftKey;
|
||||||
|
|
||||||
private Sound jump;
|
private final Sound jump;
|
||||||
// sA[0] is -x, -y
|
// sA[0] is -x, -y
|
||||||
// sA[1] is x, -y
|
// sA[1] is x, -y
|
||||||
// sA[2] is -x, y
|
// sA[2] is -x, y
|
||||||
|
@ -76,9 +76,8 @@ public class Player extends GenericSprite {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// calls parent
|
|
||||||
public void move(){
|
public void move(){
|
||||||
y = y + (int)yVelocity;
|
y += (int)yVelocity;
|
||||||
GamePanel.camera.x = GamePanel.camera.x + (int)xVelocity;
|
GamePanel.camera.x = GamePanel.camera.x + (int)xVelocity;
|
||||||
if(rightPressed){
|
if(rightPressed){
|
||||||
xVelocity+=1;
|
xVelocity+=1;
|
||||||
|
@ -95,16 +94,8 @@ public class Player extends GenericSprite {
|
||||||
capSpeed();
|
capSpeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void capSpeed(){
|
|
||||||
if(xVelocity>speedCap){
|
|
||||||
xVelocity = speedCap;
|
|
||||||
} else if(xVelocity<-1*speedCap) {
|
|
||||||
xVelocity = -1*speedCap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int draw(Graphics g, int frame) {
|
public int draw(Graphics g, int frame) {
|
||||||
// g.setColor(Color.WHITE);
|
frame %= spriteArray[0][0].length;
|
||||||
if (!upPressed && !downPressed && !leftPressed && !rightPressed) {
|
if (!upPressed && !downPressed && !leftPressed && !rightPressed) {
|
||||||
g.drawImage(spriteArray[lastXDirection][lastYDirection][0], x, y, null);
|
g.drawImage(spriteArray[lastXDirection][lastYDirection][0], x, y, null);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -10,8 +10,8 @@ public class Tile {
|
||||||
this.y = y;
|
this.y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Actions when tile interacts with sprites
|
//Actions when tile interacts with players
|
||||||
public void collide(GenericSprite s){
|
public void playerCollide(GenericSprite s){
|
||||||
int realX = x-GamePanel.camera.x;
|
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&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){
|
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){
|
public void draw(Graphics g){
|
||||||
g.setColor(Color.black);
|
g.setColor(Color.black);
|
||||||
g.fillRect(x-GamePanel.camera.x, y, length, length);
|
g.fillRect(x-GamePanel.camera.x, y, length, length);
|
||||||
|
|
Loading…
Reference in New Issue