Karte mit PositionsWechsel

Jo Leute,
würde gerne eine Karte erstellen mit beispielweise 5 x 5 Feldern.
Man sollte sich als Object Spieler über diese Felder bewegen können in “+”/"-" X sowie Y Richtung.

Felder Speichern dann in mehrdem. Array ? Oder doch Hashmap ?
Natürlich sollte diese Karte auch erweiterbar sein auf X,Y Felder.
Wenn ja, wie kann ich den Array die DateiTypen

<Feld> <Feld>

zuweisen ?

import java.util.ArrayList;

public class Feld
{
    static List<Feld> feldList = new ArrayList<Feld>(); // Liste mit allen Feldern
    private String n; // Name
    private int x; // OrtX
    private int y; // OrtY

    public Feld(String n,int x,int y)
    {      
        this.n = n;
        this.x = x;
        this.y = y;
        feldList.add(this); // Feld wird zur globalen Liste hinzugefügt
    }

    public static Feld getFeld(int x)
    {
        return feldList.get(x); // Gibt Feld an position x aus
    }

    public String getName()
    {
        return this.n;
    }

}

Spieler

{
    private int px; // X Positon des Spielers
    private int py; // Y "" 

    public Player()
    {
        px++; // Bewegt den Spieler in x Richtung
    }

    public Feld getPosition()
    {
        return Feld.getFeld(px);
    }

}```

Für ein 2D Spielfeld würde ich dann auch ein 2D-Array verwenden. Das könnte bspw. so aussehen:
Feld[][] spielfeld = new Feld[5][5];

Ich würde die Position der Spielfigur auch nicht unbedingt im Player speichern, sondern vermutlich eher im Feld. Noch schöner wäre eine Klasse Spielfeld, die das alles steuert:

public class Spielfeld {
  private Feld[][] felder;

  // Erzeugt quadratisches Spielfeld in der gewünschten Größe
  public Spielfeld(int groesse) {
    felder = new Feld[groesse][groesse];
  }

  public void bewegeZu(Player player, int x, int y) {
    // Prüfen ob x und y gültig (im Spielfeld) sind

    Feld feld = felder[x][y];
    feld.setPlayer(player);
  }
}

Vielleicht ist hierbei wichtig zu sagen, dass es nur genau ein SpielFeld gibt und einen Spieler.
Das SpielFeld beinhaltet natürlich die Felder als Objekte.

Eine Nebenbemerkung. Kommentare an Variablen lassen sich meist umgehen, indem man diesen Variablen gleich einen sprechenden Namen gibt. Also etwa statt

public class Feld
{
    static List<Feld> feldList = new ArrayList<Feld>(); // Liste mit allen Feldern
    private String n; // Name
    private int x; // OrtX
    private int y; // OrtY

    public Feld(String n,int x,int y)
    {      
        this.n = n;
        this.x = x;
        this.y = y;
        feldList.add(this); // Feld wird zur globalen Liste hinzugefügt
    }

    [...]
}

besser

public class Feld
{
    static List<Feld> feldList = new ArrayList<Feld>();
    private String name;
    private int ortX;
    private int ortY;

    public Feld(String name, int ortX, int ortY)
    {      
        this.name= name;
        this.ortX = ortX;
        this.ortY = ortY;
        feldList.add(this); // Feld wird zur globalen Liste hinzugefügt
    }

    [...]
}

Das ist nur ein KlassenSkizze und sollte fürs Forum sein.

So 5x5. Du brauchst Methoden für:
moveLeft
moveRight
moveUp
moveDown
moveLeftUp
moveRightUp
moveLeftDown
moveRightDown
Dann willst du nicht immer schreiben müssen, neuePos >= 5? Daher verwendet man dann Modulo, Modulo kann aber auch negative Zahlen, deshalb:

int neuePos = (((altePos % 5) + 5) % 5);
(altePos ist z. B. -1)
Dann eine DS (wie oben),
List<ArrayList<Feld>> lalf = new....; (oder spielfeld/felder(s))
Dann noch Ausgabemethoden (toString()-)…
Schreibe diese 4 Sachen erst mal, denn sehen ich/wir weiter. Grüßle

Man sollte kritischst hinterfragen, ob diese Methoden denn alle notwendig sind. Die “low-level”-Generalisierung wäre

void move(int dx, int dy) { ... }

die man mit den entsprechenden Werten (0,1), (0,-1), … (1,1) aufrufen kann, für “Up”, “Down”, … “UpRight”.

Je nachdem, wie man das ganze verwenden will, könnte es auch sinnvoll(er) sein, sich sowas zu basteln wie eine

enum Direction {
    UP(0,1),
    DOWN(0,-1),
    ...
    UP_RIGHT(1,1),
    ...
}

die dann intern diese move-Methode aufruft. (Oder vielleicht auch in einer Methode wie

Feld getNeighbor(Direction d) { .. }

verwendet werden könnte…)

Für das Spielfeld an sich würde ich, nebenbei, ohnehin ein Interface bzw. eine einfache Klasse definieren. Dann kann man sich später aussuchen ob man’s als array[][] oder List oder Map implementiert…

Ja, kritisch hinterfragen ist gut.

List<ArrayList< ist Quatsch.

Die Frage ist, welche moves sind erlaubt.

Findet man sicher irgendwo im Internet:

	private Object[][] felder = new Object[5][5];
	private int x = 4;
	private int y = 4;
	
	public Brett() {
		for (int i = 0; i < felder.length; i++) {
			for (int j = 0; j < felder**.length; j++) {
				felder**[j] = 'o';
			}
		}
		felder[y][x] = '.';
	}
	
	public void move(int w, int h) {
		Object tmp = felder[y][x];
		felder[y][x] = 'o';
		y = ((((y+w) % felder.length) + felder.length) % felder.length);
		x = ((((x+h) % felder[y].length) + felder[y].length) % felder[y].length);
		felder[y][x] = tmp;
	}
	
	public String toString() {
		String s = "";
		for (int i = 0; i < felder.length; i++) {
			for (int j = 0; j < felder**.length; j++) {
				s += felder**[j] + " ";
			}
			s += "
";
		}
		return s;
	}
	
	public static void main(String[] args) {
		Brett t = new Brett();
		System.out.println(t);
		for (int i = 0; i < 15; i++) {
			t.move((int) (Math.random() * 3.0) - 1, (int) (Math.random() * 3.0) - 1);
			System.out.println(i + 1 + "
" + t);
		}
	}

}```

