Benutzereingaben Verarbeiten und Speichern

Nachdem nun unsere Main-Loop fertiggestellt wurde können wir uns mit dem E der Spiele-Entwicklung auseinandersetzen. Vielleicht bist du schon einmal über das EVA-Prinzip im Zusammenhang der Programmierung gestolpert. EVA steht für die Eingabe, die Verarbeitung und die Ausgabe in einem Programm. Unser Spiel ist wie jedes andere Programm keine Ausnahme dieses kausalen Gesetzes der Programmierung: Auf jede Ursache folgt auch eine Wirkung.

Die Ursache in einem Spiel bildet die Eingabe des Spielers und wirkt sich somit auf das Spielgeschehen aus. Wenn der Spieler nach „Oben“ drückt soll sein Avatar auch dieser Richtung folgen. Um eine solche Eingabe des Benutzers zu ermöglichen benötigen wir einen Algorithmus, der sich an unsere Main-Loop aus dem letzten Beitrag Die Loop andockt. Dazu erstellen wir uns eine JavaScript-Klasse, die unsere Eingaben überprüft und in Variablen sichert.

Eine JavaScript-Klasse ist unsere Blaupause, um ein Objekt zu erstellen, das mit verschiedenen Werten und Funktionen gefüllt wird. Damit der Browser versteht, dass wir eine Klasse erstellen und dass diese einzigartig ist, benötigt die Klasse den Begriff class und einen Namen, den wir ihr geben. Jede dieser Blaupausen, die wir erstellen hat einen sogenannten Konstruktor, der beim Erstellen eines Objekts die notwendigen Variablen deklariert und gegebenenfalls Werte zu diesen Variablen hinzufügt. Der Konstruktor (constructor) wird aufgerufen, wenn ein neues Objekt aus der Klasse erstellt wird. Im Parameter des Konstruktors können Werte für Variablen übergeben werden, die das zu erstellende Objekt erhalten soll.

Durch eine Zeile "args = args || {}" geben wir unserem Code zu verstehen, dass die Variable args ein leeres Objekt sein muss, sofern kein Parameter zum neuen Objekt hinzugefügt wurde.

class Input {
	constructor( args ) {
		args = args || {};
		
		this.Arrows = {};
		this.Arrows.Left = false;
		this.Arrows.Right = false;
		this.Arrows.Up = false;
		this.Arrows.Down = false;
		
		this.Space = false;
	}
}

Damit unsere Klasse Input überprüfen kann, ob eine Taste vom Spieler betätigt wurde benötigen wir einen Algorithmus der zum einen bestimmte Tasten erkennen kann und einen bool'schen Wert in einer Variable unserer Klasse hinterlegt. Zum anderen müssen wir das Standard-Verhalten der Taste entfernen, damit der Benutzer beispielsweise nicht analog nach unten scrollt, wenn er die „Pfeil nach unten“-Taste betätigt während er die Spielfigur nach unten laufen lassen möchte.

Unser Keyboard verwendet eine eigene Zeichen-Codierung die für alle Tastaturen festgelegt wurde, damit wir als Programmierer über einen Zahlencode feststellen können, welche Tasten vom Benutzer betätigt werden. Dadurch können wir unseren beiden Funktionen Bedingungen aufstellen, die je nach Fall andere Auswirkungen auf das Spielgeschehen haben können. Eine jQuery-Funktion kann als Listener fungieren, damit das „herunterpressen“ und „loslassen“ von bestimmten Tasten in Variablen festgelegt werden kann, ohne diese Funktion jedes Mal über die Main-Loop aufrufen zu müssen.

