Tag 3 – Eingaben behandeln

veröffentlicht am: 3. Dezember 2012

Schon Tag 3 des Tutorials.
Bevor wir allerdings und mit den Eingaben des Nutzers beschäftigen, will ich nochmal ein kleines „Problem“ beseitigen, dass ich mit dem Starten des Spiels hatte.

Normalerweise arbeitet man immer in der Hauptklasse oder zumindestens im Hauptprojekt. Wenn man jetzt auf Run klickt, versucht Eclipse dieses Library-Projekt zu starten, was aber nicht funktioniert. Da ich zum Entwickeln meistens die Desktop-Version starten will, habe ich mich dazu entschieden, den Desktop-Starter in mein Hauptprojekt zu packen (falls es einen besseren Weg gibt, sagt es mir!)
Dazu mache ich einen Rechtsklick auf testgame und dann Properties -> Java Build Bath -> Source und dann rechts auf „Link Source“
Hier wählt man den assets-Ordner aus dem testgame-android Projekt aus.
Im Reiter Libraries drückt man dann „Add JARs“:
dann testgame-desktop/libs auswählen und dort folgende Dateien

gdx-backend-lwjgl-natives.jar
gdx-backend-lwjgl-sources.jar
gdx-backend-lwjgl.jar
gdx-natives.jar

Nun muss man nur noch die Main-Klasse aus testgame-desktop in das testgame Projekt kopieren und man kann mit einem Klick auf „Run“ das Spiel starten.
Man muss nur noch auswählen, dass man es als „Java Application“ starten will und dann die Klasse „Main“ auswählen.

Nun kann man die TestGame Klasse bearbeiten und muss nicht nochmal links auf testgame-desktop klicken, bevor man die App testen kann. 🙂

Nun aber zum eigentlichen Thema des heutigen Tutorials.
Die Eingaben des Nutzers werden in libgdx ziemlich abstrahiert. Es ist z.B. egal, ob du auf den Touchscreen drückst oder mit der Maus klickst.
Es gibt zwei Wege, die Eingaben abzufangen. Der erste nennt sich pollen, d.h. man kann in der render()-Methode nachfragen, welche Tasten gerade gedrückt sind und wo der Nutzer gerade den Screen berührt.

Über das Objekt Gdx.input kann man alles, was an input kommt abfragen. (mit dem Auto-vervollständigen von Eclipse [Strg+Leertaste] kriegt man hier einen guten Überblick).

Solche Sachen können wir nun abfragen:
ist die Taste A gedrückt?

if(Gdx.input.isKeyPressed(Keys.A)){ }

ist der Touchscreen oder die Maus gedrückt?