Replace Brett durch Spiel und felder durch fields, etc. Auch new Object[5][6]; wäre denkbar. Bitte eine Variable für 'o' und so.

Eine mögliche Ausgabe: 

o o o o o
o o o o o
o o o o o
o o o o o
o o o o .

1
o o o o o
o o o o o
o o o o o
. o o o o
o o o o o

2
o o o o o
o o o o o
o o o o o
o . o o o
o o o o o

3
o o o o o
o o o o o
. o o o o
o o o o o
o o o o o

4
o o o o o
o o o o o
. o o o o
o o o o o
o o o o o

5
o o o o o
o . o o o
o o o o o
o o o o o
o o o o o

6
o o o o o
o o o o o
o o . o o
o o o o o
o o o o o

7
o o o o o
o o o o o
o o o o o
o o o . o
o o o o o

8
o o o o o
o o o o o
o o o o o
o o o o o
o o . o o

9
o o o o o
o o o o o
o o o o o
o o o o o
o o . o o

10
o o o o o
o o o o o
o o o o o
o o o o o
o . o o o

11
o o o o o
o o o o o
o o o o o
o o o o o
o . o o o

12
o o . o o
o o o o o
o o o o o
o o o o o
o o o o o

13
o o o . o
o o o o o
o o o o o
o o o o o
o o o o o

14
o o o o o
o o o . o
o o o o o
o o o o o
o o o o o

15
o o o o o
o o o o o
o o . o o
o o o o o
o o o o o



Ich hab das ohne IDe getippt, also nicht gleich hauen. @TO: Melde dich einfach, was du vorhast.

Ich war ne Zeit nicht verfügbar morgen lege ich los :slight_smile: danke

Bitte, aber mein (doppel-)post kann eigentlich weg (js-aufforderung versehentlich). Brett müsste eigentlich Game heißen, felder fields, x ist die horizontale und y die vertikale Dimension.

Dann Konstruktor zwei Zeichen/Objekte mitgeben.

Dann
h = y+h…
w = x+w…
usw.

Da x oder w von y oder h abhängig ist.

Dann müssen die zwei Zeichen/Objekte getauscht/geswappt werden.

Wenn das jemand anpassen könnte?

Mit einzelnen move()-Methoden lässt sich das alles “begrenzen”.

Grüße an euch, 3. Adv. nun auch.

*** Edit ***

