Die Planung und Vorbereitung eines Browser-Spiels

Im letzten Beitrag haben wir zusammen die Eingabe für unser Spiel erstellt, um unserem Programmcode beizubringen welche Tasten wir verwenden werden. Um die Eingaben zu verarbeiten benötigen wir einen steuerbaren Charakter, der sich in unserer Spiel-Umgebung befindet. In diesem Beitrag kümmern wir uns um die Planung des Spiels, sowie die Erstellung eines bewegbaren Objekts in unserer Spielumgebung.

Die beiden Grund-Voraussetzungen Main-Loop und die Eingabe unseres Spiels wurden bereits fertiggestellt. Nun solltest Du eine Vorstellung haben was für ein Spiel Du erstellen möchtest. Viele Entwickler haben schon viel früher eine Idee von Ihrem Wunsch-Ergebnis und planen auch schon vor der ersten Zeile Code, damit sie eine Struktur für das Spiel erarbeiten können und Ihre Gedanken schriftlich zu Papier bringen. Im Prozess des Entwickelns können natürlich manche Algorithmen nicht in das Spiel aufgenommen werden, die in der Planung noch als wichtig erachtet wurden. Das sich Spiel-Funktionen grundlegend verändern kann zusätzlich in der Entstehung vorkommen. Auch während des Erstellens kann der Entwickler auf verschiedene Besonderheiten aufmerksam werden, die er bei der Planung nicht berücksichtigen konnte, da sich bestimmte Verhaltensmuster erst beim Testen bemerkbar machen.

Für das Grundspiel übernehme ich die Planung für Dich. Während du die Code-Schnipsel umsetzt kannst du gerne Feinheiten, wie die Bewegungsgeschwindigkeit oder Zufallszahlen justieren. Ich erkläre Dir an den passenden Stellen welche Variablen Du verändern kannst, damit das Spiel deinen eigenen Vorstellungen entspricht. Nach der Fertigstellung dieses Tutorials kannst Du Dein Spiel auch weiter Anpassen, indem du andere Grafiken für die Objekte verwendest oder mehr Gegenstände oder Gegner erstellst.

Ein beliebtes Genre unter vielen Gamern ist der Space Shooter. Der Spieler braucht eine gute Auffassungsgabe und eine schnelle Reaktion um in diesem Spiel punkten zu können. In unserem Tutorial werden wir genau so ein Spiel erstellen, da Anfänger einfach in die Spiele-Entwicklung schnuppern und alle wichtigen Themen der Programmierung angeschnitten werden können.

Überlegen wir was wir für einen Space Shooter alles benötigen. Unser Spieler braucht ein steuerbares Raumschiff mit dem er Gegner und Gefahren zerstören oder umfliegen kann. Um ein Hindernis zu zerstören benötigen wir eine Art Angriff den das Raumschiff für den Spieler ausführen wird. In unserem Beispiel wird das ein Projektil sein, dass der Spieler mit der Leertaste abfeuern kann. Da wir uns im Weltraum befinden sind wohl die besten Hindernisse Meteore, die ziellos durch das All fliegen. Zu guter Letzt benötigen wir intelligentes Leben, das versuchen wird unser Raumschiff mit verschiedenen Angriffen zu eliminieren.

