# KeyPressed



## Anto (16. Nov 2011)

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


----------



## Sunchezz (16. Nov 2011)

vielleicht nach Threads


----------



## Anto (16. Nov 2011)

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


----------



## Gossi (16. Nov 2011)

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 (16. Nov 2011)

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.


----------



## vanny (16. Nov 2011)

Anto hat gesagt.:


> Ich kann immer nur eine Figur gleichzeitig bewegen...
> 
> Anto



den find ich irgendwie gut

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 (16. Nov 2011)

Du machst dans ganze aber so, oder?


```
@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 (16. Nov 2011)

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

analog im zweifel eben figur2.up();

Wieso fragst Du?


----------



## Gossi (16. Nov 2011)

Anto hat gesagt.:


> 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:

```
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 (16. Nov 2011)

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?


----------



## Gossi (16. Nov 2011)

Anto hat gesagt.:


> [...]da drumherumkomme?



Eine möglichkeit wären booleans, so wie in meinen 2 Codeausschnitten gezeigt...


----------



## Anto (16. Nov 2011)

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... (16. Nov 2011)

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 (16. Nov 2011)

Anto hat gesagt.:


> 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...



Anto hat gesagt.:


> 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...


----------



## Anto (16. Nov 2011)

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?


----------



## Gossi (16. Nov 2011)

Anto hat gesagt.:


> 2. Was genau ist ein GameLoop?
> 
> Da brauche ich ja doch wieder Threads?



Richtig, normalerweise wird deine main Klasse auf implements Runnable gestellt und dann haste du eine run-Methode die alle x Milliskekunden ausgeführt wird und deine Berechnungen etc macht...

Evtl. interessant für dich


----------



## Anto (16. Nov 2011)

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?


----------



## Michael... (16. Nov 2011)

Anto hat gesagt.:


> 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
> ...


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.


----------



## vanny (16. Nov 2011)

Stichwort EDT

//EDIT: to late ((


----------



## Michael... (16. Nov 2011)

Anto hat gesagt.:


> 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 (16. Nov 2011)

Anto hat gesagt.:


> 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
> ...




Dann kannst du auch ne prüfung einbauen:

```
@Override
    public void keyPressed(final KeyEvent e) {
 
        if (e.getKeyCode() == KeyEvent.VK_W) {
            up = true;
            down = false;
            left = false;
            rigth = false;

            //usw
 
    }
```


----------



## Anto (16. Nov 2011)

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 (16. Nov 2011)

Anto hat gesagt.:


> 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... (16. Nov 2011)

Anto hat gesagt.:


> 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.


----------



## Gossi (16. Nov 2011)

Michael... hat gesagt.:


> Das ist schlecht. Für ein Arcade Games sind mindestens zwei Threads notwendig.
> 
> Das was Du vorhast geht nicht ohne Threads.



Doch, geht bestimmt, aber nur mit doppelt soviel code, bei halb soviel geschwindigkeit, mit einem virtel Userbillity...


----------



## Anto (16. Nov 2011)

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 (16. Nov 2011)

Überlegung: KANN auch mein Model runnable implementieren oder muss das zwingend die GUI machen?


----------



## Michael... (16. Nov 2011)

Anto hat gesagt.:


> Überlegung: KANN auch mein Model runnable implementieren oder muss das zwingend die GUI machen?


Weder noch. Die GameLoop würde ich vom Controller erzeugen und starten lassen. Selbstverständlich muss das Model in der Loop bekannt sein, da es ja von dieser manipuliert werden muss.


----------



## Anto (16. Nov 2011)

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... (16. Nov 2011)

Anto hat gesagt.:


> 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.


Anto hat gesagt.:


> 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 (16. Nov 2011)

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... (16. Nov 2011)

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 (16. Nov 2011)

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... (16. Nov 2011)

Anto hat gesagt.:


> 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:

```
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();
	}
```



Anto hat gesagt.:


> 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 (17. Nov 2011)

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 (17. Nov 2011)

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... (17. Nov 2011)

Anto hat gesagt.:


> 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.


Anto hat gesagt.:


> (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


Anto hat gesagt.:


> 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... (17. Nov 2011)

Anto hat gesagt.:


> Ganz banale Frage: Was IST ein Thread?


Auf Wikipedia sollte sich sicher was dazu finden lassen.
Ist quasi ein "Handlungsstrang" in dem ein Ablauf beschreiben wird. In der Programmierung werden Threads verwendet um mehrere Prozesse parallel abzuarbeiten.
Zum Einlesen bzgl. Java gibt es hier was:
Galileo Computing :: Java ist auch eine Insel - 12 Einführung in die nebenläufige Programmierung


----------



## Anto (17. Nov 2011)

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 (17. Nov 2011)

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... (17. Nov 2011)

Anto hat gesagt.:


> 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.


Anto hat gesagt.:


> 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.


Anto hat gesagt.:


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


?


Anto hat gesagt.:


> 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 (17. Nov 2011)

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 (17. Nov 2011)

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?


----------



## Michael... (17. Nov 2011)

Anto hat gesagt.:


> 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?


Anto hat gesagt.:


> 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 (17. Nov 2011)

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!


----------



## Anto (17. Nov 2011)

Ist das echt so schwierig und unlösbar?


----------



## Michael... (17. Nov 2011)

Anto hat gesagt.:


> zu 1: Ersteres.


und woher kommen dann die getter. Hab ich das richtig verstanden: Ihr castet das Observable auf ein Object vom Typ Model?


Anto hat gesagt.:


> 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)