class Input {
	constructor( args ) {
		/* ... */
		
		this.init();
	}
	init() {
		this.trackInputs();
	}
	trackInputs() {
		var _this = this;
		$( document ).on( 'keydown', function( e ) {
			if( e.which == 37 ) _this.Arrows.Left = true;
			if( e.which == 39 ) _this.Arrows.Right = true;
			if( e.which == 38 ) _this.Arrows.Up = true;
			if( e.which == 40 ) _this.Arrows.Down = true;
			if( e.which == 32 ) _this.Space = true;
			if( $.inArray( e.which, [ 32, 37, 38, 39, 40 ] ) !== -1 )
				e.preventDefault();
		});
		$( document ).on( 'keyup', function( e ) {
			if( e.which == 37 ) _this.Arrows.Left = false;
			if( e.which == 39 ) _this.Arrows.Right = false;
			if( e.which == 38 ) _this.Arrows.Up = false;
			if( e.which == 40 ) _this.Arrows.Down = false;
			if( e.which == 32 ) _this.Space = false;
			if( $.inArray( e.which, [ 32, 37, 38, 39, 40 ] ) !== -1 )
				e.preventDefault();
		});
	}
}

Es ist wichtig bereits zu Beginn der Programmierung festzustellen, ob ein Fehler gemacht wurde. Die Eingabe der festgelegten Tasten können wir in einem HTML-Element ausgeben lassen. Da wir noch kein Objekt aus unserer Blaupause erstellt haben müssen wir das nachholen. jQuery liefert uns eine initiale Funktion, die aufgerufen wird sobald das Dokument vollständig geladen ist. Zuerst müssen wir eine globale Variable myInput schaffen, die unser Objekt tragen wird. Danach deklarieren wir das zu erstellende Objekt mit dem Operator new. Auf diese Weise kann unser Objekt myInput von jeder Stelle aus aufgerufen und die Tasteneingaben des Benutzers ausgewertet werden. In unserer Main-Loop können wir eine Test-Funktion setzen, die die Ausgaben formatiert in einem Element mit der ID „test_output“ darstellt.

var myInput;
(function($) {
	$( document ).ready( function() {
		myInput = new Input();
		mainLoop();
	});
	function mainLoop( timestamp ) {
		var progress = timestamp - lastRender;
		lastRender = timestamp;
		
		if( typeof myInput !== "undefined" ) renderInput();
		
		window.requestAnimationFrame( mainLoop );
	}
	function renderInput() {
		var result = '';
		
		result += '
UP: '+ myInput.Arrows.Up +'
'; result += '
DOWN: '+ myInput.Arrows.Down +'
'; result += '
LEFT: '+ myInput.Arrows.Left +'
'; result += '
RIGHT: '+ myInput.Arrows.Right +'
'; result += '
SPACE: '+ myInput.Space +'
'; $( '#test_output' ).html( result ); } class Input { // Siehe oben } })(jQuery);

Unsere Klasse ist nun fertiggestellt und kann getestet werden. Sollten die Werte nicht von false auf true springen, wenn wir eine der Ziel-Tasten drücken wurde ein Fehler im Code gemacht. Du kannst in der Entwickler-Konsole (F12) nachsehen, ob ein Fehler erkannt wurde. JavaScript-Validatoren können dir beim Debuggen eines Fehlers auch eine große Hilfe leisten. Google hilft dir bei der Suche nach einem Validator.

In unserer Funktion trackInputs() haben wir den Pfeiltasten, sowie der Leertaste eine Variable zugewiesen, die je nach Status seinen bool'schen Wert verändert. Durch das betätigen der Tasten kannst du sehen, wie sich die Werte verändern.

 

Da wir die Ausgabe in unserem finalen Spiel nicht benötigen werden kannst du die Funktion renderInput() und dessen Aufruf in der Main-Loop löschen.

Im nächsten Beitrag bereiten wir eine Klasse vor und planen gemeinsam, um das Spiel in eine richtige Struktur zu bringen. Hier verraten wir dir auch, was wir zusammen programmieren werden.

 

Codepalm
Spieleprogrammierung für Einsteiger
Teil 2: Spielsteuerung durch Tastatur-Eingabe