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;
/*
* 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 {
protected float posX, posY;
protected float velX, velY;
protected float accelleration = 0.012f;
protected float friction = 0.2f;
protected float width = 1f, height = 1f;
protected float posX, posY; // Position des entities in gameunits.
protected float velX, velY; // Geschwindigkeit des eitities in gameunits pro frame.
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.
// Das ist die Ticker-Methode von ea; wird jeden frame ausgeführt (frameloop)
@Override
public void tick() {
// Geschwindigkeit wird zur position addiert (es gibt natürlich auch negative
// Ges.)
posX += velX;
posY += velY;
// Reibung wird mit einberechnet, damit nicht unentlich beschleunigt wird
velX *= (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) {
velX = 0f;
}
@@ -25,8 +35,15 @@ public abstract class Entity implements Ticker {
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();
/**
* Checkt collision mit tiles von der map. Fertig, clean, nie wieder ändern.
*/
public void checkTileCollisions(Map map) {
for (int x = (int) (posX - 2); x < posX + 2; x++) {
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 (velY < 0) {
velY = 0;

View File

@@ -3,26 +3,48 @@ package main;
import ea.ActionFigur;
import ea.Figur;
/*
* Alles was zustände hat und sich Bewegen kann, ist ein LivingEntity.
*/
public class LivingEntity extends Entity {
protected boolean side;
protected ActionFigur actionFigur;
protected float spriteOffsetX, spriteOffsetY;
protected boolean side; // true = gespiegelt, false = nicht
protected ActionFigur actionFigur; // Sprite des Entities
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) {
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
protected void update() {
// Prüft zu welcher seite man guckt
if (velX < 0) {
side = true;
} else if (velX > 0) {
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);
actionFigur.positionSetzen(posX * Tile.getSize() + offsetX, posY * Tile.getSize() + offsetY);
// Packt das Sprite an die richtige Stelle
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() {
@@ -41,6 +63,9 @@ public class LivingEntity extends Entity {
velY += accelleration;
}
/**
* Spiegelt die figur autmatisch, wenn nötig.
*/
protected void zustandSetzen(String name) {
actionFigur.spiegelXSetzen(side);
actionFigur.zustandSetzen(name);

View File

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

View File

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

View File

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

View File

@@ -13,8 +13,6 @@ public class PlayerSheetLoader {
private static Figur[] player;
private static final float SCALE_DIV = 1.5f;
public PlayerSheetLoader() {
try {
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) {
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 y = 0; y < h; y++) {
Color clr = new Color(sheet.getRGB((s * w + x), (row * h + y)), true);
if (clr.getAlpha() > 0) {
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);
}
}
}
sprites[s].farbeSetzen(x, y, clr);
}
}
}

View File

@@ -1,7 +1,5 @@
package main;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.IOException;
@@ -10,36 +8,45 @@ import javax.imageio.ImageIO;
import ea.Bild;
import ea.Knoten;
/**
* Ein feld auf der Map
*/
public class Tile extends Knoten {
// IDs der Tiles
public static final int GRASS = 0;
public static final int WALL_TOP = 1;
public static final int WALL_BOTTOM = 2;
private Bild img;
private int id;
private Bild img; // Bild, das gerendert wird
private int id; // Die id dises Tiles
public Tile(int id) {
this.id = id;
// Das Bild laden
try {
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) {
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);
AffineTransform at = new AffineTransform();
at.scale(World.SCALE, World.SCALE);
AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
after = scaleOp.filter(buff, after);
int x = 0;
int y = 0;
img = new Bild(x, y, after);
// Skalieren
BufferedImage scaled = new BufferedImage(World.SCALE, World.SCALE, BufferedImage.TYPE_INT_RGB);
scaled.getGraphics().drawImage(buff, 0, 0, World.SCALE, World.SCALE, null);
img = new Bild(0, 0, scaled);
} catch (IOException e) {
e.printStackTrace();
}
// Bild zu EA hinzufügen.
add(img);
}
/**
* @return den Pfad der zu der Id gehört.
*/
private String getPathFromId(int id) {
switch (id) {
case GRASS:
@@ -52,26 +59,46 @@ public class Tile extends Knoten {
return null;
}
/**
* @return die Größe der Textur
*/
public static int getSize() {
return 16 * World.SCALE;
return 16;
}
/**
* @return ob man durch das Tile durchgehen kann
*/
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;
}
/**
* Kleine Hifsmethode un die koordinate der Top-Kante rurückzugenben.
*/
public int getTop() {
return positionY();
}
/**
* Kleine Hifsmethode un die koordinate der Bottom-Kante rurückzugenben.
*/
public int getBottom() {
return positionY() + getSize();
}
/**
* Kleine Hifsmethode un die koordinate der Left-Kante rurückzugenben.
*/
public int getLeft() {
return positionX();
}
/**
* Kleine Hifsmethode un die koordinate der Right-Kante rurückzugenben.
*/
public int getRight() {
return positionX() + getSize();
}

View File

@@ -2,16 +2,23 @@ package main;
import ea.Knoten;
/**
* Hier werden alle Maps gespeichert.
*/
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) {
// Map initialisieren
currentMap = new Map();
// Map zu EA hinzufügen
add(currentMap);
// und Spieler auch
add(player.actionFigur);
}