KeyPressed

Anto

Aktives Mitglied
Ich habe ein Spiel, bei dem sich 2 Spielfiguren auf einem Spielfeld bewegen sollen.

Die Bewegung funktioniert über KeyListener, ich mache das momentan über KeyPressed.

Problem dabei: Ich kann immer nur eine Figur gleichzeitig bewegen, wenn ich 2 Tasten gleichzeitig drücke, blockieren sich die Prozesse scheinbar gegenseitig.

Wie programmiert man da drumherum. bzw. nach was google ich am sinnvollsten?

Danke und Grüße

Anto
 

Anto

Aktives Mitglied
Dass das "irgendwie" was mit Threading und/oder Timern zu tun haben könnte habe ich auch mitbekommen....mehr aber nicht.
 

Gossi

Bekanntes Mitglied
Was sollen die Threads denn machen?

Prüfen ob sich etwas bewegt, du kannst also eine Klasse erstellen, die den KeyListener benutzt, diesen kannst du dann deiner Spielfigur zuweisen und aus dieser Klasse halt änderungen an der Position abfragen.... nur so als Idee :)
 

Anto

Aktives Mitglied
Der KeyListener hängt am Spielbrett, weil da auch noch andere Sachen passieren. Vor allem. was würde das daran ändern, dass die Figuren sich nicht gleochzeitig bewegen können?

Ich weiß bis jetzt ja nicht einmal genau, warum das so ist, nur dass das unter Java scheinbar "normal" ist und es irgendeinen Trick gibt, da drumrumzukommen, der wohl "Standard" ist, nur den Trick habe ich noch nicht gefunden.
 
V

vanny

Gast
Ich kann immer nur eine Figur gleichzeitig bewegen...

Anto

den find ich irgendwie gut:D

Ok, zum Thema:

Schau dir mal an, wie das mit dem Zeichnen von Komponenten und das Eventhandling im Bezug auf Threads in Java abläuft.
Dann denke ich, dass dein Problem mit dem single-key recht schnell gelöst werden kann.

Ohne Code lässt sich da von meiner Seite aus leider nicht mehr zu sagen.

Gruß Vanny
 

Gossi

Bekanntes Mitglied
Du machst dans ganze aber so, oder?

Java:
	@Override
	public void keyPressed(final KeyEvent e) {

		if (e.getKeyCode() == KeyEvent.VK_W) {
			up = true;
		}
		if (e.getKeyCode() == KeyEvent.VK_S) {
			down = true;
		}
		if (e.getKeyCode() == KeyEvent.VK_A) {
			left = true;
		}
		if (e.getKeyCode() == KeyEvent.VK_D) {
			right = true;
		}
		if (e.getKeyCode() == KeyEvent.VK_UP) {
			up = true;
		}
		if (e.getKeyCode() == KeyEvent.VK_DOWN) {
			down = true;
		}
		if (e.getKeyCode() == KeyEvent.VK_LEFT) {
			left = true;
		}
		if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = true;
		}

	}

	@Override
	public void keyReleased(final KeyEvent e) {

		if (e.getKeyCode() == KeyEvent.VK_W) {
			up = false;
		}
		if (e.getKeyCode() == KeyEvent.VK_S) {
			down = false;
		}
		if (e.getKeyCode() == KeyEvent.VK_A) {
			left = false;
		}
		if (e.getKeyCode() == KeyEvent.VK_D) {
			right = false;
		}
		if (e.getKeyCode() == KeyEvent.VK_UP) {
			up = false;
		}
		if (e.getKeyCode() == KeyEvent.VK_DOWN) {
			down = false;
		}
		if (e.getKeyCode() == KeyEvent.VK_LEFT) {
			left = false;
		}
		if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = false;
		}
	}
 

Anto

Aktives Mitglied
So ähnlich , ja. mehr in der richtung if keycode = x then figur1.up();

analog im zweifel eben figur2.up();

Wieso fragst Du?
 

Gossi

Bekanntes Mitglied
So ähnlich , ja. mehr in der richtung if keycode = x then figur1.up();

analog im zweifel eben figur2.up();

Wieso fragst Du?

Ich kann mit diesem Quellcode meine Figur auch schräg laufen lassen, wie sieht das bei dir aus?
Dann hast du nähmlich das Problem, dass du allgemein nur 1 Tastendruck verarbeitest

