From 0a6d76da3a26e297131b01137ec716f7c4499817 Mon Sep 17 00:00:00 2001 From: Asecave Date: Fri, 25 Jun 2021 18:39:42 +0200 Subject: [PATCH] fixing snake --- Zoelda/src/main/Main.java | 5 +- Zoelda/src/main/World.java | 22 ++++++- Zoelda/src/main/entities/Entity.java | 9 +++ Zoelda/src/main/entities/LivingEntity.java | 17 ++++- Zoelda/src/main/entities/Snake.java | 60 +++++------------- Zoelda/src/main/entities/Spider.java | 10 +++ Zoelda/src/main/entities/player/Player.java | 50 +++++++++------ Zoelda/src/main/maps/Map.java | 30 ++++++++- .../images/snake_spritesheet_calciumtrice.png | Bin 2329 -> 4100 bytes 9 files changed, 128 insertions(+), 75 deletions(-) diff --git a/Zoelda/src/main/Main.java b/Zoelda/src/main/Main.java index 4673f0b..0a70962 100644 --- a/Zoelda/src/main/Main.java +++ b/Zoelda/src/main/Main.java @@ -12,11 +12,8 @@ public class Main extends Game { private World world; private HUD hud; - public static final int WIDTH = 1000; // Fensterbreite - public static final int HEIGHT = 800; // Fensterhöhe - public Main() { - super(WIDTH, HEIGHT); + super(World.SCALE * 15, World.SCALE * 11); instance = this; // Welt initialisieren und Spieler hinzufügen diff --git a/Zoelda/src/main/World.java b/Zoelda/src/main/World.java index c0b157a..961a00f 100644 --- a/Zoelda/src/main/World.java +++ b/Zoelda/src/main/World.java @@ -2,15 +2,18 @@ package main; import ea.Knoten; import main.entities.Entity; -import main.maps.Corridor; +import main.entities.Snake; +import main.entities.Spider; +import main.entities.player.Player; import main.maps.Map; +import main.maps.TestMap; /** * Hier werden alle Maps gespeichert. */ public class World extends Knoten { - public static final int SCALE_FACTOR = 4; // Der Basis Zoomfaktor + public static final int SCALE_FACTOR = 6; // Der Basis Zoomfaktor public static final int SCALE = SCALE_FACTOR * Tile.getSize(); // Eine Gameunit ist so viele pixel lang private Map currentMap; // Die Map die aktuell angezeigt werden soll. @@ -18,11 +21,24 @@ public class World extends Knoten { public World() { // Map initialisieren - currentMap = new Corridor(); + currentMap = new TestMap(); // Map zu EA hinzufügen add(currentMap); + // und Entities auch + Player player = new Player(); + add(player.actionFigur); + currentMap.getEntities().add(player); + currentMap.setPlayer(player); + + Spider spider = new Spider(); + // und Spinnen auch + add(spider.actionFigur); + currentMap.getEntities().add(spider); + Snake snake = new Snake(); + add(snake.actionFigur); + currentMap.getEntities().add(snake); // Alle Entities als ticker registrieren (triggert dann die update methoden) for (Entity e : currentMap.getEntities()) { diff --git a/Zoelda/src/main/entities/Entity.java b/Zoelda/src/main/entities/Entity.java index 1e55521..b8b5051 100644 --- a/Zoelda/src/main/entities/Entity.java +++ b/Zoelda/src/main/entities/Entity.java @@ -17,6 +17,7 @@ public abstract class Entity implements Ticker { protected float accelleration = 0.012f; // Beschleunigung des entities in gameunits pro frame. protected float friction = 0.2f; // Entschleunigung des entities in gameunits pro frame. protected float width = 1f, height = 1f; // Breite und Höhe der Hitbox. + private boolean deleteEntity; // Wenn true wird dieses Entity von der map gelöscht // Das ist die Ticker-Methode von ea; wird jeden frame ausgeführt (frameloop) @Override @@ -169,4 +170,12 @@ public abstract class Entity implements Ticker { public void setVelY(float velY) { this.velY = velY; } + + public void deleteEntity() { + deleteEntity = true; + } + + public boolean isReadyToDelete() { + return deleteEntity; + } } diff --git a/Zoelda/src/main/entities/LivingEntity.java b/Zoelda/src/main/entities/LivingEntity.java index 91ac26c..75b4b7d 100644 --- a/Zoelda/src/main/entities/LivingEntity.java +++ b/Zoelda/src/main/entities/LivingEntity.java @@ -15,6 +15,7 @@ public abstract class LivingEntity extends Entity { protected float spriteOffsetX, spriteOffsetY; // Offset des Sprites. Hier kann man die relative render-Position nachjustieren. protected float hp = 1f; //hp des Entitys protected boolean mirrored; + /** * @param figur - erstes (standart) Sprite * @param name - name des Zustands @@ -41,6 +42,16 @@ public abstract class LivingEntity extends Entity { } else if (velX > 0) { side = false; } + + if (hp <= 0) { + actionFigur.zustandSetzen(getDeathAnimationName()); + velX = 0; + velY = 0; + if (actionFigur.aktuelleFigur().aktuellesBild() == actionFigur.aktuelleFigur().animation().length - 1) { + System.out.println("adasd"); + deleteEntity(); + } + } // Packt das Sprite an die richtige Stelle actionFigur.faktorSetzen((int) (spriteScale * World.SCALE_FACTOR)); @@ -61,7 +72,11 @@ public abstract class LivingEntity extends Entity { return hp; } - public void takeDamage(float damage) { + public void takeDamage(float damage, Entity e) { + actionFigur.zustandSetzen(getDamageAnimationName()); hp -= damage; } + + public abstract String getDeathAnimationName(); + public abstract String getDamageAnimationName(); } diff --git a/Zoelda/src/main/entities/Snake.java b/Zoelda/src/main/entities/Snake.java index 5a1a8cd..a31d937 100644 --- a/Zoelda/src/main/entities/Snake.java +++ b/Zoelda/src/main/entities/Snake.java @@ -1,16 +1,12 @@ package main.entities; -import ea.Vektor; import main.Main; import main.SheetLoader; import main.entities.player.Player; -import java.util.ArrayList; - public class Snake extends LivingEntity { - private static SheetLoader loader = new SheetLoader("/res/images/snake_spritesheet_calciumtrice.png", 32, 32, - new int[] { 10, 10, 10, 10, 10, 8 }); + private static SheetLoader loader = new SheetLoader("/res/images/snake_spritesheet_calciumtrice.png", 32, 32, new int[] { 10, 10, 10, 10, 13, 8 }); public Snake() { super(loader.getFigur(0), "idle"); @@ -26,54 +22,28 @@ public class Snake extends LivingEntity { actionFigur.neuerZustand(loader.getFigur(1), "lost_sight"); actionFigur.neuerZustand(loader.getFigur(2), "walk"); actionFigur.neuerZustand(loader.getFigur(3), "attack"); - actionFigur.neuerZustand(loader.getFigur(4), "die"); - actionFigur.neuerZustand(loader.getFigur(5), "damage"); + actionFigur.neuerZustand(loader.getFigur(4), getDeathAnimationName()); + actionFigur.neuerZustand(loader.getFigur(5), getDamageAnimationName()); + + loader.getFigur(3).animationsGeschwindigkeitSetzen(80); + loader.getFigur(5).animationsGeschwindigkeitSetzen(40); } @Override protected void update() { - ArrayList entities = Main.instance.getWorld().getCurrentMap().getEntities(); - Entity nearestPlayer = null; - for (Entity e : entities) { - if (e instanceof Player) { - if (nearestPlayer == null || e.dist(this) < nearestPlayer.dist(this)) { - nearestPlayer = e; - } - } - } - if (nearestPlayer != null && lineOfSightClear(nearestPlayer) && !actionFigur.aktuellesVerhalten().equals("damage")) { - if (dist(nearestPlayer) < 1f) { - zustandSetzen("attack"); - } else { - if (actionFigur.aktuellesVerhalten().equals("attack")) { - actionFigur.aktuelleFigur().animationsBildSetzen(0); - } - Vektor toPlayer = new Vektor(nearestPlayer.posX - posX, nearestPlayer.posY - posY); - toPlayer = toPlayer.normiert(); - velX += toPlayer.x * accelleration; - velY += toPlayer.y * accelleration; - zustandSetzen("walk"); - } - } else { - if (actionFigur.aktuellesVerhalten().equals("walk")) { - zustandSetzen("lost_sight"); - } - if (actionFigur.aktuellesVerhalten().equals("lost_sight") && actionFigur.aktuelleFigur() - .aktuellesBild() == actionFigur.aktuelleFigur().animation().length - 1) { - actionFigur.aktuelleFigur().animationsBildSetzen(0); - zustandSetzen("idle"); - } - } - if (actionFigur.aktuellesVerhalten().equals("damage") && actionFigur.aktuelleFigur().aktuellesBild() < actionFigur.aktuelleFigur().animation().length - 1){ - zustandSetzen("walk"); - } + Player player = Main.instance.getWorld().getCurrentMap().getPlayer(); + this.checkTileCollisions(Main.instance.getWorld().getCurrentMap()); super.update(); } @Override - public void takeDamage(float damage) { - zustandSetzen("damage"); - super.takeDamage(damage); + public String getDeathAnimationName() { + return "die"; + } + + @Override + public String getDamageAnimationName() { + return "damage"; } } diff --git a/Zoelda/src/main/entities/Spider.java b/Zoelda/src/main/entities/Spider.java index 8c24f25..624ca51 100644 --- a/Zoelda/src/main/entities/Spider.java +++ b/Zoelda/src/main/entities/Spider.java @@ -30,4 +30,14 @@ public class Spider extends LivingEntity { } + @Override + public String getDeathAnimationName() { + return null; + } + + @Override + public String getDamageAnimationName() { + return null; + } + } diff --git a/Zoelda/src/main/entities/player/Player.java b/Zoelda/src/main/entities/player/Player.java index 1dc4280..8baa6a5 100644 --- a/Zoelda/src/main/entities/player/Player.java +++ b/Zoelda/src/main/entities/player/Player.java @@ -7,11 +7,13 @@ import main.SheetLoader; import main.entities.Entity; import main.entities.LivingEntity; +import java.awt.event.ActionListener; import java.util.ArrayList; public class Player extends LivingEntity { - private static SheetLoader loader = new SheetLoader("/res/images/player_sprite_sheet.png", 64, 32, new int[] {5, 8, 7, 6, 2, 5, 4, 7}); + private static SheetLoader loader = new SheetLoader("/res/images/player_sprite_sheet.png", 64, 32, new int[] { 5, 8, 7, 6, 2, 5, 4, 7 }); + private boolean onlyAttackOnceTrigger; public Player() { super(loader.getFigur(0), "idle"); @@ -23,7 +25,7 @@ public class Player extends LivingEntity { spriteScale = 0.8f; posX = 4f; posY = 4f; - + // unterschiedliche Animationsgeschwindigkeiten // für idle loader.getFigur(0).animationsGeschwindigkeitSetzen(200); @@ -35,7 +37,8 @@ public class Player extends LivingEntity { actionFigur.neuerZustand(loader.getFigur(1), "walk"); actionFigur.neuerZustand(loader.getFigur(2), "strike"); actionFigur.neuerZustand(loader.getFigur(3), "swipe"); - actionFigur.neuerZustand(loader.getFigur(7), "die"); + actionFigur.neuerZustand(loader.getFigur(6), getDamageAnimationName()); + actionFigur.neuerZustand(loader.getFigur(7), getDeathAnimationName()); } @Override @@ -46,7 +49,7 @@ public class Player extends LivingEntity { // Bei diser soll man sich nicht bewegen können aber weiter rutschen if (!((actionFigur.aktuellesVerhalten().equals("swipe")) && actionFigur.aktuelleFigur().aktuellesBild() < actionFigur.aktuelleFigur().animation().length - 1)) { - + // wasd movement if (Main.instance.tasteGedrueckt(Taste.A)) { velX -= accelleration; @@ -60,7 +63,7 @@ public class Player extends LivingEntity { if (Main.instance.tasteGedrueckt(Taste.S)) { velY += accelleration; } - + // Auf idle stellen wenn man sich nicht bewegt if (velX == 0 && velY == 0) { zustandSetzen("idle"); @@ -78,17 +81,9 @@ public class Player extends LivingEntity { if (Main.instance.tasteGedrueckt(Taste.F)) { zustandSetzen("die"); } - - if (actionFigur.aktuellesVerhalten().equals("swipe") && actionFigur.aktuelleFigur().aktuellesBild() == 1) { - ArrayList entities = Main.instance.getWorld().getCurrentMap().getEntities(); - for (Entity e : entities) { - if (e instanceof LivingEntity) { - ((LivingEntity) e).takeDamage(0.1f); - } - } - } + onlyAttackOnceTrigger = false; } - + // auf Kollisionen prüfen checkTileCollisions(Main.instance.getWorld().getCurrentMap()); // LivingEntity auch updaten lassen @@ -100,19 +95,32 @@ public class Player extends LivingEntity { // auf Kollisionen prüfen checkTileCollisions(Main.instance.getWorld().getCurrentMap()); - - if (actionFigur.aktuellesVerhalten().equals("swipe") && actionFigur.aktuelleFigur().aktuellesBild() == 1) { + + if (!onlyAttackOnceTrigger && actionFigur.aktuellesVerhalten().equals("swipe") && actionFigur.aktuelleFigur().aktuellesBild() == 1) { + onlyAttackOnceTrigger = true; ArrayList entities = Main.instance.getWorld().getCurrentMap().getEntities(); for (Entity e : entities) { - if (e instanceof LivingEntity && e != this) { + if (e instanceof LivingEntity && e != this && e.dist(this) <= 1f) { LivingEntity le = (LivingEntity) e; Vektor toE = vectorToEntity(le); toE = toE.normiert(); - le.setVelX(le.getVelX()+toE.x * 0.05f); - le.setVelY(le.getVelY()+toE.x * 0.05f); - le.takeDamage(0.1f); + if ((toE.x > 0 && !side) || (toE.x <= 0 && side)) { + le.setVelX(le.getVelX() + toE.x * 0.05f); + le.setVelY(le.getVelY() + toE.y * 0.05f); + le.takeDamage(0.1f, this); + } } } } } + + @Override + public String getDeathAnimationName() { + return "die"; + } + + @Override + public String getDamageAnimationName() { + return "damage"; + } } diff --git a/Zoelda/src/main/maps/Map.java b/Zoelda/src/main/maps/Map.java index 4605914..4c57e1e 100644 --- a/Zoelda/src/main/maps/Map.java +++ b/Zoelda/src/main/maps/Map.java @@ -3,21 +3,28 @@ package main.maps; import java.util.ArrayList; import ea.Knoten; +import ea.Ticker; +import main.Main; import main.Tile; import main.entities.Entity; +import main.entities.LivingEntity; +import main.entities.player.Player; /** * Auf der Map sind alle Entities, sowie die Tiles gespiechert. */ -public abstract class Map extends Knoten { +public abstract class Map extends Knoten implements Ticker { protected Tile[][] map; // Die Tiles der map in einem 2D Array. private ArrayList entities; + private Player player; public Map(int width, int height) { map = new Tile[width][height]; entities = new ArrayList<>(100); + + Main.instance.manager.anmelden(this, 20); } /** @@ -30,6 +37,19 @@ public abstract class Map extends Knoten { return null; } + @Override + public void tick() { + for (int i = 0; i < entities.size(); i++) { + Entity e = entities.get(i); + if (e instanceof LivingEntity) { + if (e.isReadyToDelete()) { + Main.instance.manager.abmelden(e); + entities.remove(e); + } + } + } + } + public int getWidth() { return map.length; } @@ -41,4 +61,12 @@ public abstract class Map extends Knoten { public ArrayList getEntities() { return entities; } + + public Player getPlayer() { + return player; + } + + public void setPlayer(Player player) { + this.player = player; + } } diff --git a/Zoelda/src/res/images/snake_spritesheet_calciumtrice.png b/Zoelda/src/res/images/snake_spritesheet_calciumtrice.png index ceaf5fe9795b6586c61b41e5156a3144fd24a42c..55dd879f0d55d7cab23dc2ea83bce98feb953e83 100644 GIT binary patch literal 4100 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 tvL}i+qaMp*Uh--at*0pzT@IIvZMJ$hP%