This commit is contained in:
Asecave
2021-06-23 15:44:37 +02:00
parent 90680be439
commit 7d055dbe2f
8 changed files with 157 additions and 49 deletions

View File

@@ -2,20 +2,30 @@ package main;
import ea.Ticker; import ea.Ticker;
/*
* Alles was sich bewegen kann oder nicht an das grid der map gebunden ist, ist ein Entity.
* Also Der Spieler, Gegner, Items, Coins, ...
*/
public abstract class Entity implements Ticker { public abstract class Entity implements Ticker {
protected float posX, posY; protected float posX, posY; // Position des entities in gameunits.
protected float velX, velY; protected float velX, velY; // Geschwindigkeit des eitities in gameunits pro frame.
protected float accelleration = 0.012f; protected float accelleration = 0.012f; // Beschleunigung des entities in gameunits pro frame.
protected float friction = 0.2f; protected float friction = 0.2f; // Entschleunigung des entities in gameunits pro frame.
protected float width = 1f, height = 1f; protected float width = 1f, height = 1f; // Breite und Höhe der Hitbox.
// Das ist die Ticker-Methode von ea; wird jeden frame ausgeführt (frameloop)
@Override @Override
public void tick() { public void tick() {
// Geschwindigkeit wird zur position addiert (es gibt natürlich auch negative
// Ges.)
posX += velX; posX += velX;
posY += velY; posY += velY;
// Reibung wird mit einberechnet, damit nicht unentlich beschleunigt wird
velX *= (1f - friction); velX *= (1f - friction);
velY *= (1f - friction); velY *= (1f - friction);
// Wenn die Geschwindigkeit (ungefähr) nicht mehr sichtbar ist wird sie einfach
// auf 0 gecapt.
if (velX < 0.001f && velX > -0.001f) { if (velX < 0.001f && velX > -0.001f) {
velX = 0f; velX = 0f;
} }
@@ -25,8 +35,15 @@ public abstract class Entity implements Ticker {
update(); update();
} }
/**
* Die Update-Methode wird jeden frame ausgeführt. Hier kann man die logik für
* sein Entity reinschreiben wenn man sie Überschreibt.
*/
protected abstract void update(); protected abstract void update();
/**
* Checkt collision mit tiles von der map. Fertig, clean, nie wieder ändern.
*/
public void checkTileCollisions(Map map) { public void checkTileCollisions(Map map) {
for (int x = (int) (posX - 2); x < posX + 2; x++) { for (int x = (int) (posX - 2); x < posX + 2; x++) {
for (int y = (int) (posY - 2); y < posY + 2; y++) { for (int y = (int) (posY - 2); y < posY + 2; y++) {
@@ -53,7 +70,10 @@ public abstract class Entity implements Ticker {
} }
} }
public void onBlockCollision(boolean[] sides, int blockX, int blockY) { /**
* Gehört mit zur collision detection. Hier werden kollisionen aufgelöst.
*/
private void onBlockCollision(boolean[] sides, int blockX, int blockY) {
if (sides[0]) { if (sides[0]) {
if (velY < 0) { if (velY < 0) {
velY = 0; velY = 0;

View File

@@ -3,26 +3,48 @@ package main;
import ea.ActionFigur; import ea.ActionFigur;
import ea.Figur; import ea.Figur;
/*
* Alles was zustände hat und sich Bewegen kann, ist ein LivingEntity.
*/
public class LivingEntity extends Entity { public class LivingEntity extends Entity {
protected boolean side; protected boolean side; // true = gespiegelt, false = nicht
protected ActionFigur actionFigur; protected ActionFigur actionFigur; // Sprite des Entities
protected float spriteOffsetX, spriteOffsetY; protected float spriteScale = 1f; // Skalierung des Sprites
protected float spriteOffsetX, spriteOffsetY; // Offset des Sprites. Hier kann man die relative render-Position nachjustieren.
/**
* @param figur - erstes (standart) Sprite
* @param name - name des Zustands
*/
public LivingEntity(Figur figur, String name) { public LivingEntity(Figur figur, String name) {
actionFigur = new ActionFigur(figur, name); actionFigur = new ActionFigur(figur, name) {
// DEBUG: render boxes
// @Override
// public void zeichnen(Graphics2D g, BoundingRechteck r) {
// g.setColor(Color.GREEN);
// g.drawRect((int) actionFigur.positionX(), (int) actionFigur.positionY(), (int) actionFigur.getBreite(), (int) actionFigur.getHoehe());
// g.drawRect((int) ((posX - width / 2) * World.SCALE), (int) ((posY - height / 2) * World.SCALE), (int) (width * World.SCALE), (int) (height * World.SCALE));
// super.zeichnen(g, r);
// }
};
} }
@Override @Override
protected void update() { protected void update() {
// Prüft zu welcher seite man guckt
if (velX < 0) { if (velX < 0) {
side = true; side = true;
} else if (velX > 0) { } else if (velX > 0) {
side = false; side = false;
} }
int offsetX = (int) ((width * Tile.getSize()) / 2 - (actionFigur.getBreite()) / 2 + spriteOffsetX * Tile.getSize() - (width * Tile.getSize()) / 2);
int offsetY = (int) ((height * Tile.getSize()) / 2 - (actionFigur.getHoehe()) / 2 + spriteOffsetY * Tile.getSize() - (height * Tile.getSize()) / 2); // Packt das Sprite an die richtige Stelle
actionFigur.positionSetzen(posX * Tile.getSize() + offsetX, posY * Tile.getSize() + offsetY); actionFigur.faktorSetzen((int) (spriteScale * World.SCALE_FACTOR));
float offsetX = width / 2 + spriteOffsetX - width / 2;
float offsetY = height / 2 + spriteOffsetY - height / 2;
actionFigur.positionSetzen((posX + offsetX) * World.SCALE - actionFigur.getBreite() / 2, (posY + offsetY) * World.SCALE - actionFigur.getHoehe() / 2);
} }
protected void moveLeft() { protected void moveLeft() {
@@ -41,6 +63,9 @@ public class LivingEntity extends Entity {
velY += accelleration; velY += accelleration;
} }
/**
* Spiegelt die figur autmatisch, wenn nötig.
*/
protected void zustandSetzen(String name) { protected void zustandSetzen(String name) {
actionFigur.spiegelXSetzen(side); actionFigur.spiegelXSetzen(side);
actionFigur.zustandSetzen(name); actionFigur.zustandSetzen(name);

View File

@@ -3,23 +3,30 @@ package main;
import ea.Game; import ea.Game;
import ea.Manager; import ea.Manager;
/**
* Von hier wird alles initialisiert. Das ist die höchste Klasse.
*/
public class Malin extends Game { public class Malin extends Game {
private World world; private World world;
private Player player; private Player player;
public static final int WIDTH = 1440; public static final int WIDTH = 1440; // Fensterbreite
public static final int HEIGHT = 1056; public static final int HEIGHT = 1056; // Fensterhöhe
public Malin() { public Malin() {
super(WIDTH, HEIGHT); super(WIDTH, HEIGHT);
// Lönk
player = new Player(this); player = new Player(this);
// Welt initialisieren und Spieler hinzufügen
world = new World(player); world = new World(player);
// die Welt zu EA hinzufügen
wurzel.add(world); wurzel.add(world);
Manager manager = new Manager(); Manager manager = new Manager();
// Den Spieler als ticker registrieren (triggert dann die update methoden)
manager.anmelden(player, 20); manager.anmelden(player, 20);
} }

View File

@@ -2,29 +2,43 @@ package main;
import ea.Knoten; import ea.Knoten;
/**
* Auf der Map sind alle Entities, sowie die Tiles gespiechert.
*/
public class Map extends Knoten { public class Map extends Knoten {
private Tile[][] map; private Tile[][] map; // Die Tiles der map in einem 2D Array.
public Map() { public Map() {
// Größe der Map ist 15u breit und 11u hoch
map = new Tile[15][11]; map = new Tile[15][11];
// Tiles der map definieren
for (int x = 0; x < map.length; x++) { for (int x = 0; x < map.length; x++) {
for (int y = 0; y < map[0].length; y++) { for (int y = 0; y < map[0].length; y++) {
int id = 0; int id = 0;
// Auf bestimmten koordinaten wird die id des Tiles geändert
if (y == 1 || (x == 7 && y == 7)) { if (y == 1 || (x == 7 && y == 7)) {
id = Tile.WALL_BOTTOM; id = Tile.WALL_BOTTOM;
} }
if (y == 0 || y == map[0].length - 1 || x == 0 || x == map.length - 1 || (x == 7 && y == 6)) { if (y == 0 || y == map[0].length - 1 || x == 0 || x == map.length - 1 || (x == 7 && y == 6)) {
id = Tile.WALL_TOP; id = Tile.WALL_TOP;
} }
// Tile wird erstellt
map[x][y] = new Tile(id); map[x][y] = new Tile(id);
map[x][y].setX(x * Tile.getSize()); // Tile wird an die passende Position gebracht
map[x][y].setY(y * Tile.getSize()); map[x][y].positionSetzen(x * World.SCALE, y * World.SCALE);
// Tile wird zur EA hinzugefügt (damit es auch gerendert wird)
add(map[x][y]); add(map[x][y]);
} }
} }
} }
/**
* Gibt das Tile-Objekt an der gegebenen Koordinate zurück. (nur wenn es existiert)
*/
public Tile getTile(int x, int y) { public Tile getTile(int x, int y) {
if (x >= 0 && x < map.length && y >= 0 && y < map[0].length) { if (x >= 0 && x < map.length && y >= 0 && y < map[0].length) {
return map[x][y]; return map[x][y];

View File

@@ -4,22 +4,28 @@ import ea.Taste;
public class Player extends LivingEntity { public class Player extends LivingEntity {
private Malin main; private Malin main; // Referenz auf die main Klasse. Wird später für den Keyboardinput verwendet
private static PlayerSheetLoader loader = new PlayerSheetLoader(); private static PlayerSheetLoader loader = new PlayerSheetLoader();
public Player(Malin main) { public Player(Malin main) {
super(loader.getPlayer(0), "idle_left"); super(loader.getPlayer(0), "idle_left");
this.main = main; this.main = main;
// Entity-Eigenschaften werden festgelegt
width = 0.7f; width = 0.7f;
height = 0.8f; height = 0.8f;
spriteOffsetY = -0.14f; spriteOffsetY = -0.14f;
spriteScale = 0.8f;
posX = 4f; posX = 4f;
posY = 4f; posY = 4f;
// unterschiedliche Animationsgeschwindigkeiten
// für idle
loader.getPlayer(0).animationsGeschwindigkeitSetzen(200); loader.getPlayer(0).animationsGeschwindigkeitSetzen(200);
// fürs laufen
loader.getPlayer(1).animationsGeschwindigkeitSetzen(50); loader.getPlayer(1).animationsGeschwindigkeitSetzen(50);
// Zustände werden hinzugefügt
actionFigur.neuerZustand(loader.getPlayer(0), "idle"); actionFigur.neuerZustand(loader.getPlayer(0), "idle");
actionFigur.neuerZustand(loader.getPlayer(1), "walk"); actionFigur.neuerZustand(loader.getPlayer(1), "walk");
actionFigur.neuerZustand(loader.getPlayer(2), "strike"); actionFigur.neuerZustand(loader.getPlayer(2), "strike");
@@ -29,10 +35,14 @@ public class Player extends LivingEntity {
@Override @Override
protected void update() { protected void update() {
// Bei dieser animation bleibt man sofort stehen.
if (!((actionFigur.aktuellesVerhalten().equals("strike")) if (!((actionFigur.aktuellesVerhalten().equals("strike"))
&& actionFigur.aktuelleFigur().aktuellesBild() < actionFigur.aktuelleFigur().animation().length - 1)) { && actionFigur.aktuelleFigur().aktuellesBild() < actionFigur.aktuelleFigur().animation().length - 1)) {
// Bei diser soll man sich nicht bewegen können aber weiter rutschen
if (!((actionFigur.aktuellesVerhalten().equals("swipe")) if (!((actionFigur.aktuellesVerhalten().equals("swipe"))
&& actionFigur.aktuelleFigur().aktuellesBild() < actionFigur.aktuelleFigur().animation().length - 1)) { && actionFigur.aktuelleFigur().aktuellesBild() < actionFigur.aktuelleFigur().animation().length - 1)) {
// wasd movement
if (main.tasteGedrueckt(Taste.A)) { if (main.tasteGedrueckt(Taste.A)) {
moveLeft(); moveLeft();
} }
@@ -45,12 +55,15 @@ public class Player extends LivingEntity {
if (main.tasteGedrueckt(Taste.S)) { if (main.tasteGedrueckt(Taste.S)) {
moveDown(); moveDown();
} }
// Auf idle stellen wenn man sich nicht bewegt
if (velX == 0 && velY == 0) { if (velX == 0 && velY == 0) {
zustandSetzen("idle"); zustandSetzen("idle");
} else { } else {
zustandSetzen("walk"); zustandSetzen("walk");
} }
// Attacken
if (main.tasteGedrueckt(Taste.LEERTASTE)) { if (main.tasteGedrueckt(Taste.LEERTASTE)) {
zustandSetzen("swipe"); zustandSetzen("swipe");
} }
@@ -58,7 +71,10 @@ public class Player extends LivingEntity {
zustandSetzen("strike"); zustandSetzen("strike");
} }
} }
// auf Kollisionen prüfen
checkTileCollisions(main.getWorld().getCurrentMap()); checkTileCollisions(main.getWorld().getCurrentMap());
// LivingEntity auch updaten lassen
super.update(); super.update();
} else { } else {
velX = 0; velX = 0;

View File

@@ -13,8 +13,6 @@ public class PlayerSheetLoader {
private static Figur[] player; private static Figur[] player;
private static final float SCALE_DIV = 1.5f;
public PlayerSheetLoader() { public PlayerSheetLoader() {
try { try {
BufferedImage sheet = ImageIO.read(Tile.class.getResourceAsStream("/res/images/player_sprite_sheet.png")); BufferedImage sheet = ImageIO.read(Tile.class.getResourceAsStream("/res/images/player_sprite_sheet.png"));
@@ -51,17 +49,11 @@ public class PlayerSheetLoader {
private void loadSprites(PixelFeld[] sprites, int row, int w, int h, BufferedImage sheet) { private void loadSprites(PixelFeld[] sprites, int row, int w, int h, BufferedImage sheet) {
for (int s = 0; s < sprites.length; s++) { for (int s = 0; s < sprites.length; s++) {
sprites[s] = new PixelFeld(w * (int) (World.SCALE / SCALE_DIV), h * (int) (World.SCALE / SCALE_DIV), 1); sprites[s] = new PixelFeld(w, h, 1);
for (int x = 0; x < w; x++) { for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) { for (int y = 0; y < h; y++) {
Color clr = new Color(sheet.getRGB((s * w + x), (row * h + y)), true); Color clr = new Color(sheet.getRGB((s * w + x), (row * h + y)), true);
if (clr.getAlpha() > 0) { sprites[s].farbeSetzen(x, y, clr);
for (int i = 0; i < World.SCALE / SCALE_DIV; i++) {
for (int j = 0; j < World.SCALE / SCALE_DIV; j++) {
sprites[s].farbeSetzen(x * (int) (World.SCALE / SCALE_DIV) + i, y * (int) (World.SCALE / SCALE_DIV) + j, clr);
}
}
}
} }
} }
} }

View File

@@ -1,7 +1,5 @@
package main; package main;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
@@ -10,36 +8,45 @@ import javax.imageio.ImageIO;
import ea.Bild; import ea.Bild;
import ea.Knoten; import ea.Knoten;
/**
* Ein feld auf der Map
*/
public class Tile extends Knoten { public class Tile extends Knoten {
// IDs der Tiles
public static final int GRASS = 0; public static final int GRASS = 0;
public static final int WALL_TOP = 1; public static final int WALL_TOP = 1;
public static final int WALL_BOTTOM = 2; public static final int WALL_BOTTOM = 2;
private Bild img; private Bild img; // Bild, das gerendert wird
private int id; private int id; // Die id dises Tiles
public Tile(int id) { public Tile(int id) {
this.id = id; this.id = id;
// Das Bild laden
try { try {
BufferedImage buff = ImageIO.read(Tile.class.getResourceAsStream(getPathFromId(id))); BufferedImage buff = ImageIO.read(Tile.class.getResourceAsStream(getPathFromId(id)));
// Gras hat 8 verschiedene Texturen von denen eine zufällig ausgewählt werden
// muss.
if (id == GRASS) { if (id == GRASS) {
buff = buff.getSubimage(16 * (int) (Math.random() * 8), 0, 16, 16); buff = buff.getSubimage(16 * (int) (Math.random() * 8), 0, 16, 16);
} }
BufferedImage after = new BufferedImage(buff.getWidth() * World.SCALE, buff.getHeight() * World.SCALE, BufferedImage.TYPE_INT_ARGB); // Skalieren
AffineTransform at = new AffineTransform(); BufferedImage scaled = new BufferedImage(World.SCALE, World.SCALE, BufferedImage.TYPE_INT_RGB);
at.scale(World.SCALE, World.SCALE); scaled.getGraphics().drawImage(buff, 0, 0, World.SCALE, World.SCALE, null);
AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); img = new Bild(0, 0, scaled);
after = scaleOp.filter(buff, after);
int x = 0;
int y = 0;
img = new Bild(x, y, after);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
// Bild zu EA hinzufügen.
add(img); add(img);
} }
/**
* @return den Pfad der zu der Id gehört.
*/
private String getPathFromId(int id) { private String getPathFromId(int id) {
switch (id) { switch (id) {
case GRASS: case GRASS:
@@ -52,26 +59,46 @@ public class Tile extends Knoten {
return null; return null;
} }
/**
* @return die Größe der Textur
*/
public static int getSize() { public static int getSize() {
return 16 * World.SCALE; return 16;
} }
/**
* @return ob man durch das Tile durchgehen kann
*/
public boolean isCollidable() { public boolean isCollidable() {
// Alle Tiles durch die man nicht laufen soll müssen hier true zurückgeben,
// sonst werden sie bei der Collisiondetection nicht berücksichtigt.
return id == WALL_TOP; return id == WALL_TOP;
} }
/**
* Kleine Hifsmethode un die koordinate der Top-Kante rurückzugenben.
*/
public int getTop() { public int getTop() {
return positionY(); return positionY();
} }
/**
* Kleine Hifsmethode un die koordinate der Bottom-Kante rurückzugenben.
*/
public int getBottom() { public int getBottom() {
return positionY() + getSize(); return positionY() + getSize();
} }
/**
* Kleine Hifsmethode un die koordinate der Left-Kante rurückzugenben.
*/
public int getLeft() { public int getLeft() {
return positionX(); return positionX();
} }
/**
* Kleine Hifsmethode un die koordinate der Right-Kante rurückzugenben.
*/
public int getRight() { public int getRight() {
return positionX() + getSize(); return positionX() + getSize();
} }

View File

@@ -2,16 +2,23 @@ package main;
import ea.Knoten; import ea.Knoten;
/**
* Hier werden alle Maps gespeichert.
*/
public class World extends Knoten { public class World extends Knoten {
public static final int SCALE = 6; 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; private Map currentMap; // Die Map die aktuell angezeigt werden soll.
public World(Player player) { public World(Player player) {
// Map initialisieren
currentMap = new Map(); currentMap = new Map();
// Map zu EA hinzufügen
add(currentMap); add(currentMap);
// und Spieler auch
add(player.actionFigur); add(player.actionFigur);
} }