Der Game Over Status in Browser-Spielen

Der Game Over Status in Browser-Spielen

 

Die Programmierung von Leben und Tod

 

Im Beitrag Objekte kollidieren lassen haben wir die Klasse Meteor erstellt und die Klasse Bullet mit einer Kollisions-Abfrage versehen, die ├╝berpr├╝ft ob ein Projektil mit einem Meteor-Objekt kollidiert. Der Spieler soll aber nicht nur reine Ziel├╝bungen meistern, sondern den Meteoren ausweichen, bevor er getroffen wird.

Der aktuelle Stand des Spiels l├Ąsst die Meteore nur ├╝ber unser Raumschiff hinweg fliegen. Da Meteore aber bekanntlich Dinge zerst├Âren die sie treffen, sollten wir das mithilfe einer Kollisions-Abfrage an unserem Raumschiff ber├╝cksichtigen. Dadurch bekommt der Spieler einen Anreiz die vorbei fliegenden Objekte nicht nur zu zerst├Âren, sondern diesen auch auszuweichen, wenn ein Offensiv-Angriff keine Option mehr darstellt.

Wir ben├Âtigen also eine Abfrage, ob Meteore in unserer Array myMeteors mit dem Raumschiff kollidieren und sollten auch testen ob unser Raumschiff zu viel Schaden abbekommen hat. Der Schaden wird durch die Anzahl der Leben visualisiert, die wir bereits im Beitrag Vorbereitung und Planung in unserer Klasse Spaceship erstellt und deklariert haben.

Dank der update()-Funktion unseres Raumschiffs k├Ânnen wir eine neue Funktion zur Timeline der Main-Loop hinzuf├╝gen und diese in unserem Schiff unterbringen. Die Funktion collision() wird unser Pr├╝f-Element, das mit einer Schleife durch die Positionen aller Meteore iteriert um eine m├Âgliche Kollision mithilfe von checkCollision() festzustellen. Sollte eine Kollision zwischen einem Meteor und dem Raumschiff stattfinden wird zuerst Leben vom Raumschiff abgezogen und der Meteor wird im Anschluss darauf zerst├Ârt, indem dessen Variable Destroyed einen bool'schen Wert true erh├Ąlt. Durch die Integration beider Algorithmen in der gleichen Funktion verhindern wir eine m├Âgliche Doppel-Kollision.

/* ... */
class Spaceship {
	constructor( args ) {
		/* ... */
	}
	update() {
		if( !GameOver ) {
			if( typeof myInput !== "undefined" ) {
				this.move();
				this.shoot();
				this.collision();
			}
			/* ... */
		}
	}
	move() {
		/* ... */
	}
	shoot() {
		/* ... */
	}
	collision() {
		
		for( var i=0; i < myMeteors.length; i++ )
			if( !myMeteors[ i ].Destroyed )
				if( checkCollsion( this, myMeteors[ i ] ) ) {
					
					this.Life -= 1;
					if( this.Life <= 0 )
						GameOver = true;
					
					myMeteors[ i ].Destroyed = true;
					
				}
		
	}
}
/* ... */

 

Unser Raumschiff kann nun zwar Schaden erleiden, kann aber immer noch nicht zerst├Ârt werden. Denn der Algorithmus f├╝r den Status GameOver wurde noch nicht in unserem Code implementiert.

F├╝r diesen Status ben├Âtigen wir eine Erweiterung der Renderer-Klasse. Sie wird zuk├╝nftig ├╝berpr├╝fen, ob das Raumschiff noch Leben besitzt und andernfalls eine Nachricht auf unsere Leinwand projizieren, die dem Spieler mitteilt, dass das Spiel beendet ist.

Die Ausgabe wird durch ein neues HTML-Element gesetzt, das eine eindeutige ID besitzt, damit wir noch ein paar spezielle CSS-Formatierungen vornehmen k├Ânnen. Ich habe f├╝r Dich eine Formatierung erstellt, die Du gerne f├╝r Dein eigenes Spiel verwenden kannst. Du kannst gerne die Typografie und Farben nach Belieben ├Ąndern.