Anto hat gesagt.:


> 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.

```
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 (17. Nov 2011)

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 (18. Nov 2011)

Anto hat gesagt.:


> 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:


```
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...


----------



## ProggingTroll (18. Nov 2011)

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.


----------



## Michael... (18. Nov 2011)

Anto hat gesagt.:


> 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!


keine Angst ich bin nicht sauer und viel Mühe hat es auch nicht gekostet, war ja nur ne Sache von ein paar Minuten

Grundvoraussetzung um das zu verstehen ist, dass man sich mit Threads etwas auskennt.
Vom Prinzip ist es ganz einfach. Es gibt einen Thread/Handlungsstrang der die Tastatur überwacht und entsprechend die boolean Flags setzt.
Parallel dazu läuft ein zweiter Thread/Handlungsstrang, der die Flags auswertet und entsprechende Manipulationen an der Position und Ausrichtung vornimmt.
Ist jetzt nicht MVC konform, aber sollte ja nur darstellen wie man mit Hilfe solcher Flags und zweier Threads kommunizieren und berechnen kann ohne sich gegenseitig zu blockieren.


----------



## Anto (18. Nov 2011)

2 Fragen dazu: Einerseits scheine ich ja, wenn ich das richtig sehe, nicht drumherumzukommen, dass die Klasse, die KeyPressed implementiert, auch Runnable implementiert, oder?

Genau das ist bei uns ja aber verboten, weil ich maximal ein Interface pro Klasse haben darf!

na ok die 2. frage geht da vielleicht doch etwas weit.....die besagten booleschen Variablen müssen auch wirklich in der Klasse mit dem Listener stehen, das ist unter keinen Umständen möglich dass die im model stehen oder?


----------



## Michael... (18. Nov 2011)

Anto hat gesagt.:


> 2 Fragen dazu: Einerseits scheine ich ja, wenn ich das richtig sehe, nicht drumherumzukommen, dass die Klasse, die KeyPressed implementiert, auch Runnable implementiert, oder?


Nein, wird in meinem Fall nicht gemacht und bei Gossi nur um die Demo kompakter zu halten. Es gibt keinen Grund warum die Klasse den KeyListener implementiert ein Runnable sein sollte.


Anto hat gesagt.:


> na ok die 2. frage geht da vielleicht doch etwas weit.....die besagten booleschen Variablen müssen auch wirklich in der Klasse mit dem Listener stehen, das ist unter keinen Umständen möglich dass die im model stehen oder?


Nein, in Deinem Fall würde ich Sie mit ins Model stecken, da das Model das zentrale Kommunikationsmittel über die Klassen und Threads hinweg ist.


----------



## ProggingTroll (18. Nov 2011)

Michael... hat gesagt.:


> Nein, in Deinem Fall würde ich Sie mit ins Model stecken, da das Model das zentrale Kommunikationsmittel über die Klassen und Threads hinweg ist.



Das muss man etwas anders sehen. Gemäß Single Responsibility Prinzip ist es nur Aufgabe des Models, die Spielelogik bereitszustellen. Es interessiert die Geschäftslogik nicht, ob irgendwelche Keys gedrückt werden und damit irgendwelche boolschen Werte. Darum hat das im Business Layer nichts zu suchen. Das Anfangen von Benutzereingaben ist beim MVC Aufgabe des Controlers und das Anzeigen der Spielfiguren Aufgabe der View. Es ist sehr wichtig, dieses zu beachten, da man sonst archtitekturelle Fehler macht, die später teuer werden.


----------



## Anto (18. Nov 2011)

Troll (ich hab so eine Ahnung dass ich weiß wer Du bist......), nach der Logik wäre meine Aufgabe aber schlicht nicht lösbar, bei mir müssen die Booleans ins Model.

Michael/Gossi: Wenn mein Thread aber doch nicht in der Listenerklasse sitzt, wie soll er dann an KeyPressed herankommen?


----------



## ProggingTroll (18. Nov 2011)

Anto hat gesagt.:


> Troll (ich hab so eine Ahnung dass ich weiß wer Du bist......), nach der Logik wäre meine Aufgabe aber schlicht nicht lösbar, bei mir müssen die Booleans ins Model.