Hier, noch mal neu:

	private Object[][] fields = new Object[5][6];
	private int x = 4;
	private int y = 4;
	
	public Game(Object o1, Object o2) {
		for (int i = 0; i < fields.length; i++) {
			for (int j = 0; j < fields**.length; j++) {
				fields**[j] = o1;
			}
		}
		fields[y][x] = o2;
	}
	
	public void move(int w, int h) {
		h = ((((y+h) % fields.length) + fields.length) % fields.length);
		w = ((((x+w) % fields[h].length) + fields[h].length) % fields[h].length);
		change(w, h, x, y);
		x = w; y = h;
	}
	
	public String toString() {
		String s = "";
		for (int i = 0; i < fields.length; i++) { // (+= in loop)
			for (int j = 0; j < fields**.length; j++) {
				s += fields**[j] + " ";
			}
			s += "
";
		}
		return s;
	}
	
	private void change(int w, int h, int x, int y) {
		Object tmp = fields[h][w];
		fields[h][w] = fields[y][x];
		fields[y][x] = tmp;
	}
	
	public static void main(String[] args) {
		Game t = new Game('o', 'x');
		t.move(-1, 0); // move on left
		System.out.println(t);
		for (int i = 0; i < 15; i++) {
			t.move((int) (Math.random() * 3.0) - 1, (int) (Math.random() * 3.0) - 1);
			System.out.println(i + 1 + "
" + t);
		}
	}

}```
o1 == zB Zeichen für leer
o2 == zB Zeichen für Spieler
x == w == width == verschiebe nach links/rechts (also horizontal, xAchse)
y == h == height == verschiebe nach oben/unten (also vertikal, yAchse)

Bitte.. @Mod(s): Beitrag #8 (von mir) bitte entfernen, weil ist jetzt verbessert.

Hey dein Ansatz am Anfang finde ich eigentlich gar nicht so falsch. Vor allem wenn es komplexer wird. Wenn es allerdings richtig kompliziert wird, dann wäre auch eine Hashmap nicht falsch (imho) bei der jedes Feld (sei es denn individuell) eine ID bekommt und nur geladen wird, wenn das Feld wirklich betreten wird (und nach einem bestimmten Intervall die ungenutzten wieder rausgeräumt werden).
Dabei würde ich das ganze dann aber so gestalten, dass jedes Feld seine Nachbarn kennt und das ganze nicht mehr direkt von der Game-Klasse gesteuert wird. Jedes Feld sollte auch eine Art Heartbeat haben, so dass es sich nach längerer ungenutzter Zeit serialisiert (oder serialisieren lässt) und dann zerstört. Sollte das Feld wieder betreten werden, wird es erneut geladen.

Ich habe das just4fun auch mal eben programmiert (1. Variante), aber wahrscheinlich etwas aufwendiger (auch wenn ich ein paar wichtige Abfrage
evtl. übersprungen habe und die String-Konkatenationen an machen stellen ‘unschön’ aussehen.

Hier einfach mal der Code:

Game.class
[spoiler]


import com.sun.istack.internal.logging.Logger;
import java.util.Scanner;

/**
 * @author BinaryLogic
 * @description Hauptklasse/-instanz, die sich um die Initialisierung
 * und die Ablaufreihenfolge kümmert
 */
public class Game implements Runnable {

    private static final Logger logger = Logger.getLogger(Game.class);
    private static final int WORLDSIZE_WIDTH = 10;
    private static final int WORLDSIZE_HEIGHT = 10;

    private StringBuilder stringBuilder;
    private Scanner scanner;
    private boolean interrupted;
    private Field[][] fields;
    private Player player;

    private void initialize() {
        initializeUtilities();
        initializeAndBuildWorld();
        initializePlayer();
        // hier könnte es weitergehen mit initializeAndBuildEnemies usw.
    }

    private void initializeUtilities() {
        // weitere Utilities könnten z.B. Klassen zur Deserialisierung von Feldern sein
        // Monster- und Itembuilder usw. (evtl. aber eher auslagern)
        scanner = new Scanner(System.in);
        stringBuilder = new StringBuilder();
    }

    private void initializePlayer() {
        player = new Player("Spieler 1");

        int x = (int) Math.random()*WORLDSIZE_HEIGHT;
        int y = (int) Math.random()*WORLDSIZE_WIDTH;
        player.setPosition(x, y);
        fields[x][y].walkIn(player);
        logger.info("Initialize new player '" + player.getName() + "' on position [" + x + "," + y + "]");
    }

    private void initializeAndBuildWorld() {
        initializeWorld();
        buildWorld();
    }

    private void initializeWorld() {
        // alles was zur Welt gehört initialisieren
        // hier minimal
        logger.info("Initialize world with " + WORLDSIZE_WIDTH + "*" +  WORLDSIZE_WIDTH + " null fields.");
        fields = new Field[WORLDSIZE_HEIGHT][WORLDSIZE_WIDTH];
    }

    private void buildWorld() {
        // hier alles was zur Welt gehört bauen mit Inhalten evtl. spezielle FieldBuilder-Klasse
        // hier minimal
        logger.info("Initialize all fields on world map with default name");
        for(int row = 0; row < WORLDSIZE_HEIGHT; row++) {
            for(int column = 0; column < WORLDSIZE_WIDTH; column++) {
                fields[row][column] = new Field(
                        String.format("Field [%d,%d]", row, column)
                );
            }
        }
    }

    public static void main(String[] args) {
        Game game = new Game();
        game.initialize();
        Thread th = new Thread(game);
        th.start();
    }

    @Override
    public void run() {
        while(!interrupted) {
            print();
            update();
        }
    }
    private void update() {
        // zugelassen sind n (norden), w (westen), o (osten), s (sueden)
        // hier normalerweise [genaue] Input-Abfrage
        // weitere updates mögl. z.B. Monsterbewegung
        System.out.println("Naechster Schritt: ");
        String input = scanner.next().trim().toLowerCase();

        if(isInterrupted(input)) { return; }
        updatePlayerPosition(input);
    }

    private boolean isInterrupted(String input) {
        if(input.equals("x")) {
            interrupted = true;
        }
        return interrupted;
    }

    private void updatePlayerPosition(String input) {
        Position playerPosition = player.getPosition();
        int row = playerPosition.getX();
        int column = playerPosition.getY();

        if(input.equals("n") && row > 0) {
            updatePlayerPosition(row, column, Direction.NORTH);
        }
        else if(input.equals("w") && column > 0) {
            updatePlayerPosition(row, column, Direction.WEST);
        }
        else if(input.equals("o") && column < (fields[0].length-1)) {
            updatePlayerPosition(row, column, Direction.EAST);
        }
        else if(input.equals("s") && row < (fields.length-1)) {
            updatePlayerPosition(row, column, Direction.SOUTH);
        } else {
            System.out.println("Es sind nur die Himmelsrichtungen: 'n','w','o','s' und 'x' zum Abbruch erlaubt.");
        }
    }

    private void updatePlayerPosition(int row, int column, Direction direction) {
        fields[row][column].walkOut(player);
        fields[row + direction.getdX()][column + direction.getdY()].walkIn(player);
        player.changePosition(direction.getdX(), direction.getdY());
    }

    private void print() {
        stringBuilder = new StringBuilder();
        for(int row = 0; row < fields.length; row++) {
            for(int column = 0; column < fields.length; column++) {
                stringBuilder.append(fields[row][column]);
            }
            stringBuilder.append("
");
        }
        System.out.println(stringBuilder.toString());
        stringBuilder.delete(0, stringBuilder.length());
    }
}

[/spoiler]

Field.class
[spoiler]```package de.binarylogic;

