# Klasse auf mehrere Objekte zugreifen lassen



## coindrop (8. Jan 2015)

Hallo,

ich bin nicht gerade der geübteste in Java.
Ich soll ein Labyrinthspiel mit Lanterna auf dem Terminal schreiben. Das Level wird aus einer properties-Datei gelesen. In dieser Datei sind Koordinaten mit einem Wert von 0 bis 5, welcher den Typ des Spielfelds (Wand, Eingang, Ausgang, Hindernis, bewegendes Hindernis, Schlüssel) bestimmt.

Das Einlesen der Datei, Steuerung etc. habe ich alles bis jetzt machen können (bin noch nicht fertig). Mein Problem ist, dass ich alles in einer Klasse maze.java geschrieben habe, d.h. ich habe zwei Klassen maze und main. Das Spiel soll aber Objektorientiert programmiert werden. Die sechs Feldtypen sollen eigene Objekte sein, die von einer Oberklasse erben.

So ganz fehlt mir der Denkansatz hierfür.

Ich habe mir anfangs gedacht erstmal die Oberklasse Feld und dann die Klassen für die einzelnen Feldtypen zu erstellen. 


```
public class Feld {
  private int x, y, typ;
  public Feld(intx, int y, int typ){
      this.x=x;
      this.y=y;
  }
}

public class schluessel extends Projekt{
  public schluessel(int x, int y, int typ) {
            super(x, y, typ);
  }
  schluessel key = new Schluessel(x, y, 5);
 }
```

x und y stehen für die Koordinaten, und 5 für den Feldtyp. Die Koordinaten werden aus der properties gelesen. Nur weiß ich nicht wie meine Klasse, in der die Properties gelesen wird und das Labyrinth auf dem Terminal ausgegeben wird, auf die Objekte zugreifen soll.
Ich weiß auch gar nicht ob mein Ansatz in die richtige Richtung geht. Umso mehr würde ich mich in diesem Fall über paar Denkanstöße freuen 

Danke


----------



## coco07 (8. Jan 2015)

Hey Coindrop,
als aller erstes sollte man nach den Objekten im Spiel Ausschsu halten. Also was brauche ich, was ich im wirklichen Leben auch voneinander unterscheiden würde, in meinem Programm. Ich würde mir alle Objekte auf ein Blatt Papier schreiben und dann versuchen Beziehungen/gleiches Verhalten zwischen diesen Objekten zu finden. Wenn Objekt A ein Objekt B ist(ist ein - Beziehung) benutzt du Vererbung um die Übersichtlichkeit im Programm zu erhöhen, Polymorphie anzuwenden und Refundanz zu vermeiden. Ein einfaches Beispiel ist zum Beispiel: Ein Auto ist ein Fahrzeug, ein Fahrrad ist ein Fahrzeug. Damit hast du Beziehungen zwischen Autos mit Fahrzeugen und Fahrrädern mit Fahrzeugen hergestellt, welche beide einen "is a" Test bestehen. Nun kannst du z.B. polymorphe Parameter für Methoden in deinem Programm bereitstellen. Wenn du jedoch gleiches Verhalten zwischen 2 Objekten aber keine direkte Beziehung finden kannst, nutzt du Interfaces. Interfaces nutzt man auch um eventuellen Problemen mit Vererbungshierachien aus dem Wege zu gehen, weil Java lediglich die Einfachvererbung kennt. Beispiel dafür wäre zum Beispiel ein Auto und ein Mensch. Beide Objekte sind Verbraucher, haben aber dennoch nichts mit einander zu tun(abgesehen vom "hat ein" Prinzip). Wie bei Vererbung kannst du den Interface-Typ nun als Variable, Parameter in deinem Programm nutzen. Für genauere Beschreibungen schaue dir mal das Buch "Java von Kopf bis Fuß an". Nun kannst du deine Objekte ja in der Main Methode erzeugen und dem jeweils anderem Objekt eine Referenz auf ein Objekt geben um die interne Kommunikation zwischen einzelnen Objekten zu ermöglichen. In deinem Fall kannst du sicherlich eine abstrakte Klasse namens Feld erstellen und dann spezifischere Klassen mit z.B. weiteren Variablen und erweitertem Verhalten daraus ableiten. Ganz wichtig ist immer, nicht einfach irgendwo etwas zu vererben, nur weil du einige Variablen oder Methoden auch in einer anderen Klasse verwenden willst.In solchen Fällen liegt meistens eine Designschwäche vor. Sollte noch was unklar sein, melde dich nochmal 