In JavaScript können wir aus den geplanten Typen unsere Blaupausen erstellen, die verschiedene Eigenschaften und Fähigkeiten besitzen, wie schon im letzten Schritt unsere Eingabe-Klasse. Bringen wir unsere geplanten Typen zu Papier und definieren, was für Fähigkeiten diese benötigen:

  • Die Klasse "Spaceship"
    Das Raumschiff wird die Einheit, die der Spieler steuern kann. Durch das betätigen der Pfeil-Tasten kann der Spieler das Raumschiff nach links und rechts bewegen, sowie beschleunigen und verlangsamen. Mit der Leertaste soll ein Projektil abgefeuert werden, das Meteoren oder Alien-Schiffe treffen und zerstören kann.
  • Die Klasse "Renderer"
    Um unsere Objekte auf dem Bildschirm darstellen zu lassen verwenden wir einen sogenannten Renderer, der die Ausgabe steuert. Sobald ein Objekt auf dem Bildschirm erscheint, verschwindet oder sich bewegt greift diese Klasse auf verschiedene Funktionen zu, die die visuelle Darstellung im Browser beeinflusst. Ohne diese Klasse wird sich der Spieler schwer tun sein Raumschiff oder die Hindernisse zu sehen.
  • Die Klasse "Bullet"
    Ein "Bullet" ist das Projektil, dass das Raumschiff oder das Alien-Schiff abfeuern kann. Einmal abgefeuert folgt das Projektil eine bestimmte Richtung und soll erst stoppen wenn eine Kollision stattfindet, oder es sich außerhalb des sichtbaren Bereichs gelangt. Sobald es mit einem Meteor, einem Alien oder mit dem Raumschiff des Spielers kollidiert erleidet das getroffene Objekt Schaden und kann dadurch zerstört werden. In diesem Fall wird auch das Projektil aus der Spielumgebung entfernt.
  • Die Klasse "Meteor"
    Ein Meteor soll zufällig oben im nicht sichtbaren Bereich des Spielfelds erscheinen und sich in eine zufällige Richtung nach unten bewegen. Sobald der Meteor mit dem Raumschiff kollidiert erleidet das Schiff Schaden und der Meteor wird dadurch zerstört. Der Spieler hat die Möglichkeit den Meteor mit Projektilen zu beschießen und somit zu zerstören, bevor er das Ende unserer Spiele-Landschaft erreicht oder unser Raumschiff trifft.
  • Die Klasse "PowerUp"
    Mit den PowerUps soll unser Raumschiff temporär stärker werden. Es wird 3 verschiedene Arten von PowerUps geben, die zufällig erscheinen, wenn ein Objekt zerstört wird. PowerUps können sich wie Projektile oder Meteore vom Spielbereich entfernen. Somit hat der Spieler nur begrenzt Zeit sich eines dieser nützlichen Items unter den Nagel zu reißen.
  • Die Klasse "Alien"
    Das Alien hat ähnliche Funktionen, wie das Raumschiff. Es wird durch eine kleine künstliche Intelligenz gesteuert, die das Raumschiff des Spielers verfolgen und beschießen soll. Wie beim Meteor soll der Spieler die Möglichkeit haben das Alien-Schiff zu beschießen und somit zu zerstören.

Verschiedene Algorithmen werden wir nicht nur für ein bestimmtes Objekt benötigen, sondern für mehrere Bereiche im Spiel. Deshalb benötigen wir neben den oben aufgeführten Klassen noch globale Funktionen die uns helfen werden eine Kollision abzufragen oder neue Objekte zu erstellen.

Du merkst, dass auch ein Minispiel einen gewissen Planungsaufwand vor dem Erstellen des Quellcodes benötigt. Ich empfehle Dir kein Spiel zu erstellen, bevor die Grundfunktionen nicht definiert und schriftlich verfasst wurden. Denn sobald du mehrere Tage hintereinander an deinem Spiel entwickelst kann es Dir passieren, dass du Algorithmen vergisst, oder nicht mehr weißt welches Ziel Du mit Deinem Spiel verfolgst. Eine Dokumentation kann dir auch viele Vorteile bringen, in der du immer die tagesaktuellen Codes notierst und erklärst. Damit solltest du dich in deinem Programm noch auskennen, auch wenn du mehrere Tage oder Wochen nicht mehr daran gearbeitet hast.

Unser Raumschiff wartet darauf von uns erstellt zu werden, also lassen wir es nicht mehr länger warten.

Das Raumschiff

Wie bereits unsere Klasse Input benötigt unser Raumschiff einen Namen, mit dem wir ein Objekt daraus generieren können. Ich verwende sehr gerne englische Begriffe für meine Variablen, Funktionen und Klassen da diese ohne Umlaute auskommen, die in einem Programmcode nicht verwendet werden können. Die Klasse des Raumschiffs nenne ich also Spaceship.

Das Raumschiff definiert beim Deklarieren eines Objekts einige wichtige Variablen, die wir im Spielgeschehen verwenden werden. Um den Standort des Raumschiffs zu definieren benötigen wir ein Objekt Position das die Variablen X, Y, W und H enthält und die Variablen DirectionH. X und Y sind unsere Koordinaten in Pixeln, W und H gibt die Höhe und die Breite in Pixel an um später eine valide Kollision zu generieren. Die DirectionH-Variable verwenden wir zur Kontrolle, um festzustellen in welche Richtung das Raumschiff gesteuert wird, um es nach links oder nach rechts zu neigen.

Des Weiteren benötigen wir eine Variable für die Bewegungsgeschwindigkeit (Speed), für die Anzahl an Leben (Life), den Angriffsschaden der Projektile (Damage), sowie Kontroll-Variablen wie Destroyed und ID. Es wird auch noch mehr Variablen hinzukommen, aber dazu später mehr.