/**

  • @author BinaryLogic

  • @description

  • Field bezeichnet genau ein Feld auf der

  • Weltkarte
    */
    public class Field {

    private final String name;
    // könne auch Liste mit Spielern beinhalten
    private Player player;
    // weitere Instanzvariablen, wie Terrain, Items, Mobs, Details, Whatever

    public Field(String name) {
    this.name = name;
    }

    public String getName() {
    return name;
    }

    /**

    • @description Spieler betritt das Feld
    • @param player die Spieler-Instanz, die das Feld betritt
      */
      public void walkIn(Player player) {
      this.player = player;
      }

    /**

    • @description Spieler verlässt das Feld
    • @param player die Spieler-Instanz, die das Feld verlässt
      */
      public void walkOut(Player player) {
      this.player = null;
      }

    @Override
    public String toString() {
    if(player == null) {
    return String.format("[0]");
    }
    return String.format("[P]");
    }
    }

[/spoiler]


Player.class
[spoiler]```package de.binarylogic;

import java.util.UUID;
import java.util.logging.Logger;

/**
 * @author BinaryLogic
 * @description Player ist ein einzelner Spieler
 */
public class Player {

    private static final Logger logger = Logger.getLogger(Player.class.getName());
    private final UUID id = UUID.randomUUID();
    private Position position;
    private String name;
    private int level;
    // usw.

    public Player(String name) {
        this.name = name;
        level = 0;
    }

    public UUID getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public void setPosition(int x, int y) {
        position = new Position(x, y);
    }

    public Position getPosition() {
        return position;
    }

    public void changePosition(int x, int y) {
        position.add(x, y);
    }
}
```[/spoiler]