Grüße coco07!


----------



## coindrop (8. Jan 2015)

Hi coco07,

Vielen Dank für die ausführliche Antwort!  Ich werde es mir heute abend nach der Arbeit so durchdenken, wie du eben beschrieben hast und evtl noch mal melden!


----------



## naeko (8. Jan 2015)

Wie kann man denn mit Lanterna Dateien einlesen? Oder machst du das mit Swing?


----------



## java_padawan (8. Jan 2015)

Hey, 
ich habe das selbe Problem. 
ich kann zwar eine Properties datei einlesen aber ich habe keine Ahnung wie das im Zusammenhang mit lanterna funktionieren soll. 
kann mir da jemand weiterhelfen ?


----------



## coindrop (8. Jan 2015)

Hi,

also lanterna ist für das terminal zuständig! Die Properties-files werden mit BufferedInputStream etc. gelesen (kann man sich googlen, wie properties eingelesen werden, wikipedia beispielsweise ).
In der Properties sind nur infos für mein Labyrinth drin, in welche Koordinate welche Feldtyp ist, z.B. sähe eine Zeile so aus

1, 2=5

Daraus liest der (von mir geschriebene) Code dass auf Position x=1, y=2 der Schlüssel(5) ist.

Lanterna gibt mir lediglich nur nur Befehle fürs Terminal z.B. terminal.putCharacter(char c) lässt an dort, wo der Cursor ist das char c ausgeben.


----------



## java_padawan (8. Jan 2015)

sry aber ich stehe sowas von aufm schlauch, verstehe es nicht


----------



## coindrop (8. Jan 2015)

In meiner pro perties sind halt nur position der Form "x, y=z". Mit properties.getProperty(x + "," +y) kann ich diese auswerten bzw. aufnehmen/markieren weiß nicht wie ich es ausdrücken soll.

Wenn ich jetzt schreibe

```
if(properties.getProperty(1 + "," + 2) == 5){ //evtl muss man jede Ziffer als String eingeben
 terminal.putCharacter('k');
}
```

prüft er nach ob 1, 2 gleich 5 sind, also ob es sich um den Schlüssel handelt und druckt dann an Position (1, 2) ein 'k' aus.

Der Problem bei mir hierbei ist, dass in meiner Methode direkt auf dem Terminal ausgedruckt wird. Es werden keine Objekte erstellt, welche auf dem Terminal abgebildet werden...


----------



## java_padawan (9. Jan 2015)

ah super, dann versuche ich es mal nach diesem schema  
danke dir coindrop 
sobald ich was zu deinem Problem gefunden habe schreibe ich dir !


----------



## coindrop (10. Jan 2015)

Hi Leute, wieder eine Problem:

Wie bereits erwähnt benutze ich das Lanternapackage und gebe das Spiel im Terminal aus. Ich habe ja eine Klasse, in der eine Schleife meine Spielfelder ins Termianl ausgibt.

```
terminal.moveCursor(x, y);
terminal.putCharacter(gegner.placeGegner()); //placeGegner() returnt das char 'R'
```

Ich möchte aber gerne nur eine Methode gegner.placeWand() in dieser Schleife schreiben, diese ist dann in der Klasse Gegner. Da es aber über Terminal läuft, muss ich, damit etwas dort ausgegeben wird, die Klasse Terminal importieren um die Methoden zu verwenden. Allerdings würde dies dann aber nicht auf mein Terminal-Objekt von meiner Klasse, in der die von mir oben geschriebene Schleife ist, zugreifen.

Das ist für mich vor allem wichtig, weil der Gegner sich permanent bewegen soll, während der Spieler steuert.
Daraufhin müsste dann beides parallel ablaufen, d.h. die Methode um den Spieler zu steuern und die, die den Gegner bewegen lässt sollen gleichzeitig laufen.

Ich bin für jeden Rat dankbar!