class Spaceship {
	constructor( args ) {
		args = args || {};
		
		this.Position = { X: 0, Y: 0, W: 49.5, H: 37.5 };
		if( typeof args.Position !== "undefined" ) {
			if( typeof args.Position.X !== "undefined" )
				this.Position.X = args.Position.X;
			if( typeof args.Position.Y !== "undefined" )
				this.Position.Y = args.Position.Y;
			if( typeof args.Position.W !== "undefined" )
				this.Position.W = args.Position.W;
			if( typeof args.Position.H !== "undefined" )
				this.Position.H = args.Position.H;
		}
		this.DirectionH = 0;
		
		// Die folgenden Variablen werden in kommenden Beiträgen näher erläutert
		if( typeof args.Life !== "undefined" )
			this.Life = args.Life; 
		else this.Life = 5;
		
		if( typeof args.Speed !== "undefined" )
			this.Speed = args.Speed;
		else this.Speed = 1;
		
		if( typeof args.Damage !== "undefined" )
			this.Damage = args.Damage;
		else this.Damage = 1;
	}
	update() {
		// Die Update-Funktion wird verwendet, um die Funktionen move() und shoot() auszuführen. Die Abfrage, ob die jeweilige Taste getätigt wurde wird von der Funktion selbst ausgeführt. Diese Funktion wird in der Main-Loop des Spiels aufgerufen, da diese Funktion kontinuierlich ausgeführt werden soll
	}
	move() {
		// Hier werden wir die notwendigen Algorithmen zum steuern des Schiffs eintragen
	}
	shoot() {
		// Hier werden wir die Algorithmen hinterlegen um ein Projektil zu erstellen
	}
}

Unsere Blaupause wurde erstellt und kann nun deklariert werden. Dazu erstellen wir uns zunächst eine globale Variable mySpaceship um in der jQuery- Funktion $(document).ready() das Raumschiff-Objekt zu erstellen, sobald die Webseite vollständig geladen wurde. Dadurch können wir auf unser Objekt des Raumschiffs von überall aus zugreifen.

var myInput;
var mySpaceship;
(function($) {
	$( document ).ready( function() {
		myInput = new Input();
		mySpaceship = new Spaceship(); // In den folgenden Beiträgen werden wir die Position des Raumschiffs mit Parametern verändern
		mainLoop();
	});
	function mainLoop() {
		var progress = timestamp - lastRender;
		lastRender = timestamp;
		
		if( typeof myInput !== "undefined" ) renderInput();
		if( typeof mySpaceship !== "undefined" ) mySpaceship.update();
		
		window.requestAnimationFrame( mainLoop );
	}
})(jQuery);

Damit die Funktion move() unser Raumschiff bewegen kann müssen wir in unserer Main-Loop kontinuierlich die Funktion update() des Schiffs aufrufen. Mit jedem Durchgang der Loop wird unser Raumschiff somit um die gesetzte Bewegungsgeschwindigkeit (Speed) in die gewählte Richtung bewegt, falls der Spieler eine der festgelegten Pfeiltasten betätigt. Zudem wird die Funktion shoot() unseres Raumschiffs aufgerufen, welche überprüft ob die Leertaste zum Abfeuern eines Projektils gedrückt wird.

Die Bedingungen können wir mit den Variablen unseres Objekts myInput bestücken. In ihr haben wir bereits alle Tasten hinterlegt, ob diese gedrückt werden oder nicht. Auch der Rückgabewert ist ein Boolean-Wert, deshalb brauchen wir nicht mehr auf einen Vergleichs-Operator zurückgreifen.

class Spaceship {
	constructor( args ) {
		// Siehe oben
	}
	update() {
		if( typeof myInput !== "undefined" ) {
			this.move();
			this.shoot();
		}
	}
	move() {
		if( myInput.Arrows.Left ) { /* Fliege nach links */ }
		if( myInput.Arrows.Right ) { /* Fliege nach rechts */ }
		if( myInput.Arrows.Up ) { /* Beschleunige das Raumschiff */ }
		if( myInput.Arrows.Down ) { /* Verlangsame das Raumschiff */ }
	}
	shoot() {
		if( myInput.Space ) { /* Feuere ein Projektil ab */ }
	}
}
 

Im folgenden Beitrag werden wir die Spielfläche mit HTML und CSS gestalten und für die Objekte in unserer JavaScript-Datei vorbereiten.

 

Codepalm
Spieleprogrammierung für Einsteiger
Teil 3: Ein Spiel Vorbereiten und Planen