Edith, dazu dann halt sowas:
Java:
	private void checkKeys() {

		if (up) {
			copter.setVerticalSpeed(-vertSpeed);
		}
		if (down) {
			copter.setVerticalSpeed(vertSpeed);
		}
		if (left) {
			copter.setHorizontalSpeed(-speed);
		}
		if (right) {
			copter.setHorizontalSpeed(speed);
		}

		if (!up && !down) {
			copter.setVerticalSpeed(0);
		}
		if (!left && !right) {
			copter.setHorizontalSpeed(0);
		}

	}
 

Anto

Aktives Mitglied
grundsätzlich KÖNNTE meine Figur damit schräglaufen, wird aber abgefangen (switch case).

Den Verdacht, dass ich nur einen Tastendruck verarbeite, hatte ich auch schon, ich weiß nur nicht, wie ich da drumherumkomme?
 

Anto

Aktives Mitglied
Ich seh das wohl, dass Du Dir da booleans für die Richtungen setzt.

Mal davon abgesehen dass die entsprechende Funktion ja dann immer noch aufgerufen werden müsste: Wieso sollte das einen Unterschied machen?

Ich stehe da im Moment etwas auf dem Schlauch was das helfen sollte 4 Booleans für 8 mögliche Tasten zu haben?

Dann steht eben in der Methode up: if up==true {führe den rest der Methode aus} else return;
würde das irgendetwas ändern?
 

Michael...

Top Contributor
1. Ich würde hier KeyBindings anstatt eines KeyListeners verwenden
2. Wird eine Game Loop benötigt die in einem separaten Thread läuft
3. Dürfen mit dem Tastendruck nur Flags/Werte gesetzt werden Figur1 nach rechts, Figur2 nach oben, Figur1 Geschwindkeit erhöhen... Der Tastendruck führt nicht direkt die Bewegung/Aktion aus
4. die Bewegung/ das Verhalten wird ausschließlich in der Game Loop anhand der aktuellen Zustände der Flags bzw. der aktuellen Werte ausgeführt
 

Gossi

Bekanntes Mitglied
Ich stehe da im Moment etwas auf dem Schlauch was das helfen sollte 4 Booleans für 8 mögliche Tasten zu haben?

Ähm, weil ich nur eine Spielfigur habe, die sowohl über WASD alsauch über die Pfeiltasten gesteuert werden kann.

Wenn du 2 Spieler hast musste halt [c]boolean rechtsSpielerEins[/c] oder sowas nehmen...

Mal davon abgesehen dass die entsprechende Funktion ja dann immer noch aufgerufen werden müsste: Wieso sollte das einen Unterschied machen?

Da du bei jedem Tastendruck EINMALIG den Listener aufrufst, somit kannste also eine Variable auf True setzen, sobald du aber ne zweite Taste drückst, wird die Aktion für die erste gekillt.

Hast du allerdings nen boolean was bei released wieder auf false gesetzt wird, kannste in dem GameLoop (den du ja hast, hoffe ich) auch deine Movemethode aufrufen...
 
Zuletzt bearbeitet:

Anto

Aktives Mitglied
1. ok, system begriffen, ich denke das sollte auch funktionieren so wie Ihr das sagt, danke.

2. Was genau ist ein GameLoop?

Kann gut sein, dass ich das benutze, aber ich höre das Wort hier zum ersten Mal.

Ach Moment. Ist der Gedanke, dass ich irgendwo eine dauerlaufende Methode haben muss die durchgängig prüft wie die flags aussehen?

Da brauche ich ja doch wieder Threads?
 

Anto

Aktives Mitglied
Hm Theoretisch würde das aber doch völlig reichen wenn beim Spielstart eine Methode angeworfen wird die in etwa folgendes tut:

While(1==1){if flag1 = true 1.up();
if flag2 = true 1.down
.
.
.

}

wozu brauche ich dafür nun zwingend Threads?

+, mir fällt gerade auf, das zerschießt mir doch das gesamte restliche Programm:

Ich will ja gerade nicht, dass die Figur diagonal laufen kann, das könnte sie aber doch mit dieser Implementation zwingend, eben weil man ja beispielsweise die Taste für runter und die Taste für Rechts zeitgleich drücken könnte?
 
Zuletzt bearbeitet:

Michael...

Top Contributor
Hm Theoretisch würde das aber doch völlig reichen wenn beim Spielstart eine Methode angeworfen wird die in etwa folgendes tut:

While(1==1){if flag1 = true 1.up();
if flag2 = true 1.down
.
.
.

}

wozu brauche ich dafür nun zwingend Threads?
Weil diese Schleife ja nie verlassen wird. Wie willst Du denn die Flags ändern, wenn nicht aus anderen Threads heraus?
Hintergrund: Wenn diese Schleife innerhalb des EDT läuft reagieren auch keine Listener.
 