var GameOver = false;
/* ... */
(function($) {
	/* ... */
	class Renderer {
		constructor() {
			/* ... */
		}
		update() {
			if( typeof this.Spaceship !== "undefined" ) {
				this.updateSpaceship();
				this.checkGameOver();
			}
			/* ... */
		}
		updateSpaceship() {
			/* ... */
		}
		updateBullets() {
			/* ... */
		}
		updateMeteors() {
			/* ... */
		}
		checkDestroyed() {
			/* ... */
		}
		checkGameOver() {
			
			if( GameOver && this.Spaceship.length ) {
				this.Spaceship.remove();
			}
			
		}
	}
})(jQuery);
#gameover {
	background-color: rgba( 0, 0, 0, .6 );
	color: #ffffff;
	position: absolute;
	height: 100%;
	width: 100%;
	left: 0;
	top: 0;
	z-index: 99;
	
	font-size: 50px;
	line-height: 100%;
	padding: 275px 0;
	text-align: center;
	font-family: monospace;
}

 

Verloren hat der Spieler, wenn seine 5 Leben von einem Meteor auf 0 dezimiert wurden. Leider hat der Spieler aber keine M├Âglichkeit anzusehen wie viele Leben sein Raumschiff noch besitzt. Lass uns das als n├Ąchstes ├Ąndern.

Unsere Oberfl├Ąche hat viel Platz um eine Benutzerfl├Ąche zu gestalten, in dem der Benutzer seine aktuellen Statistiken betrachten kann. Die Leben seines Schiffs sollten dazu geh├Âren. Es gibt viele M├Âglichkeiten die Leben auszugeben, eine Zahl oder ein Balken sind zwei davon. Ich habe mich daf├╝r entschieden die Anzahl an Leben als Herzen auszugeben. Daf├╝r habe ich eine Grafik vorbereitet, die Du gerne auch f├╝r dein Projekt verwenden kannst. Wenn Du m├Âchtest kannst Du gerne eine eigene Art ausdenken, wie Du die Leben des Raumschiffs darstellen m├Âchtest.

 

Die HTML-Elemente werden Absolut positioniert in unseren Main-Wrapper einf├╝gt. Das erreichen wir durch eine Grundformatierung per CSS, die das Hintergrundbild setzt, sowie die Gr├Â├če und weitere wichtige Formatierungen angibt. Du hast beim Downloaden der PNG-Datei bestimmt bemerkt, dass die 4 Bilder nicht in unterschiedlichen Dateien liegen, sondern als geb├╝ndeltes Spritesheet erstellt wurden. Wir verwenden f├╝r diesen Beitrag nur das erste Bild in der Reihe, werden uns aber in einem kommenden Beitrag mit den 3 anderen Bildern besch├Ąftigen.

Durch die Verwendung von einem Spritesheet sparen wir uns Dateivolumen und somit wertvolle Ladezeit beim Aufruf der Webseite. Wie bereits mit unserem Raumschiff im Beitrag Bewegung entwickeln k├Ânnen wir den Bildausschnitt des Spritesheets mit der Angabe der background-position ver├Ąndern. Durch die Limitierung des Bildausschnitts und der Eigenschaft overflow: hidden wird dadurch nur der Bereich dargestellt, der auf der Leinwand erscheinen darf.

.life {
	background-image: url( '/wp-content/themes/atomik_theme/scheme/post/game/games/SpaceShooter/img/powerups.png' );
	background-size: 100px 25px;
	background-repeat: no-repeat;
	background-position: 0px 0px;
	
	width: 25px;
	height: 25px;
	position: absolute;
	left: 0;
	top: 10px;
	z-index: 98;
}

 

 

Der Renderer wird sich darum k├╝mmern die Anzahl der Herzen immer gleich mit den Leben zu halten und an die richtige Stelle im GUI zu setzen. Dazu erweitern wir unsere Klasse erneut um die Funktion checkLifes(). In Ihr implementieren wir einen Algorithmus der pro Durchlauf feststellen soll, ob die Anzahl der Leben des Raumschiffs gleich der Herzen auf dem GUI ist. Andernfalls wird die Ausgabe der Leben erneuert, indem die Leben komplett entfernt und mit einer Schleife neu aufgebaut werden. Da die Herzen komplett erneuert werden k├Ânntest Du sogar ein Item erstellen, das die Leben des Raumschiffs wieder auff├╝llen kann. Das wird kein Bestandteil dieser Beitrags-Reihe, aber du solltest im Anschluss gen├╝gend Wissen haben, damit du dieses Item und noch mehr selbstst├Ąndig integrieren kannst.