EDIT: Ich ****, muss ja alles nur übergeben werden! Aber über Ratschläge für letzteres Problem bin icih immer noch offen:lol:


----------



## naeko (12. Jan 2015)

In Laterna kannst du auf die KeyEvents reagieren. Also, könntest du sagen, solange das Spiel läuft, bewegen sich die Gegner und wenn bestimmte Tasten gedrückt, dann bewegt sich auch der Spieler:

```
boolean gameOver = false;
// solange Spiel läuft
while (!gameOver) {
            // lese Eingaben vom Terminal
            Key key = screen.readInput();
            // wenn Eingabe existiert, bewege meinen Spieler
            if (key != null) {
                movePlayer(key);// hier bewegst du deinen Spieler abhängig von gedrückter Taste, irgendwann ist Spiel zu Ende und gameOver = true
            }
                // bewege Gegner
                moveNpc(); // hier bewegst du deine Gegner. Evtl, baust du Thread.sleep drum rum, damit die Gegner nicht wild durch die Gegend laufen, sondern sich alle 500? Millisekunden bewegen
            }
            // aktualisiere Terminal-Screen
            if (screen.resizePending()) {
                screen.refresh();
            }
        }
```


----------



## coindrop (13. Jan 2015)

hi naeko,
erstmal vielen Danke für deine ausführliche Antwort. Leider hat es nicht so funktioniert wie du beschrieben hast, aber mittlerweile habe ich das Problem mit Threads gelöst.

Nun habe ich noch eine (hoffentlich letzte) Frage:
In meinem Labyrinth sind mehrere dynamische Gegner, die ich in einem Array gespeichert habe. Ich habe bisher meine Methode nur auf einen Gegner anwenden können, da ich eine bestimmte Position des Arrays benutze.

Dynamische Gegner haben den Wert 4, im Array sind alle Felder gespeichert. Wie kann ich von diesem Array alle Inhalte mit dem Wert 4 ausgeben/markieren, um sie dann in meiner GegnerMove() Methode anzuwenden?


----------



## naeko (13. Jan 2015)

Ich weiß nicht wie deine Liste aussieht aber du könntest doch über diese iterieren und alle Felder ausgeben, die den Wert 4 haben oder nicht?


----------



## coindrop (14. Jan 2015)

ich hatte da die befürchtung, dass die objekte sich nicht glecihzeitig bewegen und er nur bei einem objekt feststeckt, da die Methode GegnerMove() ne while schleife hat. zumindest wenn ich mit einer for schleife das array durchgehen, dann würde ja jedes objekt nacheinander bearbeitet.


----------



## naeko (14. Jan 2015)

ja, das stimmt. Deswegen ist die Konstruktion  der Schleife wichtig. Ich stelle mir das ungefähr so vor:

```
//globale Liste mit allen Feldtypen
List<FeldTyp> alleFeldTypen;

//hier holst du dir nur die dynamischen Gegner aus der Liste alleFeldTypen
List<DynamicEnemy> enemies = getDynamicEnemies(); 
for (DynamicEnemy enemy : enemies) { // hier iterierst du über alle dynamischen Gegner und bewegst diese
     gegnerMove(enemy);
}

void gegnerMove(DynamicEnemy enemy) {
//z.B bewege Gegner nach oben
int x_alt = enemy.getX();
int y_alt = enemy.getY();
int x_neu = x_alt;
int y_neu = y_alt-1;
...
}
```


----------



## java_padawan (16. Jan 2015)

Hi mal ne andere Frage, wie seit ihr an das Speichern herangegangen ? mir ist bewusst das ich dafür ein neues File erstellen muss, dazu nochmal eine neue Property und das ganze mit store gespeichert wird aber wie setze ich das ganze in die Tat um?  ???:L
eine Idee von mir war es einfach die ints der jeweiligen Position des Spieler, Schlüssels und der Hindernisse wieder in Strings umzuwandeln und das alles so zu speichern. 

geht das ganze auch noch einfacher und ist das überhaupt der richtige Ansatz ?  :bahnhof: 

lg


----------



## coindrop (16. Jan 2015)

Hallo,
vielen Dank für die Antwort, soweit hat alles geklappt bei mir jetzt, sogar das Menu habe ich fertig.

Meine save.properties wird genau so aufgebaut wie die level.properties. Die int werte werden beim speicihern in Strings umgewandelt und dann eiingespeichert. So habe ich es und es klappt soweit auch ganz gut.

Die letze Hürde ist folgende:
Das Terminal hat die Maße 100x30. Wenn ein große Level geladen wird, wird nur ein Abschnitt vom Level angezeigt. 
1. Wenn der Eingang nicht im außerhalb des bereichs bis 100x30 ist, muss logischerweise der Abschnitt vom Eingang angezeigt werden.
2. Wenn eben genanntes Problem gelöst ist, muss, wenn der Spieler am Rande des Terminals ist, beim nächsten Schritt der nächste Abschnitt gezeigt werden.

Leider habe ich bei beiden keinen Ansatz um dies zu lösen


----------



## naeko (17. Jan 2015)

Ist der Eingang und die initiale Spielerposition dasgleiche?
Ich würde so vorgehen:

1) Teil des Terminals bestimmen, das gezeichnet werden muss. Wenn mein Level Größe 300x30 hat, dann weiß ich das ich 3 Teile vom Terminal habe (jeweils 100x30). Wenn die Eingangskoordinate z.B. (182,22) ist, dann weiß ich, dass ich den 2.Teil des Levels zeichnen muss, die anderen zwei interessieren mich vorerst nicht.
2. Wenn ich den Spieler navigiere und genau den Grenzbereich zwischen Teilen des Terminals passiere, dann muss ich den anderen Teil zeichnen. Also, wenn mein Spieler Position (101,15) hat und geht nach links (100,15), dann muss ich den 1.Teil des Terminals zeichnen.
Gehe ich wieder nach link (101,15) zeichne ich wieder den 2.Teil des Terminals....das ganze funktioniert natürlich analog wenn ein Spieler nach unten/oben geht (und dort noch ein Teil des Terminals existiert).

Alle Kenngrößen hast du dazu ja: aktuelle Position des Spielers, maximale Breite/Höhe des Terminals, maximale Breite/Höhe des Levels.


----------



## coindrop (17. Jan 2015)

Erstmal vielen Dank für die Antwort.

Deine Ideen leuchten mir schon ein, nur habe ich keine grobe Vorstellung wie ich das zeichnen im code umsetzen soll. Also der Eingang ist die initiale Spielerposition. Bewegt sich im zb im zweiten Abschnitt der Spieler, ist dies ja außerhalb 100x30, aber sind allgemein abbildungen im Terminal nicht im Bereicht 0 - 100? Der zweite Punkt ist mir auch verständlich, aber wegen punkt 1 steh ich einfach total auf dem Schlauch, wie ich das umsetzen soll bzw wie der Algorithmus aussieht


----------



## naeko (17. Jan 2015)

Wenn du im Terminal zeichnest, machst du doch sowas ähnliches wie:

```
for (int y = 0; y < labyrinth.getHeight(); y++) {
   for (int x = 0; x < labyrinth.getWidth(); x++) {
	terminal.moveCursor(x, y);
	terminal.putCharacter('0');
   }
}
```
Jetzt möchtest du aber nicht das komplette Labyrinth zeichnen, sondern nur den "sichtbaren" Teil davon
Abschnitt 1 (horizontal):

```
for (int y = 0; y < 30; y++) {
   for (int x = 0; x < 100; x++) {
	terminal.moveCursor(x, y);
	terminal.putCharacter('0');
   }
}
```
Abschnitt 2(horizontal)

```
for (int y = 0; y < 30; y++) {
   for (int x = 100; x < 200; x++) {
	terminal.moveCursor(x, y);
	terminal.putCharacter('0');
   }
}
```
usw.
Das heißt, wenn du zu Beginn definierst, welcher Abschnitt des Labyrinth gezeichnet werden soll und
diesen dynamisch zeichnest, also abhängig von deiner Spielerposition, dann soll das eigentlich gewesein sein.


----------



## coindrop (18. Jan 2015)

Super nochmal Danke! Mir wird langsam klar wie das funktionieren soll. Aber der sichtbare Bereich bleibt ja immer 100x30? für die Abschnitte die direkt neben dem ersten sind könnte ich ganz einfach beim zeichnen 100 und 30 abziehen.  Aber bei größeren Labyrinthen von den Maßen z.B. 500x500 wird das schon etwas komplizierter oder?