Michael...

Top Contributor
Ich will ja gerade nicht, dass die Figur diagonal laufen kann, das könnte sie aber doch mit dieser Implementation zwingend, eben weil man ja beispielsweise die Taste für runter und die Taste für Rechts zeitgleich drücken könnte?
Andererseits willst Du aber auch, dass Spieler1 und Spieler2 gleichzeitig Tasten drücken können.
Das liegt dann an Deiner implementierung ob Du das zulässt oder nicht.
Man kann es ja so implementieren, dass solange Spieler1 die Rechtstaste gedrückt hält (also flagRechts==true) oder grundsätzlich spezielle Tasten gedrückt hält andere Tastendrücke durch den Spieler1 ignoriert werden.
 

Gossi

Bekanntes Mitglied
Hm Theoretisch würde das aber doch völlig reichen wenn beim Spielstart eine Methode angeworfen wird die in etwa folgendes tut:

While(1==1){if flag1 = true 1.up();
if flag2 = true 1.down
.
.
.

}

2 Sachen:
1. Du musst prüfen obe dein frame.isVisible()

2. Die Schleife läuft zwar endlos, aber der Code danach wird nicht ausgefürht


wozu brauche ich dafür nun zwingend Threads?

Da ein Thread unabhängig von deinem Programm weiterläuft und seine Aufgaben ausführt, ohne die GUI zu blockieren...

+, mir fällt gerade auf, das zerschießt mir doch das gesamte restliche Programm:

Dein Beispiel ja, ein Thread der extra für den GameLoop gestartet wird nicht.

Ich will ja gerade nicht, dass die Figur diagonal laufen kann, das könnte sie aber doch mit dieser Implementation zwingend, eben weil man ja beispielsweise die Taste für runter und die Taste für Rechts zeitgleich drücken könnte?


Dann kannst du auch ne prüfung einbauen:
Java:
   @Override
    public void keyPressed(final KeyEvent e) {
 
        if (e.getKeyCode() == KeyEvent.VK_W) {
            up = true;
            down = false;
            left = false;
            rigth = false;

            //usw
 
    }
 

Anto

Aktives Mitglied
Warum legt die Methode die Listener lahm?

Ich weiß überhaupt noch nicht wirklich was ein Thread eigentlich ist und wollte EIGENTLICH nicht das ganze Spiel komplett umschreiben......von daher suche ich gerade verzweifelt eine Methode ohne Threads.
 

Gossi

Bekanntes Mitglied
Warum legt die Methode die Listener lahm?

Ich weiß überhaupt noch nicht wirklich was ein Thread eigentlich ist und wollte EIGENTLICH nicht das ganze Spiel komplett umschreiben......von daher suche ich gerade verzweifelt eine Methode ohne Threads.

Eine Möglichkeit ohne Threads is wie Autofahren ohne Lenkrad (die GUI wird blockiert) und Reifen (Programm läuft nicht wirklich gut), es geht, is aber grade in den Kurven (parallele Berechnungen) keine Schöne Geschichte...
 

Michael...

Top Contributor
Ich weiß überhaupt noch nicht wirklich was ein Thread eigentlich ist und wollte EIGENTLICH nicht das ganze Spiel komplett umschreiben......von daher suche ich gerade verzweifelt eine Methode ohne Threads.
Das ist schlecht. Für ein Arcade Games sind mindestens zwei Threads notwendig.

Das was Du vorhast geht nicht ohne Threads.
 

Anto

Aktives Mitglied
Es wurde aber gesagt die Bewegung ginge auch ohne?

Für den Rest des Spiels werde ich dann wohl nicht drumherumkommen aber die Bewegung soll angeblich ganz simpel ohne Threads gehn.

Zumal ich das ohnehin nicht wie im Tutorial machen kann, egal wo ein Thread implementiert wird, sicher nicht in der GUI: Die implementiert bereits Observer und wir dürfen nur ein Interface pro Klasse implementieren.
 

Anto

Aktives Mitglied
zu deutsch ich kann wahrscheinlich nichts von meinem schon funktionierenden Code erhalten?

Dazu kommt: der Controller implementiert die Listener (das muss er auch, vorgegeben), damit DARF er aber nicht mehr runnable implementieren weil die Vorgabe ist: Maximal ein Interface pro Klasse.

ist die Aufgabe damit unlösbar?
 

Michael...

Top Contributor
zu deutsch ich kann wahrscheinlich nichts von meinem schon funktionierenden Code erhalten?
Den kenne zwar ich nicht, aber warum nicht? Eventuell muss nur die Spielelogik angepasst werden.
Dazu kommt: der Controller implementiert die Listener (das muss er auch, vorgegeben), damit DARF er aber nicht mehr runnable implementieren weil die Vorgabe ist: Maximal ein Interface pro Klasse.

ist die Aufgabe damit unlösbar?
Hab ja nicht gesagt, dass der Controller selbst das Interface implementieren muss. Er kann ja ein Objekt einer Klasse (welche Runnable implementiert) erzeugen und dieses einem Thread übergeben.
Willkommen in der objektorientierten Welt ;-)
 