Das Model verliert dadurch seine Unabhängigkeit, weil es fest an eine Ausführung und Steuerung durch Tastatur gebunden wird. Das Model sollte aber überhaupt nicht wissen, wie es gesteuer wird. Darum gibt es ja den Controler als übergeordnete Zwischenschicht.


----------



## Anto (18. Nov 2011)

Das ist doch ein Nebenkriegsschauplatz, das ganze muss doch ersteinmal überhaupt IRGENDWIE laufen.


----------



## ProggingTroll (18. Nov 2011)

Michael... hat in seinem Beispiel ja gezeigt, wie Events, die an anderer Stelle gefeuert werden, verarbeitet werden können...


----------



## Anto (18. Nov 2011)

Ich habe einfach überall dasselbe Problem: Ich KANN da natürlich  booleans einbauen und zum beispiel sagen: Bei dem und dem Keycode setze die up/down/right/left -Variable dieses oder jenes spielers auf true, wenn die taste losgelassen wird wieder zurück auf false.

Aber was soll mir das helfen? Ich kann doch dann immer noch nicht 2 Tasten gleichzeitig drücken.

Im Grunde suche ich doch nur die beiden Wunderzeilen, die mir das ermöglichen.


----------



## Gast2 (18. Nov 2011)

Ich hab jetzt nicht den kompletten Thread gelesen, bin mir aber sicher dass das schon jemand gesagt hat:

Du brauchst 4 booleans pro Spieler (am besten im "Spieler" Objekt selbst). In der keyPressed setzt du den entsprechenden boolean auf true, in der keyReleased wieder auf false.
Ein separater Thread (deine GameLoop) schaut sich in jedem durchlauf die booleans an und bewegt den Spieler entsprechend. Wenn du 3 oder 4 Tasten gleichzeitig drückst sind bei dir halt 3 oder 4 booleans auf true gesetzt.


----------



## Sunchezz (18. Nov 2011)

Wenn du nirgendwo das Interface Runnable benutzen darfst, nimms doch im Gameloop (im seperaten Thread) als anonymes Objekt.


```
Thread gameloop = new Thread(new Runnable() {
  public void run() {
    while(gameIsRunning) {
      //TODO
    }
  }
)};
```


----------



## Anto (18. Nov 2011)

Wie gesag das mit den 4 booleans habe ich begriffen. Was ich  nciht verstehe ist warum mein GameLoop dann plötzlich funktionieren soll


----------



## Anto (18. Nov 2011)

Moment langsam. Ich glaube ich habe so ganz langsam in Ansätzen das Problem begriffen. Das Problem ist dass meine Methode eigentlich während dem ganzen Spiel durchlaufen muss das aber nicht kann, richtig? und Run sorgt jetzt irgendwie auf wundersame Weise dafür dass sie das doch kann?


----------



## Sunchezz (18. Nov 2011)

Anto hat gesagt.:


> Moment langsam. Ich glaube ich habe so ganz langsam in Ansätzen das Problem begriffen. Das Problem ist dass meine Methode eigentlich während dem ganzen Spiel durchlaufen muss das aber nicht kann, richtig? und Run sorgt jetzt irgendwie auf wundersame Weise dafür dass sie das doch kann?


Das hat nichts mit den zwei gleichzeitigen Tastenanschlägen zu tun!
Ich dachte das war dein Problem?


----------



## Anto (18. Nov 2011)

Ist das nicht dasselbe? Im Grunde muss doch die Methode, die checkt wie die booleans aussehen und entsprechend die Figuren versetzt während des ganzen Spiels laufen?

Und weil sie das nicht kann, bewegen meine Figuren sich nicht anständig parallel?
Ist das nicht der Grund?


----------



## Sunchezz (18. Nov 2011)

Also wenn dein Spiel nicht durchgängig läuft is sowieso was schiefgegangen^^

Aber mit dem ganz speziellen Problem der zwei Tastenanschläge hat das nichts zu tun.
Würde mich jetzt jedenfalls wundern. Wenn doch, würd ich gern mal den Code sehen^^


Ich unternehme mal einen gewagten versuch dir zu erklären warum es mit Booleans funktioniert:

fragst du jedesmal neu in der keyPressed() nach gedrückten Tasten ab, bekommst du IMMER nur eine Taste zurück (verrät dir auch der methoden Name)
Setzt du in einem boolean der zu der taste passt den wert true, speicherst du dir *zwischen*, das die taste immernoch gedrückt ist.
Somit weist du in der klasse der booleans zu *jeder* Zeit welche Taste(n) gedrückt sind/ist.

Wenn die taste losgelassen wird, erfärst du es in keyReleased (boolean auf false setzen)!


----------



## ProggingTroll (18. Nov 2011)

Richtig, das mit dem parallelel Laufen ist Sache des "Tricks" mit der Bitmaske. Der Thread entlastet die GUI, damit es nicht zu einem Blockieren kommt.


----------



