Wie kann Java auf ein User-Input warten?
Guten Tag,
ich programmiere gerade ein kleines Java-Projekt.
Dort hat bis jetzt auch alles geklappt, doch nun weiß ich nicht mehr weiter.
Ich habe für den Start meines Programms dieses GUI erzeugt:
Wie da ja schon steht, soll der User 'W' drücken, damit das Spiel anfängt. Doch ich weiß nicht, wie ich das programmieren kann. Ich habe schon eine Klasse (KeyHandler) angelegt, die KeyListener implementiert und die ich schon meinem Programm hinzugefügt habe, doch passieren tut dabei nichts.
Ich habe auch schon eine Ahnung warum: Die Variable 'commands.isRunning' wird direkt überprüft (und danach nicht mehr) und deshalb der If-Teil überschprungen. Wie kann jetzt Java auf einen User-Input warten, damit die Variable nicht sofort überprüft wird?
Hier ist meine Draw Klasse:
(commands.isRunning ist gerade noch false, soll aber durch das 'W' true werden. Die Methode, die dadurch aufgerufen werden soll, steht in der Klasse Commands)
public class Draw extends JPanel implements Variables {
Commands commands = new Commands();
KeyHandler keyHandler = new KeyHandler();
Player player = new Player();
Donut donut = new Donut();
Draw() {
this.addKeyListener(keyHandler);
this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
this.setBackground(Color.black);
this.setFocusable(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
commands.printStartText(g);
if (commands.isRunning() == true) {
// Draw Grid
for (int i = 0; i < SCREEN_HEIGHT / UNIT_SIZE; i++) {
g.drawLine(i * UNIT_SIZE, 0, i * UNIT_SIZE, SCREEN_HEIGHT);
g.drawLine(0, i * UNIT_SIZE, SCREEN_HEIGHT, i * UNIT_SIZE);
}
// Place Player
player.placePlayer(g);
// Place Donut
donut.placeDonut(g);
// Place Barrier
// Place Gadget
// Set Default Color
g.setColor(Color.darkGray);
}
}
}
Und hier die KeyHandler Klasse:
public class KeyHandler implements KeyListener {
Commands commands = new Commands();
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_W:
commands.startGame();
break;
case KeyEvent.VK_A:
break;
case KeyEvent.VK_S:
break;
case KeyEvent.VK_D:
break;
}
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Auch habe ich schon 'System.in.read' hinter 'commands.printStartText(g);' platziert, doch da wurde dann nur ein weißes Bild angezeigt.
Ich hoffe ihr könnt mir helfen!
Schon mal vielen Dank im voraus.
1 Antwort
1) Was soll das Interface Variables beinhalten? Es klingt für mich aufgrund des Namens schon verdächtig nach Missdesign.
2) Das Commands-Objekt, welches du in Draw kreierst, würde ich direkt an den KeyHandler weitergeben. So sparst du dir den Speicherplatz für ein zweites und kannst Zustände teilen.
3) Das Panel wird nur einmal gezeichnet. Du musst Swing einen Hinweis geben, dass die Komponente neu gezeichnet werden sollte (daraufhin reiht Swing diese Aktion in seine Queue, um sie bei nächster Gelegenheit abzuarbeiten). Eine tatsächliche Game Loop musst du dir erst selbst bauen (nicht umsonst habe ich dir bei deinen vorherigen Fragen Tools wie libGDX empfohlen).
Die nächstbeste Option, eine Game Loop in Swing zu bauen, wäre der Einsatz eines Swing Timers. Ein Beispiel, auf dem sich aufbauen lässt, findest du hier.
4) System.in stellt den Standardeingabekanal für Konsolenprogramme dar. Die GUI hingegen kommuniziert nicht darüber. Mit deinem Aufruf hast du einfach nur den GUI-Thread blockiert.
- Naja, du missbrauchst das Interface gerade als Endlager. Die Felder sollten der Klasse zugeordnet werden, der sie logisch zugehörig sind. Da deine Klasse Draw offensichtlich das Spielfeld darstellt, sind Felder für die Spielfeldgröße in ihr auch gut aufgehoben. Da sie den Objektzustand beschreiben, wären es m.E. objektgebundene Felder. Wenn andere Objekte deren Werte brauchen, müssen ihnen diese Werte übergeben werden oder sie benötigen eine Referenz auf das Spielfeld. Für Wertübergaben gibt es Parameter und return.
- Als Parameter über den Konstruktor. Die beiden Commands-Objekte haben jeweils einen eigenen Zustand. Wenn sich bei einem von beiden eine Eigenschaft running im Wert ändert, hat das keinen Einfluss auf das andere Objekt. Da du aber in zwei Klassen denselben Zustand erwartest, wäre es sinnig, auch nur ein Objekt zu verwenden.
- Und wenn du dich einmal der Webentwicklung zuwenden möchest, versuchst du das erst mit der Android API? 😉
- Ich habe doch ein komplettes Beispiel verlinkt?
1. In dem Interface habe ich Variablen definiert, die ich in mehreren Klassen benötige. Dort ist zum Beispiel auch UNIT_SIZE definiert.
Was wäre ein besserer Weg?
2. Wie würde ich das Objekt dann an KeyHandler weitergeben? Und was ist mit "Zustände teilen" gemeint?
3. Ich wollte mich erst mal weiter in JavaSwing und dann JavaFX einarbeiten, bevor ich mich dann z.B. libGDX oder jMonkeyEngine zuwende.
4. Um jetzt die GUI neu zu zeichnen, muss ich also ein Objekt von ActionListener erstellen und dann von da die repaint() Methode aufrufen?