Position.class
[spoiler]```package de.binarylogic;

/**
 * @author BinaryLogic
 * @description Position des Spielers
 */
public class Position {

    private int x;
    private int y;

    public Position(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    @Override
    public String toString() {
        return String.format("[%d,%d]", x, y);
    }

    public void add(int x, int y) {
        this.x += x;
        this.y += y;
    }
}
```[/spoiler]


Direction.class
[spoiler]```package de.binarylogic;

/**
 * @author BinaryLogic
 * @description Direction beinhaltet die einzelnen
 * Richtungen in welche sich Einheiten (speziell Spieler) bewegen können
 */
public enum Direction {
    NORTH(-1, 0),
    SOUTH(+1, 0),
    WEST(0, -1),
    EAST(0, 1);

    private int dX;
    private int dY;

    Direction(int dX, int dY) {
        this.dX = dX;
        this.dY = dY;
    }

    public int getdX() {
        return dX;
    }

    public void setdX(int dX) {
        this.dX = dX;
    }

    public int getdY() {
        return dY;
    }

    public void setdY(int dY) {
        this.dY = dY;
    }
}
```[/spoiler]


Ich habe allerdings keine Ahnung wie gut oder schlecht das ist, dass soll jemand beurteilen mit mehr Erfahrung. (Habe mein Code bisher kaum jemandem gezeigt :D)

Was stellt denn dieses

private final UUID id = UUID.randomUUID()
da :)?

Klasse für Position Sinvoll für X und Y in Player :d ? ( Wir bleiben bei 2D )

Direction mäßig soll es nur jeweils nach links recht oben und unten gehen. Also (x)±1 / (y)±1

World builder ist echt keine schlechte Idee ich habe ne Loader Klasse die alles für mich erledigt :slight_smile:

Player

[SPOILER]

import java.util.ArrayList;
public class Player
{
    private final String name;
    public static List<Player> player = new ArrayList<Player>();
    public Player(String n)
    {
        this.name = n;
        player.add(this);
    }

        public static Player getPlayer()
    {
        return player.get(0);
    }
}
```[/SPOILER]

Feld
[SPOILER]
```import java.util.List;
import java.util.ArrayList;

public class Feld
{
    static List<Feld> globalFeldList = new ArrayList<Feld>(); // Liste mit allen Feldern

    private final String NAME;           
    private final String QUOTE;      // Beschreibung 
    private final int X;             // Festgelegte Position in der übergelegenen Map
    private final int Y;
    private final List<Monster> monsterSpawn = new ArrayList<Monster>(); // Monster die Spawnen können

    private boolean isplayer;        // Ob der Spieler auf dem Feld ist ( Es gibt nur einen )

    private Player player;           

    public Feld(String name,String quote,int x,int y)
    {      
        this.NAME = name;
        this.QUOTE = quote;
        this.X = x;
        this.Y = y;
        globalFeldList.add(this); // Feld wird zur globalen Liste hinzugefügt
    }

    public void walkIn()
    {
        if(isPlayer())
        {
            System.out.println(QUOTE);
            this.player = Player.getPlayer();
        }
    }

    private void walkOut()
    {
        this.player = null;
    }

    public String getQuote()
    {        
        return this.QUOTE;
    }

    public boolean isPlayer()
    {
        return this.isplayer;
    }