## Michael... (18. Nov 2011)

Anto hat gesagt.:


> Wie gesag das mit den 4 booleans habe ich begriffen. Was ich  nciht verstehe ist warum mein GameLoop dann plötzlich funktionieren soll


Weil hier zwei Threads/Abläufe laufen.
Threads sind in er Computertechnik ein Mittel um Prozesse "parallel" abarbeiten zu können. Ohne Threads könnten auf einem Computer keine zwei Programme gleichzeitig laufen. Ich könnte während ich diesen Text schreibe nicht nebenbei ein Video anschauen oder mein E-Mail Programm könnte nicht ständig überprüfen, ob neue E-Mails für mich eingegangen sind...


			
				Anto hat gesagt.:
			
		

> Ist das nicht dasselbe? Im Grunde muss doch die Methode, die checkt wie die booleans aussehen und entsprechend die Figuren versetzt während des ganzen Spiels laufen?
> 
> Und weil sie das nicht kann, bewegen meine Figuren sich nicht anständig parallel?
> Ist das nicht der Grund?


Pauschal würde ich mal sagen: Ja


----------



## Sunchezz (18. Nov 2011)

Anto hat gesagt.:


> Ist das nicht dasselbe? Im Grunde muss doch die Methode, die checkt wie die booleans aussehen und entsprechend die Figuren versetzt während des ganzen Spiels laufen?



Ok, damit wird langsam klar wo du hängst...
Hoff ich zumindest!

run() 
bewirkt das nicht auf wundersame weise...
wohl eher "while(gameIsRunning)"

logisch muss die Methode die etwas überprüft in einem Spiel permanent laufen!


----------



## Anto (18. Nov 2011)

Ich habe aber keine Methode, wo etwas in der Art von "while game is running" drinsteht:

Ich setze einfach einmal mein Fenster mit allem was da so drauf ist und alles andere geschieht auf Tastendruck. Solange ich das fenster nicht wieder exe (habe ich noch nicht, ich habe noch kein Spielende) bleibt das Fenster ja bestehen, warum sollte ich da mehr tun als Tastendrücke fangen ?


----------



## Sunchezz (18. Nov 2011)

damit du zwei tastenanschläge gleichzeitig auswerten kannst...

Du erstellst einen Thread der immerwieder neu die Positionen für deine "figuren" setzt, sind die tastenbooleans auf false, bewegt sich auch nichts, hast also nen endlosschleife die nichts tut, aber sobald eine taste gedrückt wird (oder mehrere), kann das durch die dauerlaufende positions berechnung *gleichzeitig* verarbeitet werden.

warum du dafür nen Thread brauchst?
Damit die Schleife nicht das GUI, und den gesamten Programmablauf blockiert.


----------



## Anto (18. Nov 2011)

Ok, ich beginne zu verstehen, aber ich beginne gerade erst. 

Muss dieser Thread denn dann mein ganzes Spiel steuern oder kann ich den einfach irgendwo hinschreiben?

Also ich erzeuge irgendwo einen Thread der eine Schleife macht die ich erst noch schreiben muss?


----------



## Anto (18. Nov 2011)

Vielleicht, um die Frage mal etwas klarer zu machen:

WO muss dieser Thread hin? 
Ins Model?

WAS muss diesem Thread alles übergeben werden?

Wenn der Thread nicht in der Klasse steht, die Runnable implementiert, WOHER weiß er dann wass er alles kann?


----------



## Gast2 (18. Nov 2011)

Anto hat gesagt.:


> WO muss dieser Thread hin?
> Ins Model?


Das wär so zimelich der letzte Platz wo ich den ansiedeln würde. Entweder in den Controller oder als extra Klasse auslagern.



Anto hat gesagt.:


> WAS muss diesem Thread alles übergeben werden?


Alles was er braucht. Ich kenne deinen Code nicht, aber zumindest das Model und den Controller sollte der kennen.



Anto hat gesagt.:


> Wenn der Thread nicht in der Klasse steht, die Runnable implementiert, WOHER weiß er dann wass er alles kann?


Öhm, was?
Ein Thread arbeitet ein Runnable Objekt ab. Mehr nicht. Alles was der Thread machen soll steht in der run() Methode von deinem Runnable. In deinem Fall würde die run() Methode irgendwie so aussehen:

```
public void run()
  while (gameIsRunning) {
    // process the game
  }
}
```


----------



## Anto (18. Nov 2011)

Aber ich darf doch nichts haben was das Model UND den Controller kennt!

Es gibt nur M, C und V, zu irgendeinem von den dreien muss er gehören!

Woher weiß ich denn, was mein Thread alles braucht?

Gibt es da irgendwelche Richtlinien?


----------



## Michael... (18. Nov 2011)

Anto hat gesagt.:


> WO muss dieser Thread hin?


Wo dieser Thread hin muss ist prinzipiell egal, das ist eine Designfrage, die Du mit Deinem Gewissen und dem Benoter der Aufgabe ausmachen musst.
Ich würde vermutlich den Thread als Unterkomponent des Controllers platzieren, man kann ihn theoretisch aber auch direkt ins Model stecken. Allerdings sollte man bedenken, dass darin Spielelogik abgebildet wird.

Viel wichtiger ist was der Thread macht und worauf er Zugriff benötigt:
- die boolean Flags
- Position der Spieler
- ...
also auf die Daten des Models, nicht mehr und nicht weniger.
Und er macht nichts anderes ausser die Flags auszuwerten und dem entsprechend die Daten des Models zu manipulieren.


----------



## Anto (18. Nov 2011)

Wenn er in erster Linie die Modeldaten braucht, ist er dann nicht Teil des models? Und wenn ich ihn stattdessen in den Controller packe und ihm stattdessen das Model übergebe (das ginge ja, in der Main), wie kann ich das wenn der Controller doch den Listener implementiert und daher nicht auch noch Runnable implementieren darf? Oder kann ich dafür eine neue Controllerklasse anlegen?


----------



## bERt0r (18. Nov 2011)

Ich hab jetzt nicht die ganzen 4 Seiten Posts gelesen, aber einen Thread der die Positionen von Spielfiguren bestimmt würde ich als Controller einordnen.
Und eine Klasse kann ohne Probleme mehr als ein Interface implementieren.


----------



## Anto (18. Nov 2011)

KANN sie schon aber darf sie bei uns nicht.


----------



## Michael... (18. Nov 2011)

Anto hat gesagt.:


> Wenn er in erster Linie die Modeldaten braucht, ist er dann nicht Teil des models?


Gegenfrage: Die View benötigt ebenfalls in erster Linie die Modeldaten, daher könnte man sie doch ebenfalls als Teil des Models definieren?
Programmiertechnisch ist alles möglich, warum man etwas so oder so macht ist eine Designfrage und hier haben sich MVC und ähnliche Entwurfsmuster bewährt, da man hierbei leicht einzelne Komponenten austauschen kann ohne in die restlichen Komponenten eingreifen zu müssen.
Wenn man Spielelogik und Model mischen möchte darf man das. Man darf sich dann aber nicht wundern wenn man bei Änderungen am Model die Spielelogik anpassen muss und umgekehrt.


Anto hat gesagt.:


> Und wenn ich ihn stattdessen in den Controller packe und ihm stattdessen das Model übergebe (das ginge ja, in der Main), wie kann ich das wenn der Controller doch den Listener implementiert und daher nicht auch noch Runnable implementieren darf? Oder kann ich dafür eine neue Controllerklasse anlegen?


Man muss ihn ja nicht direkt in den Controller packen, man kann ihn ja als Subkomponente halten. So wie der Controller Model und View steuert, könnte er noch als weiter Komponenten einen Thread in Form einer Gameloop steuern.


----------



## Michael... (18. Nov 2011)

Anto hat gesagt.:


> KANN sie schon aber darf sie bei uns nicht.


Um das nochmal klar zustellen. Der Controller muss dazu nicht das Runnable Interface implementieren. (Sollte er m.M. auch nicht unbedingt). Er kann genauso gut eine Referenz auf ein Objekt halten, welches dieses Interface implementiert.

####EDIT####
Zur Erläuterung was ich meine:

```
public class MVCTDemo {
	class Model{
		//...
	}
	
	class View {
		public View(Model model) {
			//..
		}
	}
	
	class GameLoop implements Runnable {
		public GameLoop(Model model) {
			//..
		}

		public void run() {
			//..
		}
	}
	

	class Controller {
		public Controller() {
			Model model = new Model();
			View view = new View(model);
			GameLoop loop = new GameLoop(model);
			//..
			new Thread(loop).start();
		}
	}
	
	public static void main(String[] s) {
		new MVCTDemo().new Controller();
	}
}
```


----------



## Anto (18. Nov 2011)

Also eine weitere Controllerklasse, die Runnable implementiert.
Ein Objekt dieser Klasse wird in der Main erzeugt und bekommt das Model übergeben.
Ok.
Und WO muss ich nun die Threads hinschreiben?

(Moment, langsam: Meine View hat keine Modelinstanz, das darf sie auch bei uns nicht).


Meine main sieht im übrigen so aus.


```
Mod m = new Mod();
Con c = new Con(m);
c.init();
```

Da wollte ich eben jetzt noch reinschreiben: Threadklasse t = new Threadklasse(m);

und dann halt irgendwas damit machen. Ist das richtig so?


----------



## Michael... (18. Nov 2011)

Im Prinzip hast Du da oben ja schon ein komplettes Gerüst stehen, dass "nur" noch ausgefüllt werden muss. Wenn Du das Model - aus welchen Gründen auch immer - nicht der View übergeben darfst, dann muss man diesen Teil halt Euren Vorgaben anpassen.


----------



## ProggingTroll (18. Nov 2011)

Wunderbar! Michael hat völlig recht.



> Da wollte ich eben jetzt noch reinschreiben: Threadklasse t = new Threadklasse(m);
> 
> und dann halt irgendwas damit machen. Ist das richtig so?



Nein, die (gute und richtige) Idee von Michael war, auf einer Controller-Ebene eine Klasse zu erstellen, die diese Aufgabe übernimmt.


----------



## Anto (18. Nov 2011)

Na aber.....das tue ich doch. Ich schreibe in das Controllerpackage eine solche Klasse, die Runnable implementiert.
Was mir noch ein völliges Rätsel ist, ist, wo ich dann die Threads erzeugen muss?

Im Controller, und ihnen da das Model übergeben, oder so?


----------



## Michael... (18. Nov 2011)

Ein Thread läuft ja bereits, der zweite wird hier erzeugt und gestartet.


Michael... hat gesagt.:


> ```
> GameLoop loop = new GameLoop(model);
> //..
> new Thread(loop).start();
> ```


----------



## Anto (18. Nov 2011)

Das habe ich mitbekommen ich habe nur nicht verstanden, WO diese Zeile stehen soll?

im Controller?

Bzw. ich denke mal schon aber muss die Zeile, wo start aufgerufen wird, dann im Konstruktor vom Controller stehen?


----------



## Anto (18. Nov 2011)

Das Thread erzeugen geht im übrigen in meiner Controllerklasse nicht, weil er das dann nicht findet?
Also....der Aufruf von start sagt "cannot find  loop in location controllerklasse"


----------



## bERt0r (18. Nov 2011)

Halt Stopp! Bevor du noch 5 Seiten Posts hier dranhängst mach mal einen Moment pause und überleg mal. WO code steht ist für dein Problem völlig irrelevant. Du kannst die gleiche Funktionalität, mit dem gleichen DesignPattern trotzdem noch auf zig unterschiedliche Arten realisieren.
Du scheinst zu glauben, dass durch deine Vorgaben manche Dinge unmöglich sind, stimmt aber nicht, du musst nur anders an die Sache herangehen.
Ein Beispiel: Wenn du immer nur ein Interface implementieren darfst, aber eben 2 implementieren möchtest für deine Funktionalität. 
Lösung: Du schreibst dir 2 Klassen die jeweils ein Interface implementieren und machst dann eine Oberklasse, die je eine Instanz der jeweiligen Imlementierungen der Interfaces hält und diese dann bedient.


----------



## Anto (18. Nov 2011)

Dafür bräuchte ich aber doch viel mehr Wissen!

Ich meine ich habe mittlerweile begriffen, dass eine Klasse, die Runnable implementiert, aus irgendeinem Grund Threadobjekte erzeugen kann.

Anscheinend kann ich das aber nur IN dieser Klasse und an keinem anderen Ort, was also hilft mir das?

Ich bräuchte einfach viel mehr Wissen als ich habe.

Diesen Befehl, der die Startmethode aufruft: ich GLAUBE, das sind eigentlich 2 Befehle.
In der Schreibweise als EIN befehl verstehe ich aber nur begrenzt, was da geschieht.

Und in jedem Fall muss ich ja irgendwo ein Threadobjekt erzeugen, aber anscheinend gibt es ja keine einzige Klasse, in der das ohne Fehlermeldung geht?

Und eigentlich will ich nicht noch mehr Oberklassen haben, ich verstehe das was ich jetzt tue ja schon kaum.
Zumal ich dann hier wieder 5h fragen müsste, was jetzt wozu die Oberklasse sein muss und aus welchen Gründen überhaupt.



Kann man nicht in einfachen Worten sagen:

"Die Klasse, die den Listener implementiert/die Klasse, die den Spieler bewegt /welche Klasse auch immer muss ein Threadobjekt erzeugen. Dieses Objekt muss dann die Methode Start aufrufen. Start wird implementiert in der klasse, die folgendes macht: .....
und run tut im Grunde folgendes: "

So etwas in der Art suche ich, aber das scheint es nirgendwo zu geben.


----------



## ProggingTroll (18. Nov 2011)

Es gibt in der Programmierung keine Kochrezepte. Ebenso bringt es nichts, sich stumpf an irgendwelche künstlichen Regeln wie "nur ein Interface pro Klasse" zu implementieren, zu halten. Allerdings gibt es ein gewisses Set an Regeln, an dem sich orientieren kann, die sich als zuträglich erwiesen haben: Clean Code Developer - Clean Code Developer

Tatsächlich braucht man nicht einmal zwingend eine Klasse, die Runnable implementiert. (In diesem Fall aber schon).

```
Thread fred = new Thread(new Runnable()
  {
     run()
    {
    //
    }
   });

fred.start();
```


----------



## Anto (18. Nov 2011)

Naja. Bei den Programmieranteilen, die ich bis jetzt gut kann (nicht java, wie man sieht) habe ich mcih meist das erste halbe Jahr stumpf an Kochrezepte gehalten und dann angefangen mich davon wegzubewegen.
Aber für dieses erste halbe Jahr waren die Rezepte eben hilfreich.

Ich habe das nun so gemacht wie es da steht.

Bleiben 2 Fragen:
WO wird start implementiert (oder ist die vorgegeben?)

WAS in etwa kommt in run? also, ganz allgemein jetzt.


----------



## Sunchezz (18. Nov 2011)

der Thread sollte am sinnvollsten normaler weise im Controller implementiert werden.

Aber eigentlich ist das egal, es sei denn du willst dich an diese Regeln halten...
Die hauptsache ist nur das der Thread zufgriff auf alle Elemente bekommen kann (über getter) die für seine aufgaben wichtig sind.


in run() sollte alles stehen was für die flagAbfrage deiner key wichtig ist, und die berechnung der positionen!


----------



## bERt0r (18. Nov 2011)

Es wäre besser wenn du statt DesignPatterns (Kochrezepte) zu lernen du dir erst die Tutorials für gewisse Grundelemente anschaust. Threads sind ja jetzt nicht wirklich etwas java spezifisches, die start/run Geschicht allerdings schon.
Java ist auch eine Insel – 9 Threads und nebenläufige Programmierung


----------



## Anto (18. Nov 2011)

Moment, langsam. Die Positionsberechnung sollte in Run stehen?
Aber die Methoden, die die Position neu setzen, stehen doch in meiner Spielerklasse! und ich dachte immer, da gehören sie logischerweise auch hin!

oder wäre das im zweifel nur die eine Methode die immer neu durchrennt?

Ihr sagt alle, ich soll zuerst Grundlagen lernen, aber wenn ich das tue, muss ich das Semester noch einmal machen!


----------



## Sunchezz (18. Nov 2011)

du kannst die berchenungsmethoden doch auch aus der View, in dem thread aufrufen^^


----------



## bERt0r (18. Nov 2011)

Tja, ohne die Grundlagen gehts leider nicht. Wenn du nicht weißt was ein Thread ist, wirst du auch keinen programmieren können.


----------



## Anto (18. Nov 2011)

Tja also, ich habe das jetzt so gemacht wie ich DACHTE dass es richtig ist: Mein Controler:

-erzeugt ein Objekt einer Klasse X, die Runnable implementiert, und zwar im Konstruktor, und ruft darauf start auf. ausserdem wird diesem Xobjekt das model übergeben.
-reicht Keycodes (bzw. Entsprechungen davon) ans Model weiter.

Das Model seinerseits setzt daraufhin die entsprechenden Booleans in den Spielerklassen. Außerdem setzt es auch noch einen boolean, der anzeigt, dass das Spiel gestartet wurde.

In run steht nun in etwa drin:

Solange das Spiel gestartet ist:

Überprüfe ständig, welche von den entsprechenden booleans gerade auf true stehen und nutze die entsprechenden Methoden der Spielerklasse, um die Werte zu ändern.
Außerdem rufe eine Methode in Model auf, die im grunde nichts anderes macht als setChanged usw.


Wenn ich nun mein Programm starten will, startet das Programm korrekt, aber ich kann keine meiner Spielfiguren bewegen.

Das ist im Nachhinein auch kein Wunder, denn run wird ja nirgends aufgerufen!
Wo wird das gemacht? In Euren Beispielen kam immer nur start....

Habe ich etwas wichtiges vergessen, nach meinen Erzählungen hier?


----------



## Sunchezz (18. Nov 2011)

Thread.start() ruft in sich run() auf...

in der hinsicht ist also alles korrekt, wenn du start() aufrufst!



Code?


----------



## Anto (18. Nov 2011)

Ich kann doch keine 3 Klassen hier vollständig einstellen.....so von wegen Plagiatsverdacht und so.....


----------



## Sunchezz (18. Nov 2011)

bitte was?????

dann stell doch den Code des Threads, oder von mir aus auch der run() rein!


----------



## Anto (18. Nov 2011)

Aber gut, mal ein Versuch:

In meier Controllerklasse stehen in Keypressed und KeyTyped jeweils switch cases, die den Keycode ans Model weitergeben.

Im Model stehen 2 Methoden, die je nach Keycode bzw. je nachdem, von wem sie aufgerufen werden, die Booleans in den beiden Spielerobjekten neu setzen.

Das funktioniert auch soweit, wenn ich meinen Printbefehlen glauben darf.

Am Ende dieser Methode steht jeweils noch setChanged(); und notifyObservers();

Naja und meine neue Klasse, die  Runnable implementiert, bekommt ein Model übergeben. 
in run steht in etwa:

while(mod.isSet())

