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.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 {
|
||||
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', 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,17 +152,22 @@ 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;
|
||||
}
|
||||
if (player.x + Player.PLAYER_WIDTH >= GAME_WIDTH) {
|
||||
player.x = GAME_WIDTH - Player.PLAYER_WIDTH;
|
||||
}
|
||||
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
|
||||
|
@ -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--;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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 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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue