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)