{if (spieler1bool == true){spieler1.gehehoch();}
if (spieler2bool == true){spieler2.gehehoch();}

.
.
.
.
.

: mod.benachrichtige();



}


wobei in benachrichtige nur drinsteht setChanged und notify Observers.



ach bevor ich es vergesse:

Im Controller steht noch (also im Konstruktor davon):

X x = new X (m); 
new Thread(x).start();


----------



## Sunchezz (18. Nov 2011)

Und es passiert nichts??

Ich schätze dann kommst du langsam nicht drum rum mal wirklichen Quelltext zu zeigen!
Du kannst auch gerne jede Zeile beschreiben wie ebend, aber ich glaube das wird sich hier keiner antun!


Und bitte JavaTags benutzen.


----------



## Anto (18. Nov 2011)

Nein es passiert nichts.

Und so lebensmüde werde ich sicher nicht sein, es sind schon so viele rausgeflogen weil ihre Sachen im Netz aufgetaucht sind....ein Patzer, und man fliegt aus der Veranstaltung!


----------



## Sunchezz (18. Nov 2011)

Ich verstehe grade das Problem nicht...
Du hast nen Quellcode geklaut und willst den nicht veröfftlichen damits keiner merkt???
Oder was ist hier los?


Also ich kann dir grad jedenfalls nich weiterhelfen ohne Quellcode, da ich absolut nich mehr durchsehe bei deinen Aussagen!
Ich hoffe die anderen sehen mehr durch und können dir noch helfen -.-


----------



## Anto (18. Nov 2011)

Blödsinn ich klaue keine Quellcode.
Aber es ist AUCH verboten den eigenen Code zu veröffentlichen.


----------



## Sunchezz (18. Nov 2011)

Darf ich fragen wer dir das verbietet?


Auf jeden Fall ist so eine Hilfe relativ unmöglich, wenn das nich noch ne Woche so weitergehen soll -.-


----------



## Anto (18. Nov 2011)

Der Veranstalter! Es steht ganz klar in den Bedingungen, dass jeder, der seinen Code in irgendeiner Form zugänglich macht (auch online) fliegt. 
Es sind auch schon welche geflogen deswegen.

Im Moment läuft das ganze sogar (theoretisch) nur dass meine Figuren viel zu schnell sind.

Gibt es eine Möglichkeit, die langsamer zu machen?


----------



## Sunchezz (18. Nov 2011)

vielleicht in dem du den Thread in dem du die figuren bewegst, mit this.sleep(millisekunden) unterbrichst!


----------



## Anto (18. Nov 2011)

Habe ich . Aber ich sehe ein, das geht wirklich nicht mehr ohne Code:

bei 1000 und 500 millisekunden geht es, läuft aber  sehr lahm, bei allem anderen gibt es keine Bewegung.


----------



## Sunchezz (19. Nov 2011)

this.sleep(millisekunden)

Ich hoffe du rufst das innerhalb deiner run() auf!!



```
public void run() {
  while(..) {
   ....boolean überprüfung
   ....positionen setzen
   this.sleep(10);
  }
}
```

nicht außerhalb...


----------



## Sunchezz (19. Nov 2011)

Ich korrigiere, das würde nur funktionieren wenn du direkt Klasse von Thread erben lassen würdest, doch du implementierst ja nur Runnable.

ändere:

```
this.sleep(10);
```

in:


```
Thread.sleep(10);
```


----------



## Anto (20. Nov 2011)

Es hat jetzt funktioniert....mal interessehalber, wieso funktioniert das Aufrufen der startmethode eigentlich nur im Konstruktor?


Danke für die Hilfe

Grüße

Anto


----------



## Sunchezz (20. Nov 2011)

Ist jetzt nur ne Vermutun, da ich deinen Code nich kenne...
aber ich vermute das du den Thread auch nur lokal im Konstruktor implementiert hast.

Würdest du ihn als Klassen-Feld/Attribut/Variable (weiß ja nicht unter was du es kennst) implementieren, könntest du aus der ganzen Klasse und mit zusätzlichen Gettern auch aus anderen Klassen darauf zugreifen!


----------



## Anto (20. Nov 2011)

Hab ich versucht....das Objekt der neuen Klasse initialisieren geht, die Startmethode aufrufen geht nicht.

Ist auch mehr akademisch, da es ja jetzt funktioniert....von daher....


----------



## Sunchezz (20. Nov 2011)

Erbt deine neue Klasse von Thread?

oder erstellst du deine neue Klasse so: ?


```
DeineNeueKlasse dnk = new DeineNeueKlasse();
new Thread(dnk).start();
```

oder vielleicht:

```
Thread t = new Thread(new deineNeueKlasse());
t.start();
```

Weil in diesen beiden fällen ist klar das du woanders mit:

```
deineKlasse.start();
```
nicht weit kommst!
Denn dann erbt deine Klasse nicht von Thread, sondern implementiert nur Runnable.
Diese sieht die Methode start() nicht vor!


----------