Anto

Aktives Mitglied
Ich weiß, was ich an OOP nicht mag.

Was Du meinst ist aber nicht möglich nach Vorgaben: die GUI kann Runnable nicht implementieren weil die schon Observer implementiert. Die Gui ist aber alles, was der Controller erzeugen so.

Das Model wird einfach ganz regulär in der Main erzeugt und dann dem Controller übergeben.

Das Model ist aber das einzige was Runnable noch implementieren könnte, denn der Controller selbst implementiert ja den Listener.

Wie soll nun also noch, wie Du ja sagst, eine Klasse Runnable implementieren von der der Controller ein Objekt erzeugt?
 

Michael...

Top Contributor
Ein bisschen geistig Flexibilität sollte man beim Programmieren mitbringen.

Grundsätzlich sollte in der main möglichst wenig erzeugt werden. Am besten steht da nur eine Zeile drin, die das Zentrale Objekt (hier den Controller) erzeugt.

Unabhängig davon, kannst Du doch sicherlich in Deiner mein ein Runnable Objekt erzeugen, diesem das dort erzeugte Model übergeben und in einem neuen Thread starten.
 

Anto

Aktives Mitglied
Bei mir stehen da 3 Zeilen, also auch nicht viel mehr.

Außerdem muss ja offiziell das model dem Controller übergeben werden wie soll ich das dann noch dem Runnable übergeben?
Ausserdem: was wäre denn dann die Klasse zu der das Runnable Objekt gehört? wäre die in Gui, Model oder Controller?
 

Michael...

Top Contributor
Außerdem muss ja offiziell das model dem Controller übergeben werden wie soll ich das dann noch dem Runnable übergeben?
Was wäre das für eine Welt in der nur ein Objekt Referenzen auf ein anderes Objekt halten könnte/dürfte? Du übergibst doch auch das Model an Deine View?

Keine Ahnung wie es bei Dir ausschaut, aber vom Prinzip her könnte es so ausschauen:
Java:
	public static void main(String args[]) throws IOException {
		Model model = new Model();
		Controller controller = new Controller(model);
		GameLoop gameLoop = new GameLoop(model);
		new Thread(gameLoop).start();
	}
Ausserdem: was wäre denn dann die Klasse zu der das Runnable Objekt gehört? wäre die in Gui, Model oder Controller?
Ich würde das Runnable im Controller Bereich verorten, wobei es natürlich auch unabhängig von den dreien existieren könnte. Das einzige was notwendig ist, ist der Zugriff auf das Model
 

Anto

Aktives Mitglied
Nein, die View bekommt das Model nicht, nur der Controller. Die View darf das Model nicht haben laut Veranstalter, das geht alles über Observer Pattern. Prinzipiell erscheint mir das auch ganz sinnvoll (habe gestern zum ersten Mal begriffen, dass das Übergeben von Objekten ja auch nichts anderes ist als Zeiger in C),

allerdings verstehe ich folgende Schreibweise nicht:

new Thread(gameLoop).start();


Was tut das? einen Thread (was auch immer das genau ist) auf dem Objekt GameLoop (von dem ich auch nur raten kann wofür es gut ist) erzeugen und dann WORAUF start aufrufen?
 

Anto

Aktives Mitglied
Ganz banale Frage: Was IST ein Thread?

In der Literatur, die ich dazu gefunden habe, steht immer nur, wie man einen solchen implementiert (runnable usw.) und mehr oder weniger auch dass man das quasi immer braucht und dann auf einmal alles viel schneller geht aber nirgends wird beschrieben was das eigentlich ist!
 

Michael...

Top Contributor
Nein, die View bekommt das Model nicht, nur der Controller. Die View darf das Model nicht haben laut Veranstalter, das geht alles über Observer Pattern.
Dachte Ihr verwendet das MVC Pattern? zumindest sollte der View ein Model-Interface bekannt sein.
(habe gestern zum ersten Mal begriffen, dass das Übergeben von Objekten ja auch nichts anderes ist als Zeiger in C)
Nicht ganz, aber so ähnlich
new Thread(gameLoop).start();

Was tut das? einen Thread (was auch immer das genau ist) auf dem Objekt GameLoop (von dem ich auch nur raten kann wofür es gut ist) erzeugen und dann WORAUF start aufrufen?
Hier wird ein neues Thread Objekt erzeugt und anschließend gestartet (ist die Vorgehensweise bei Threads) Beim übergebenen GameLoop Objekt ist davon auszugehen, dass es Runnable implementiert. Hier wird der Spieleablauf durchgetaktet.
 

Michael...

Top Contributor

Anto

Aktives Mitglied
Theoretisch gibt es da noch ein Interface dazwischen, ja, wird aber ausdrücklich nicht gefordert. Die Idee ist: die View holt sich über die Methode update die Modelwerte mittels Gettern. Ist Ausdrücklich so erlaubt.

Java ist eine Insel ist so eine Sache....die schmeißen einem immer 3 Brocken hin, die nicht mehr funktionieren, sobald man irgendetwas abändert.

Sehe ich das jetzt richtig, dass jeder Spieler einen eigenen Thread braucht?

Wie kann das sein, da ich ja nur einen Listener habe? und der quasi immer laufen soll?

Ich habe schon begriffen, dass ich da keine echte Gleichzeitigkeit erzeuge sondern das nur für den Nutzer hinterher so aussieht, als hätte er die.

Aber alles, was man googelt, wirft einem nur hin: "Ja arbeite mit Threads, implementiere halt Runnable" aber nirgends scheint eine Anleitung zu finden zu sein, warum denn nun gerade Threads dafür sorgen können (und vor allem wie) dass zwei Sachen gleichzeitig geschehen, die das vorher nicht taten.
 

Gossi

Bekanntes Mitglied
Stell dir vor du musst 10 Matheaufgaben Lösen und gleichzeitig noch darauf hören was dein Lehrer sagt.

Wenn du das alleine machst, musst du jedesmal eine Pause in den Matheaufgaben machen, wenn dein Lehrer etwas sagt.

Nimmst du dir jetzt allerdings eine zweite Person dazu (einen Thread) und du fragst ihn nach den Matheaufgaben, was der Lehrer in der Zeit gesagt hat und er sagt dir das, bist du schneller und kannst in ruhe deine Aufgaben machen.

So läuft das bei Threads auch, jeder kümmert sich um seine Aufgabe und zwischendrin, wenn zeit ist, wird ein abgleich der Daten vorgenommen.
 

Michael...

Top Contributor
Theoretisch gibt es da noch ein Interface dazwischen, ja, wird aber ausdrücklich nicht gefordert. Die Idee ist: die View holt sich über die Methode update die Modelwerte mittels Gettern. Ist Ausdrücklich so erlaubt.
Woher kommen denn die getter? Dazu muss die View ja doch die Klasse/Interface bzw. das Model kennen.
Sehe ich das jetzt richtig, dass jeder Spieler einen eigenen Thread braucht?
Nein, es wird ein Thread benötigt, der sich um die GUI kümmert und User Events (in dem Fall die Tastendrücke) entgegen nimmt (Nennt sich EventDispatcherThread). In einem zweiten Thread werden evtl. durch die Eingaben angestossene Änderungen verarbeitet, Modelle, Werte, Daten und sonst noch was manipuliert. Am Ende der Manilpulation muss wie gewohnt die View darüber informiert, damit im EDT die GUI aktualisiert werden kann.
Wie kann das sein, da ich ja nur einen Listener habe? und der quasi immer laufen soll?
?
Aber alles, was man googelt, wirft einem nur hin: "Ja arbeite mit Threads, implementiere halt Runnable" aber nirgends scheint eine Anleitung zu finden zu sein, warum denn nun gerade Threads dafür sorgen können (und vor allem wie) dass zwei Sachen gleichzeitig geschehen, die das vorher nicht taten.
Dafür sind Threads da. Sie werden durch die JVM nativ implementiert und mit start() erzeugt die JVM einen "parallel" laufenden Prozess, was in Deinem Fall notwendig ist, da sich Figuren über ein Spielfeld bewegen sollen und Gleichzeitig auf Benutzereingaben reagiert werden soll.
 

Anto

Aktives Mitglied
Aber.....die eigentliche Aufgabe ist doch "gleiche daten ab und setze danach werte neu", wie sollte man das noch aufteilen?

Zum Verständnis: Ich habe auch erst begriffen was man bei OOP tut, als mir irgendwann jemand gesagt hat dass ein Objekt im Grunde ein Set von Werten ist - mit diesen ganzen Vergleichen von Objekten mit Autorädern etc. war ich immer völlig überfordert.

Für mich ist Programmierung nicht die reale Welt.

Das ist Mathe, weiter nichts!
 

Anto

Aktives Mitglied
Michael: nein, muss sie nicht: Die View castet sich einfach das Observable (was update ja so und so hat) auf ein Model.

Warum soll der Thread der die Tastendrücke entgegen nimmt sich um die GUI kümmern? Der Listener für die Tastendrücke steht doch gar nicht in der GUI?
 
Zuletzt bearbeitet:

Michael...

Top Contributor
Michael: nein, muss sie nicht: Die View castet sich einfach das Observable (was update ja so und so hat) auf ein Model.
Zum Verständnis: Sprichst Du hier von java.util.Observable oder von einem selbst definierten Interface?
Warum soll der Thread der die Tastendrücke entgegen nimmt sich um die GUI kümmern? Der Listener für die Tastendrücke steht doch gar nicht in der GUI?
Ein Thread ist ein Handlungsstrang, darin werden "Anweisungen" platziert unabhängig von irgendwelchen Klassen bzw. wo und wie etwas implementiert ist. Diese Anweisungen werden nacheinander vom Thread abgearbeitet, daher kann ein Thread nicht gleichzeitig zwei Sachen machen.
Anto hat gesagt.:
Für mich ist Programmierung nicht die reale Welt.
Vielleicht erläuterst Du etwas genauer, um was es geht. Bis jetzt weiß ich nur das zwei Spielfiguren gleichzeitig über ein Spielfeld bewegt werden sollen. Was ist das für eine Art von Spiel.
Eventuell kann man, das ganze dann an realen Beispielen besser erläutern.
 

Anto

Aktives Mitglied
zu 1: Ersteres.

zu 2: Na aber das soll er doch? Wenn Bewegung immer dann passiert wenn eine Taste gedrückt wird (KeyPressed) dann müssen doch, wenn 2 tasten gleichzeitig gedrückt werden (für 2 Spielfiguren) beide Aktionen gleichzeitig ausgeführt werden? nur dafür will ich die Threads doch überhaupt haben?

zu 3: Viel mehr soll aber im Grunde nicht passieren: Stell Dir Tron (oder Snake, meinetwegen) vor, nur ohne dass die Schlangen wachsen würden.

Einfach nur ein mehr oder weniger leeres Feld, auf dem sich zwei Figuren bewegen, mehr nicht!
 

Michael...

Top Contributor
und woher kommen dann die getter. Hab ich das richtig verstanden: Ihr castet das Observable auf ein Object vom Typ Model?
zu 2: Na aber das soll er doch? Wenn Bewegung immer dann passiert wenn eine Taste gedrückt wird (KeyPressed) dann müssen doch, wenn 2 tasten gleichzeitig gedrückt werden (für 2 Spielfiguren) beide Aktionen gleichzeitig ausgeführt werden? nur dafür will ich die Threads doch überhaupt haben?
Genau deswegen Threads, damit z.B. ein Thread auf die Benutzereingaben reagieren kann, während der andere die Objekte manipuliert (z.B. bewegt)
Ist das echt so schwierig und unlösbar?
Nein, man muss sich nur mit Threads und Multithreading auseinandersetzen.

Um mal auf einen grünen Zweig zu kommen, hier ein Beispiel. Die Figur kann mit den Tasten A,W,D gesteuert werden. Genauso wie sie während der Vorwärtsbewegung auf die Rechts und Links Kommandos reagiert, könnte man weitere Tasten abfragen, um einen zweiten Spieler zu steuern.
Java:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.KeyStroke;

public class GameDemo extends JFrame {
	
	private Player player;
	
	public GameDemo() {
		player = new Player(200, 200);
		GamePanel panel = new GamePanel();
		add(panel);
		new Thread(new GameLoop(panel)).start();
	}
	
	private boolean move, right, left;
	
	class GamePanel extends JComponent {
		
		public GamePanel() {
			InputMap map = this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
			map.put(KeyStroke.getKeyStroke("W"), "go");
			map.put(KeyStroke.getKeyStroke("released W"), "stop");
			getActionMap().put("go", new AbstractAction() {
				public void actionPerformed(ActionEvent evt) {
					move = true;
				}
			});
			getActionMap().put("stop", new AbstractAction() {
				public void actionPerformed(ActionEvent evt) {
					move = false;
				}
			});
			map.put(KeyStroke.getKeyStroke("D"), "right");
			map.put(KeyStroke.getKeyStroke("released D"), "stopRight");
			getActionMap().put("right", new AbstractAction() {
				public void actionPerformed(ActionEvent evt) {
					right = true;
					left = false;
				}
			});
			getActionMap().put("stopRight", new AbstractAction() {
				public void actionPerformed(ActionEvent evt) {
					right = false;
				}
			});
			map.put(KeyStroke.getKeyStroke("A"), "left");
			map.put(KeyStroke.getKeyStroke("released A"), "stopLeft");
			getActionMap().put("left", new AbstractAction() {
				public void actionPerformed(ActionEvent evt) {
					left = true;
					right = false;
				}
			});
			getActionMap().put("stopLeft", new AbstractAction() {
				public void actionPerformed(ActionEvent evt) {
					left = false;
				}
			});
		}
		
		public void paintComponent(Graphics g) {
			super.paintComponent(g);
			Graphics2D g2 = (Graphics2D)g.create();
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			g2.translate(player.getX(), player.getY());
			g2.fillOval(-10, -10, 20, 20);
			g2.rotate(Math.toRadians(player.dir));
			g2.translate(5, 0);
			g2.fillRect(-5, -5, 15, 10);
			g2.dispose();
		}
	}
	
	class GameLoop implements Runnable {
		private JComponent comp;
		
		public GameLoop(JComponent comp) {
			this.comp = comp;
		}
		
		public void run() {
			while(true) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				if (right)
					player.turnRight();
				else if(left)
					player.turnLeft();
				if (move)
					player.move(10);
				comp.repaint();
			}
		}
	}
	
	class Player {
		private Point pos;
		private int dir;
		
		public Player(int x, int y) {
			pos = new Point(x, y);
			dir = 0;
		}
		
		public void move(int steps) {
			pos.x += Math.cos(Math.toRadians(dir)) *steps;
			pos.y += Math.sin(Math.toRadians(dir)) *steps;
		}
		
		public void turnRight() {
			dir = (dir+10)%360;
		}
		
		public void turnLeft() {
			dir = (dir-10)%360;
		}
		
		public int getX() {
			return pos.x;
		}
		
		public int getY() {
			return pos.y;
		}
		
		public int getDir() {
			return dir;
		}
	}
	
	public static void main(String[] s) {
		JFrame frame = new GameDemo();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setBounds(0, 0, 400, 400);
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
	}
}
 

Anto

Aktives Mitglied
Hey.......


Wenn Du jetzt sauer bist deswegen kann ich das voll verstehen......

Du hast Dir echt eine Riesenmühe gemacht mit dem Programm da....

Mein Problem ist, ich verstehe 80-90% davon nicht!

Um überhaupt erstmal zu verstehen, was Du da tust, müsste ich die ganze Nacht googeln, und DANN wäre ich froh wenn ich es verstanden hätte.....


Sorry

Anto
 

Gossi

Bekanntes Mitglied
Um überhaupt erstmal zu verstehen, was Du da tust, müsste ich die ganze Nacht googeln, und DANN wäre ich froh wenn ich es verstanden hätte.....

Okay, versuchen wir mal ein einfacheres abgespecktes Beispiel:

Java:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JPanel;

//extends JPanel erbt also von einem JPanel
// implements Runnable wird benötigt um eine run() Mehtode zu schreiben
// implements KeyListener wird benötigt um direkt hier Tastaturanschläge zu verwerten
public class GamePanel extends JPanel implements Runnable, KeyListener {

	//JFrame das den ganzen KRam anzeigt
	JFrame frame;

	//booleans um die Bewegungsrichtung zu setzen
	boolean up;
	boolean down;
	boolean left;
	boolean right;
	boolean started;

	//main Methode wird beim Starten aufgerufen und erstellt nen neues Objekt hiervon
	public static void main(final String[] args) {
		new GamePanel(800, 600);
	}

	//Der Konstruktor wird beim erstellen des Objekts aufgerufen
	public GamePanel(final int w, final int h) {
		//Setzt die Größe des Fensters
		this.setPreferredSize(new Dimension(w, h));
		//Setzt die Hintergrundfarbe
		this.setBackground(Color.BLUE);

		//Nun müssen wirs uns noch um das Frame kümmern:

		//Erzeugen des Frames
		frame = new JFrame("Titel");
		//Position auf dem Bildschrim
		frame.setLocation(100, 100);
		//Was soll bei einem Klick auf das X (bei Windoof) passieren
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//Fügt das JPanel dem Frame hinzu
		frame.add(this);
		Fügt einen KeyListener hinzu
		frame.addKeyListener(this);
		//Setzt das Frame auf Sichtbar
		frame.setVisible(true);

		//Das startetn setzen:
		started = true;

		//Erzeugt einen neuen Thread der die run() Methode dieser Klasse aufruft beim Starten
		Thread th = new Thread(this);
		//Ruft die run() Methode auf
		th.start();
	}