if(Gdx.input.isTouched(){ }

Ein Problem ist nur, dass die Mauskoordinaten in der linken oberen Ecke beginnen und sich nicht an die Auflösung der Kamera halten. Um das zu erreichen, müssen wir den Vektor mit der Kameramatrix projezieren (hört sich kompliziert nach MatheLK an, sind für uns aber nur drei Zeilen):

// project input to gamescreen
Vector3 touchPos = new Vector3();
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
// touchPos.x and touchPos.y are now the variables to use

Nun können wir das ganze in der render()-Methode unterbringen und somit die Kugel immer dorthin setzen, wo die Maus gerade ist:

if(Gdx.input.isTouched()){
	// project input
	Vector3 touchPos = new Vector3();
	touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
	camera.unproject(touchPos);
	
	x=(int) touchPos.x-32; 
	y=(int) touchPos.y-32;
}
// [...]
batch.draw(ball,x,y);

Die andere Möglichkeit ist es, die Events mit einer Art EventListener direkt abzufangen, wenn sie gesendet werden. Dies ist besonders sinnvoll, wenn es auf die genaue Reihenfolge ankommt (z.B. wenn man sich sein eigenes Textfeld bauen will). Dafür baut man sich seinen eigenen InputProcessor, den man nur noch bei libgdx registrieren muss (letze beiden Zeilen).

class GameInputProcessor implements InputProcessor {
	@Override
	public boolean keyDown(int keycode) {
		return false;
	}

	@Override
	public boolean keyUp(int keycode) {
		return false;
	}
	@Override
	public boolean keyTyped(char character) {
		return false;
	}
	@Override
	public boolean touchDown(int x, int y, int pointer, int button) {
		return false;
	}
	@Override
	public boolean touchUp(int x, int y, int pointer, int button) {
		return false;
	}
	@Override
	public boolean touchDragged(int x, int y, int pointer) {
		return false;
	}
	@Override
	public boolean scrolled(int amount) {
		return false;
	}

	@Override
	public boolean mouseMoved(int screenX, int screenY) {
		return false;
	}
}	
// [..] in create():
GameInputProcessor inputProcessor = new GameInputProcessor();
Gdx.input.setInputProcessor(inputProcessor);

Der InputProcessor kann verschiedenste Events abfangen.
keyDown() und keyUp() sagen dir, wann eine Taste gedrückt bzw. wieder losgelassen wurde und geben dir dabei den Keycode (die man via Keys.X kriegt) zurück. Bei keyTyped kriegt man einen gerade eingegebenen Buchstaben als char zurück, was sinnvoll sein kann, wenn man ein Textfeld implementiert.
touchDown() und touchUp() werden aufgerufen, wenn man mit der Maus oder dem Finger drückt. Hier wird die x und y Koordinate übergeben (die wieder transformiert werden muss, um auf das Spielfeld zu passen). Außerdem wird übergeben, der wievielte Finger es ist (bei multitouch) und welcher Knopf gedrückt wurde (wenn man eine Maus hat).
Dann gibt es noch die methode mouseMoved(), die aufgerufen wird, wenn man die Maus bewegt, aber nicht gleichzeitig einen Knopf drückt und die Methode touchDragged, die aufgerufen wird, falls man die Maus oder den Finger gedrückt hat und dabei bewegt.
Letztendlich gibt es noch die coole Methode scrolled(), die aufgerufen wird, wenn man das Mausrad dreht.

Die erste Methode ist somit besser, wenn man in jedem Frame nur den Zustand von Tasten oder die Position des Fingers abfragen will (wie es normalerweise ingame der Fall ist) und die zweite, wenn man z.b. die UI für sein Spiel schreibt und es dabei auch auf die Reihenfolge von Events ankommt und keins verpasst werden darf.

Das war heute mal ein sehr textlastiges Tutorial und ich werde es morgen wahrscheinlich nochmal überarbeiten, da ich schon relativ müde bin.
Morgen wird dann entweder was zu Animationen oder was zu Schriften kommen. (je nachdem wo drauf ich Lust habe).

geposted in libgdx

6 Antworten zu “Tag 3 – Eingaben behandeln”

  1. Hi,

    erstmal Herzlichen Dank für alle Tutorials! Hab alle gelesen.
    Ich hab das oben genannte Problem gelöst, indem immer die letzte Konfiguration ausgeführt wird, siehe: http://eclipseone.wordpress.com/2010/01/15/always-rundebug-the-last-launched-class-instead-of-the-selected-one-in-eclipse/#more-622

    Ich würde mich über weitere Tutorials freuen. Hat der normale Adventskalender nicht 24 Türchen?! 😉

    • bitowl sagt:

      diese Version wurde mir auch am Tag 20 oder so im IRC vorgeschlagen (deshalb gibt es im GitHub-Code auch immer drei Projekte). Hab momentan leider viel zu tun, deswegen steht Tag 24 immernoch auf privat, da ich den noch nicht nach Englisch übersetzt hab.

  2. daniel sagt:

    Hi bitowl,

    love the tutorials btw, if i was to use the inputProcesser, would i still have to unproject the camera? or id there another way to do this.. basically i am using the mythology of MVC, so the gameScreen handle the input and extends inputprocessor. yet it does not have a cam, making unprojecting slightly more difficult.. or would you just move this part of controller class( touch ) to the renderer class that has a cam?

    hope this makes sense.

    Cheers

    • bitowl sagt:

      sorry, this comment has been eaten by my spam filter, so I just saw it.

      If you do not unproject the touch-coordinates, they are not in the same coordinate-system as your screenspace.
      So I would either do the mouse-handling in the same class as the rendering or pass the camera to your inputprocessor, so he can use it when unprojecting.

      I hope, I could help
      bitowl

  3. Fabian sagt:

    Hey bitowl,
    ich habe derzeit ein Problem beim zeichnen einer Bilddatei. Solange ich sie mittels draw auf den Bildschirm zeichne ist alles gut, male ich sie aber nach einem Event(TouchEvent), dreht sich das Bild um 180 Grad.
    Kannst du mir helfen?

    grüße Fabian!

    • bitowl sagt:

      Heyho,

      so ohne Quellcode ist das natürlich schwer. Wenn du mir ein Minimalbeispiel schicken kannst, kann ich da mal drüber gucken.
      Ansonsten findest du sicher im Forum oder IRC von libgdx Hilfe.

      bitowl

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.