----------



## coindrop (18. Jan 2015)

Ich glaub ich habe einen Ansatz. Aber mein Problem ist noch, den Abschnitt zu beginn auszuwählen bzw zeichnen zu lassen, in dem sich die Anfangsposition (Eingang) befindet.


----------



## naeko (18. Jan 2015)

Du hast die Größe des Labyrinth (z.B. 500x60)
Du hast die Größe des Terminals (z.B 100x30)
Du hast die Anfangsposition (z.B. 432/44)
Insgesamt hast du damit 5 Abschnitte horizontal (0 bis 100, 100 bis 200....400 bis 500) und 
2 Abschnitte vertikal (0 bis 30, 30 bis 60)

Also berechnest du zuerst den horizontalen Teilabschnitt:
432 / 100 = 4 (4. Abschnitt horizontal)->teilTerminal_width
danach den vertikalen Abschnitt:
44 / 30 = 1 (1. Abschnitt vertikal)_>teilTerminal_height

Wenn du Terminal zeichnest, sagst du dann 

```
for (int y = 30*teilTerminal_height; y < 30*teilTerminal_height+30; y++) {
   for (int x = 100*teilTerminal_width; x < 100*teilTerminal_width+100; x++) {
     // hier holst du dann aus deiner Property-Datei die Figuren für die Koordinate x/y
     char c = getFigureByCoordinate(x,y);
     // hier platzierst du deine Figur in den Terminal, da dieser aber immer 100x30 ist, würde eine Koordinate 432/44 im Terminal unter 32x14 platziert. Diese Berechnung erreichst du mit Modulo-Operation
     terminal.moveCursor(x % 100, y % 30);
     terminal.putCharacter(c);
   }
}
```
damit zeichnest du den Bereich 400 bis 500 (horizontal) und 30 bis 50 (vertikal) und genau dort ist deine Anfangsposition. Wenn du deinen Spieler in einen anderen Teilabschnitt des Labyrinths navigierst, musst du die Variablen 
teilTerminal_width und teilTerminal_height neu berechnen und den Terminal mit der o.g. Schleife wieder neuzeichnen


----------



## coindrop (19. Jan 2015)

Perfekt! Vielen vielen Danke! Ich bin jetzt fertig damit!

Kleine Frage: Kann man optional Musik/Sounds einbinden? Ich habe eine Mp3 Datei in Wav konvertiert, bisher habe ich es nicht geschafft. Ist es denn möglich?


----------



## naeko (19. Jan 2015)

Im Netz gibt es da genug Beispiele, wie man es macht. Einfach mal googeln, z.B. hier:
audio - How to play an mp3 file in java - Stack Overflow


----------



## WhiteSoul_ (11. Jan 2016)

Hallo ! ich hoffe einer kann mir helfen, ich habe dieses Jahr genau die selbe Aufgabe und ich bin jetzt soweit, dass im Terminal ein Level ausgedruckt wird, aber die readinput()-methode funktioniert nicht, und ich glaube das liegt daran, dass ich die Klassen hätte anders aufteilen sollen,oder so ähnlich... Deswegen erstmal die frage, in welcher klasse habt ihr das terminal erstellt und wo befindet sich bei euch dann die readinput()-schleife? und was steht dann in der Main-class?


----------



## VfL_Freak (12. Jan 2016)

Moin,



WhiteSoul_ hat gesagt.:


> Hallo ! ich hoffe einer kann mir helfen, ich habe dieses Jahr genau die selbe Aufgabe und ich bin jetzt soweit, dass im Terminal ein Level ausgedruckt wird, aber die readinput()-methode funktioniert nicht, und ich glaube das liegt daran, dass ich die Klassen hätte anders aufteilen sollen,oder so ähnlich... Deswegen erstmal die frage, in welcher klasse habt ihr das terminal erstellt und wo befindet sich bei euch dann die readinput()-schleife? und was steht dann in der Main-class?


Mit wem sprichst du ??? 

http://www.java-forum.org/forum-faq-beitraege/7407-man-fragen-richtig-stellt.html

Gruß Klaus


----------