    public String getName()
    {
        return this.NAME;
    }

}
```[/SPOILER]

Das Array ist für mich nicht so leicht greifbar. Ich müsste das Array halt schon mit BESTIMMTEN Felder füllen.

Ja, das mit der Position muss man nicht unbedingt machen. Ich blähe das gerne etwas auf,
damit ich schnell rüberschauen kann und gleich begreife was passiert ohne zu überlegen, was jetzt war :wink:

Bzgl. UUID:
Universally Unique Identifier – Wikipedia
https://docs.oracle.com/javase/8/docs/api/java/util/UUID.html

Bzgl. Position:
Ich finde die Idee mit den Builder immer ganz nett, vor allem wenn es darum geht die Kopplung etwas zu lockern.

Bzgl. statischen Listen in o.g. Klassen:
Im OOP-Sinne ist die Idee mit den statischen Listen in der Klasse selbst aber wahrscheinlich nicht die Beste.

Bzgl. Spieler/Einheiten-Bewgungen auf dem Spielfeld
Eventuell könnte man sich für so etwas einen EntityManager (oder -Controller) erstellen, der sich um alle
Einheiten (egal ob Spieler/NPC) kümmert. Der weiß dann wo sich die Einheiten befinden und kann mit einem
RegionManager(oder -Controller) kommunizieren, wenn sich irgendwas ändert. Der RegionManager hält dann
die verschiedenen Felder, welcher er in Regionen verpackt (Dorf, Nordberge, Teufelsmoor, whatever).
Außerdem bin ich noch nicht einmal wirklich sicher, ob der Spieler überhaupt seine Position zu wissen braucht
(man könnte ihn auch einfach durch die Räume schieben) :stuck_out_tongue_winking_eye: => aber da habe ich wahrscheinlich zu wenig Ahnung um das zu beurteilen

Kommt immer darauf an, für welchen Zweck das ganze sein soll. Lieber das ganze etwas aufblähen, wenn es umfangreicher/komplexer wird,
dann muss man nicht den Code an X-stellen ändern, wenn doch was anderes gemacht wird. Ansonsten geht es natürlich auch weit leichter.

Jo, ich hab’s überflogen. Wirklich langer Text (tldr;), man kann das wirkl ausweiten.

Direction/enum verwendet (gut), run → update → updatePlayerPosition → updatePlayerPosition → player.changePosition → position.add → this.x += x usw. An der Stelle könnte Modulo wirklich helfen.

(int) Math.random()*WORLDSIZE_HEIGHT
(int) [explicit narrowing primitiv type conversion] bindet stärker (links nach rechts, aber höherer Rang), gibt immer 0 zurück.

boolean interrupted könnte volatile deklariert werden → mehrere Game-Threads || mehrere main-Threads (von außen) → AngelikaLanger.com - Java Multithread Support - Anhalten und Beenden von Threads - Angelika Langer Training/Consulting

Sonst wirkl alles schön.

Alles ist immer noch konsolen-basiert, private void print() {-basiert, d. h. (G)UI-Klassen mit paintComponent() repaint() usw. kämen u. a. noch dazu.

Logger.getLogger verwende ich eigentlich nicht, irgendwie ist mir der „unheimlich“ :smiley: . Grüßle

Die Bewegung gefällt mir auch noch nicht zu 100%, weil man hier bei neuen Richtungen (so, sw, nw, no, hoch, runter) immer an zwei Stellen Änderungen vornehmen müsste. Diese Methodenschachtelung mache ich immer so, damit ich grob sehe was passiert, wenn ich wirklich ins Detail will, kann ich mich da sehr leicht mit der IDE und entspr. Tastenkombis durchhangeln.

(int) Math.random()*WORLDSIZE_HEIGHT
(int) [explicit narrowing primitiv type conversion] bindet stärker (links nach rechts, aber höherer Rang), gibt immer 0 zurück.

Guter Einwand, danke! Mir ist schon aufgefallen, dass der Spieler immer bei 0,0 startet, habe das bisher aber noch nicht weiter verfolgt (oder schon wieder verdrängt ;P)

@Rest:
An Threads oder GUI habe ich jetzt noch gar nicht so gedacht (klar Game als MainThread, aber davon sollten eigntl. eh nicht mehrere Instanzen laufen).
GUI würde sowieso nicht direkt in der Game-Klasse einbauen sondern mit einem Controller interagieren, der sich um die Weitergabe der (wirklich relevanten Informationen) an die GUI kümmert.

Edit:
Was den Logger angeht, den benutze ich immer lieber für Ausgaben die nicht an den Benutzer (in dem Fall) Spieler gedacht sind. Mit
einer einfachen Config-File oder ein paar Zeilen Code kann man diese Ausgaben dann nämlich einfach z.B. in eine Log-Datei schreiben lassen. Wenn ich alles System.out. mache, müsste ich das später manuell ändern.
An 1-2 Stellen sehe ich auch noch String.format(), die da nicht mehr hingehören :wink:
Die randomisierte Spielerposition hab ich gefixt, fehlen ja nur klammern.