class Renderer {
	constructor() {
		/* ... */
	}
	update() {
		if( typeof this.Spaceship !== "undefined" ) {
			this.updateSpaceship();
			this.checkGameOver();
			this.checkLifes();
		}
		/* ... */
	}
	updateSpaceship() {
		/* ... */
	}
	updateBullets() {
		/* ... */
	}
	updateMeteors() {
		/* ... */
	}
	checkDestroyed() {
		/* ... */
	}
	checkGameOver() {
		/* ... */
	}
	checkLifes() {
		
		var spaceshipLifes = mySpaceship.Life;
		var currentGUILifes = $( '#main_wrapper .life' ).length;
		if( spaceshipLifes != currentGUILifes ) {
			$( '#main_wrapper .life' ).remove();
			for( var i=0; i < spaceshipLifes; i++ ) {
				$( '#main_wrapper' ).append( '
' ); } } } }

 

Da unsere Meteore im letzten Beitrag ebenfalls Leben erhalten haben, die wir bisher aber noch nicht verwenden, werden wir jetzt unsere Klasse Bullet um einen kleinen Code-Schnipsel erweitern, der den Meteor nicht sofort zerst├Ârt, sondern erst Schw├Ącht bevor er eliminiert wird. Eine Abfrage, ob das Leben des Meteors kleiner oder gleich 0 betr├Ągt, wird nach der Subtraktion des Projektil-Schadens vom Leben durchgef├╝hrt.

class Bullet {
	constructor( args ) {
		/* ... */
	}
	
	update() {
		/* ... */
	}
	move() {
		/* ... */
	}
	collision() {
		
		if( !this.Destroyed )
			for( var i=0; i < myMeteors.length; i++ )
				if( !myMeteors[ i ].Destroyed )
					if( checkCollsion( this, myMeteors[ i ] ) ) {
						myBullets[ this.ID ].Destroyed = true;
						
						// Um den Schaden zu berechnen m├╝ssen wir nur das Leben des Meteors mit dem verursachenden Schaden des Projektils subtrahieren
						myMeteors[ i ].Life -= this.Damage;
						if( myMeteors[ i ].Life <= 0 ) {
							// Danach pr├╝fen wir ob das Leben des Meteors kleiner gleich 0 ist und setzen die Variable "Destroyed" erst dann auf "true"
							myMeteors[ i ].Destroyed = true;
						}
						
					}
		
	}
	
}

 

In vielen F├Ąllen m├Âchte man dem Spieler die M├Âglichkeit geben sich mit anderen zu messen, um einen Wett. Welche M├Âglichkeit ist daf├╝r besser geeignet als ein Score?

Mit Score bezeichnet man die Punkte, die der Spieler in einer Sitzung erlangt hat. Wir als Programmierer des Spiels m├╝ssen festlegen, wie viele Punkte der Spieler f├╝r welche Ereignisse erh├Ąlt. Unser SpaceShooter gibt schon fast vor, dass der Spieler Punkte erh├Ąlt, wenn er Meteore abschie├čt. Da wir zwei unterschiedliche Meteore haben k├Ânnen wir f├╝r diese 2 Typen auch unterschiedlich viele Punkte verteilen. Ich habe definiert, dass kleine Meteore 25 Punkte und gro├če Meteore 100 Punkte einbringen. Diese beiden Werte kannst Du aber gerne nach Belieben individualisieren.

F├╝r den Score ben├Âtigen wir zun├Ąchst eine globale Variable, die beim zerst├Âren des Meteors mit dem definierten Wert addiert wird. Das passiert in der Funktion collision() unseres Projektils.

var Score = 0;
/* ... */
(function($) {
	/* ... */
	class Bullet {
		constructor( args ) {
			/* ... */
		}
		
		update() {
			/* ... */
		}
		move() {
			/* ... */
		}
		collision() {
			
			if( !this.Destroyed )
				for( var i=0; i < myMeteors.length; i++ )
					if( !myMeteors[ i ].Destroyed )
						if( checkCollsion( this, myMeteors[ i ] ) ) {
							myBullets[ this.ID ].Destroyed = true;
							
							myMeteors[ i ].Life -= this.Damage;
							if( myMeteors[ i ].Life <= 0 ) {
								myMeteors[ i ].Destroyed = true;
								
								// Es wird gepr├╝ft, von welchem Typ der Meteor ist und dementsprechend wird der Score um 25 oder 100 Punkte erh├Âht 
								if( myMeteors[ i ].Type == "Big" )
									Score += 100;
								else 
									Score += 25;
							}
							
						}
			
		}
		
	}
	/* ... */
})(jQuery);

 

 

Au├čerdem soll der Renderer unsere Variable auf der GUI ausgeben, damit der Spieler seinen Score immer im ├ťberblick beh├Ąlt. Die update()-Funktion wird um einen neuen Aufruf der Funktion changeScore() erweitert, die den Score ├Ąndern soll, sobald die angezeigte Punktausgabe nicht mit der Variable ├╝bereinstimmt. jQuery besitzt bereits eine Funktion, mit der Inhalte von HTML-Elementen in der Laufzeit bearbeitet werden k├Ânnen. Das HTML-Element wird per ID selektiert und mit dem .html()-Befehl kann der Inhalt des Elements, also den Score des Spielers, eingef├╝gt werden.

class Renderer {
	constructor() {
		/* ... */
	}
	update() {
		/* ... */
	}
	updateSpaceship() {
		/* ... */
	}
	updateBullets() {
		/* ... */
	}
	updateMeteors() {
		/* ... */
	}
	checkDestroyed() {
		/* ... */
	}
	checkGameOver() {
		
		if( GameOver )
			if( $( '#spaceship' ).length ) {
				this.Spaceship.remove();
				$( '#main_wrapper' ).append( '
GameOver
Score: '+Score+'
' ); } } checkLifes() { /* ... */ } changeScore() { var $Score = $( '#main_wrapper #score' ); if( $Score.length ) { if( $Score.html() !== Score ) $Score.html( Score ); } else { $( '#main_wrapper' ).append( '
'+Score+'
' ); } } }

 

Zu guter Letzt k├Ânnen wir den Score noch einmal ausgeben lassen, sobald der Spieler besiegt wurde und es GameOver hei├čt. Der Score wird in ein HTML-Element gepackt und im Wrapper der GameOver-Nachricht angeh├Ąngt. Mit CSS k├Ânnen wir die Formatierung des Scores ein wenig ver├Ąndern um eine kleinere Schriftgr├Â├če zu erhalten, als der Text ÔÇ×Game OverÔÇť. Du hast nat├╝rlich wieder die M├Âglichkeit deine eigene Formatierung zu setzen, wenn Du das m├Âchtest.

#score {
	position: absolute;
	right: 25px;
	top: 10px;
	z-index: 98;
	
	font-size: 22px;
	color: #ffffff;
	font-weight: 600;
	font-family: monospace;
}
/* ... */
#gameover .score {
	font-size: 32px;
}

 

Nun m├╝ssen wir nur noch den Renderer ein klein wenig erweitern, indem wir in der Funktion checkGameOver() den Score in den String der html()-Funktion hinzuf├╝gen. Eine CSS-Klasse .score hilft dem Score seine CSS-Formatierung zu erhalten.

 

Mit diesen ├änderungen k├Ânnen sich unsere Spieler messen. Wenn Du Dich mit Datenbanken auskennen solltest kannst du die Scores pro Spieler sichern und einen Highscore entwickeln, der die besten Spieler neben Deinem Spiel ausgibt. Damit k├Ânnen sich die Spieler sogar auf deiner Webseite verewigen.

Der n├Ąchste Beitrag behandelt tempor├Ąre PowerUps, die es erm├Âglichen werden das Raumschiff f├╝r einen kurzen Zeitraum zu verbessern. Diese Beispiele kannst du verwenden um Deine eigenen PowerUps zu entwickeln, um den Spieler mit noch m├Ąchtigeren Boni zu belohnen.

 

Codepalm
Spieleprogrammierung f├╝r Einsteiger
Teil 8: Der Game Over Status in Browser-Spielen
 

Folge Codepalm und verpasse keine Neuigkeiten mehr

 

Das konnte dich auch interessieren

Schreibe einen Kommentar

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

 

Kommentare

Sei der Erste, der einen Kommentar erstellt!

 
Secured By miniOrange