	//Nun folgen die Befehle, die bei den einzelnen Tastatur Events ausgelöst werden

	@Override
	public void keyTyped(final KeyEvent e) {
		/***************************
		 * do nothing | do nothing *
		 ***************************/
	}

	//Wenn die Taste gedrückt wird
	@Override
	public void keyPressed(final KeyEvent e) {
		
		//Wenn das Event ein Tastenanschlag auf Pfeiltaste oben ist:
		if (e.getKeyCode() == KeyEvent.VK_VK_UP) {
			//Soll die boolische Variable up auf true gesetzt werden
			up = true;
		}
		//Das gleiche mit Pfeiltaste runter und down
		if (e.getKeyCode() == KeyEvent.VK_DOWN) {
			down = true;
		}
		//mit Pfeiltaste links und left
		if (e.getKeyCode() == KeyEvent.VK_LEFT) {
			left = true;
		}
		//und mit Pfeiltaste rechts und right
		if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = true;
		}

	}

	//Und hier sagen wir was gemacht werden soll wenn die Taste wieder losgelassen wird
	@Override
	public void keyReleased(final KeyEvent e) {
		//Hoch auf false
		if (e.getKeyCode() == KeyEvent.VK_UP) {
			up = false;
		}
		//Runter auf false
		if (e.getKeyCode() == KeyEvent.VK_DOWN) {
			down = false;
		}
		//Links auf false
		if (e.getKeyCode() == KeyEvent.VK_LEFT) {
			left = false;
		}
		//Rechts auf false
		if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = false;
		}

	}

	//Diese Methode wird im run() immer wieder aufgerufen um die "Geschwindigkeit" der
	//Spielfiguren zu setzen
	private void checkKeys() {

		if (up) {
			copter.setVerticalSpeed(-vertSpeed);
		}
		if (down) {
			copter.setVerticalSpeed(vertSpeed);
		}
		if (left) {
			copter.setHorizontalSpeed(-speed);
		}
		if (right) {
			copter.setHorizontalSpeed(speed);
		}

		if (!up && !down) {
			copter.setVerticalSpeed(0);
		}
		if (!left && !right) {
			copter.setHorizontalSpeed(0);
		}

		//Man könnte es bei left z.B. auch so schreiben um sicher zu gehen,
		//dass man sich nur in eine Richtung bewegen kann:
		if (left && !right && !up && !down) {
			copter.setHorizontalSpeed(-speed);
		}

	}

	//Hier die vielerorts angekündigte run() Methode, diese Berechnungen werden unabhängig
	//von den anderen Methoden ausgeführt, sie läuft also quasi Parallel
	@Override
	public void run() {
		//Solange wie das Frame sichtbar ist
		while (frame.isVisible()) {

			//Wenn das Spiel gestartet ist
			if (isStarted()) {
				//Überprüfen ob Tasten gedrückt sind, wenn ja Geschwindigkeit setzen
				checkKeys();
				//Die Logic ausführen (Berechnungen)
				doLogic();
				//In diese Methode kommen die Bewegungen deiner Spieler
				moveObjects();
			}

			//Mal das Spielbrett nachzeichnen ;)
			repaint();

			//Und das ganze, wenns geht, alle 10ms
			try {
				Thread.sleep(10);
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		}
	}

}

So, wenn wir uns nun mal die Run Methode anschauen, wirst du sehen, dass sie die doLogic() etc aufruft.

d.H., dass der gestartete Thread sich um diese Methoden kümmert, während der "Hauptthread" der ja beim Programmstart bereits vorhanden ist, sich um das erfragen der Tastenanschläge kümmert.

Somit haben wir also, bedingt durch den zweiten Thread die Möglichkeit, berechnungen durchzuführen, ohne die GUI zu blockieren...
 
Erstmal vorweg: Threading sollte im Sinne eines Separate of Concerns genau wie Persistenzzugriffe etc. immer als eigener Concern behandelt werden.

Zweitens ist das Problem, dass KeyPressed keine multiple-presses Keys unterstützt. Man bekommt immer nur die Antwort eines Keys zur Zeit. Darum behelfen sich die beiden hier mit den booleans. Ich denke, jetzt sollte es klar geworden sein.
 

Ähnliche Java Themen

Neue Themen


Oben