Grundlagen der Spieleprogrammierung – K├╝nstliche Intelligenz

K├╝nstliche Intelligenz

 

Die Programmierung von intelligenten Gegnern

 

Ob Geister bei Pacman, Dr. Robotnik bei Sonic the Hedgehog oder Bowser bei Mario. Was w├Ąre ein Spiel ohne einen f├Ąhigen Gegner, der dem Akteur seine Grenzen zeigt? Ohne Gegenspieler oder Bosse m├╝sste sich das Genre unseres Spiels wohl eher an Games wie "Die Sims" oder "Harvest Moon" richten.

Da wir aber kein Spiel zum entspannen programmieren m├Âchten, sondern den Spielern Reflexe und Geschick abverlangen, ben├Âtigen wir einen Gegner der zu unserem Genre passt. Und welche Angreifer w├╝rden wohl besser passen als Alien-Raumschiffe, die mit allen Mitteln versuchen unser Raumschiff zu Weltraum-Schrott zu verarbeiten.

Legen wir los mit dem wohl wichtigsten Teil unseres Spiels: Der k├╝nstlichen Intelligenz!

Zugegeben ist die KI, die wir erstellen werden relativ Dumm. Wir werden eine neue Klasse f├╝r das Alien erstellen und ihm beibringen zu erkennen, wo sich unser Spieler befindet. Das Alien soll entscheiden k├Ânnen, ob es dem Spieler folgen oder fliehen soll. Zudem soll das Alien-Raumschiff Projektile abfeuern, wenn der Spieler direkt unter ihm positioniert ist.

Nichts desto trotz ist dieser Beitrag ein guter Einstieg in die k├╝nstliche Intelligenz, die Du absolvieren solltest, wenn Du noch nie eine KI entwickelt hast.

 

F├╝r das gegnerische Raumschiff habe ich ein Bild herausgesucht (passend zum Stil der Umgebung), dass Du gerne wie ├╝blich frei verwenden darfst. Kenney.nl w├╝rde sich bestimmt ├╝ber eine Verlinkung zu seiner Webseite freuen.

Wie schon f├╝r andere Objekte werden wir eine neue CSS-Klasse definieren, die das Raumschiff auf unserem Spielfeld darstellen wird. Dazu kannst Du bereits bestehende Vorlagen aus unserem CSS-Code entnehmen.

 

.alien {
	background-image: url( '/wp-content/themes/atomik_theme/scheme/post/game/games/SpaceShooter/img/enemyShip.png' );
	background-repeat: no-repeat;
	background-size: 49px 25px;
	background-position: 0px 0px;
	
	width: 49px; 
	height: 25px;
	
	position: absolute;
	left: 0px;
	top: 0px;
}

 

Auch die Deklaration des Alien-Arrays darf zum Beginn des JavaScript-Programms nicht fehlen, um die Variable in den globalen Namespace aufzunehmen. Diese Array kannst Du ebenfalls aus einer bestehenden Vorlage, wie zum Beispiel dem Meteor einfach kopieren und ├Ąndern. Achte bei der Namensgebung nur immer darauf, dass Du genau wei├čt, wobei es sich bei der Variable handelt. Eine gute Benennung der Variablen hilft Dir beim Editieren des Programms zu erkennen welchen Zweck Du mit der Variable erf├╝llen m├Âchtest.

// Wir platzieren unser neues Alien-Objekt in den globalen Namespace um von ├╝berall aus Zugriff zu bekommen
var myAliens;

[...]

(function($) {
	$( document ).ready( function() {
		myInput = new Input();
		myBullets = [];
		myMeteors = [];
		// Das Objekt wird wie gewohnt als Array neben den anderen deklariert
		myAliens = [];
		myPowerups = [];
		
		[...]
	});
})(jQuery);

 

Im Renderer wird nun einiges erweitert und umgeschrieben, um den Gegner auf der Oberfl├Ąche unseres Spiels platzieren zu k├Ânnen.

Zuerst setzen wir eine neue Abfrage zur "Destroyed"-Bedingung in der update()-Funktion, die ├╝berpr├╝fen soll ob das Alien schon abgeschossen wurde, oder ob es noch in unserer Array munter vor sich hin schwebt.

Eine Funktion updateAliens() wird dazu verwendet die Position der Gegner pro Durchgang unseres Spiels zu erneuern und gegebenenfalls einen neuen Gegner auf das Spielfeld zu platzieren.

Wie bereits bei den Meteoren oder den Projektilen ├╝berpr├╝fen wir in der Funktion checkDestroyed(), ob das Alien bereits das Zeitliche gesegnet hat und f├╝gen in diesem Fall das HTML-Attribut "data-destroyed" zum Tag unserer Struktur hinzu. Da unsere CSS-Struktur alle HTML-Elemente ausblendet, wenn dieses Tag auf "true" gesetzt ist wird das Raumschiff nicht weiter angezeigt.

[...]

(function($) {
	class Renderer {
		constructor() {
			[...]
		}
		
		update() {
			[...]
			
			// Hier wird die Bedingung der Zerst├Ârten Objekte durch unser Alien-Objekt erweitert
			if( typeof myBullets !== "undefined" && myBullets.length > 0
			||	typeof myMeteors !== "undefined" && myMeteors.length > 0
			||	typeof myAliens !== "undefined" && myAliens.length > 0 )
				this.checkDestroyed();
		}
		
		[...]
		
		// Die Funktion updatePowerups() wird dupliziert und f├╝r die Aliens angepasst.
		updateAliens() {
			
			for( var i=0; i < myAliens.length; i++ ) {
				var $Alien = $( '#alien_'+myAliens[ i ].ID );
				if( $Alien.length ) {
					if( myAliens[ i ].Position.Y <= Field.Height + 50 ) {
						$Alien.css({ 
							'top' : myAliens[ i ].Position.Y+'px',
							'left' : myAliens[ i ].Position.X+'px'
						});
					}
				}
				else {
					$( '#main_wrapper' ).append( '<div id="alien_'+myAliens[ i ].ID+'" class="alien" style="left: '+( myAliens[ i ].Position.X )+'px; top: '+( myAliens[ i ].Position.Y )+'px;"></div>' );
				}
			}
			
		}
		
		[...]
		
		// Die Erweiterung unserer checkDestroyed-Funktion sieht ein Duplikat einer der anderen Schleifen vor. Hier muss wieder nur das entsprechende Objekt zu "myAliens" ge├Ąndert werden
		checkDestroyed() {
			[...]
			
			for( var i=0; i < myAliens.length; i++ )
				if( typeof myAliens[ i ] !== "undefined" )
					if( typeof myAliens[ i ].Destroyed !== "undefined" && myAliens[ i ].Destroyed == true )
						$( '#alien_'+myAliens[ i ].ID ).attr( 'data-destroyed', 'true' );
					else if( typeof myAliens[ i ].Destroyed !== "undefined" && myAliens[ i ].Destroyed == false )
						$( '#alien_'+myAliens[ i ].ID ).attr( 'data-destroyed', 'false' );
			
			[...]
		}
		
		[...]
		
		// Die Kollision wird durch die Gegnerischen Projektile erweitert. Sofern kein Schild am Raumschiff installiert ist und die beiden Objekte kollidieren, erleidet das Schiff Schaden vom Projektil.
		collision() {
			[...]
			
			for( var i=0; i < myBullets.length; i++ )
				if( myBullets[ i ].Type == "Alien" )
				if( !myBullets[ i ].Destroyed )
					if( checkCollision( this, myBullets[ i ] ) ) {
						
						var shield_exists = false;
						for( var j=0; j < this.PowerUps.length; j++ ) 
							if( this.PowerUps[ j ].Type == "Shield" && this.PowerUps[ j ].Duration > 0 ) {
								this.PowerUps[ j ].Duration = 0;
								shield_exists = true;
								break;
							}
						
						if( !shield_exists ) {
							this.Life -= myBullets[ i ].Damage;
							if( this.Life <= 0 )
								GameOver = true;
						}
						
						myBullets[ i ].Life = 0;
						myBullets[ i ].Destroyed = true;
						
					}
			
			[...]
		}
		
	}
})(jQuery);

 

 

Die neue Klasse "Alien" beinhaltet wieder viele Strukturen aus unseren anderen Klassen. Der constructor() enth├Ąlt die Kontroll-Variable "Destroyed", eine ID zum ansteuern vom Renderer und anderen Algorithmen, sowie Koordinaten und Ma├če des Raumschiffs. Damit es nicht direkt beim ersten Treffer Zerst├Ârt wird setzen wir eine zuf├Ąllige Anzahl an Leben. Zudem ben├Âtigen wir den Schaden, den das Alien erteilen soll eine Geschwindigkeit und noch weitere wichtige Variablen, die Du in den Kommentaren des Quellcodes nachlesen kannst.

// F├╝r unsere neue Klasse "Alien" k├Ânnen wieder einige Strukturen aus unserer Klasse "Meteor" verwenden. Um Dir Zeit beim Programmieren zu sparen kannst Du Dir die Klasse kopieren und einfach die notwendigen ├änderungen vornehmen
class Alien {
	
	constructor( args ) {
		args = args || {};
		
		this.Destroyed = false;
		
		if( typeof args.ID !== "undefined" )
			this.ID = args.ID;
		else
			this.ID = myAliens.length;
		
		this.Position = { X: 0, Y: -30, W: 49, H: 25 }; // Ich habe das Raumschiff auf eine feste Breite und H├Âhe definiert. Falls Du ein anderes Bild f├╝r Dein Alien verwendest solltest Du die Breite und H├Âhe anpassen.
		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;
		}
		if( this.Position.X == 0 ) {
			var randomPositionX = Math.floor( ( Math.random() * Field.Width ) + 1 );
			this.Position.X = randomPositionX;
		}
		
		this.DirectionH = 0;
		
		if( typeof args.Life !== "undefined" )
			this.Life = args.Life;
		else
			this.Life = Math.floor( ( Math.random() * 15 ) + 5 ); // Sofern einem neuen Objekt keine maximalen Leben beim Deklarieren ├╝bergeben wurden soll ein zuf├Ąlliger Wert zwischen 5 und 20 gesetzt werden.
		
		if( typeof args.Speed !== "undefined" )
			this.Speed = args.Speed;
		else
			this.Speed = Math.floor( ( Math.random() * 3 ) + 3 ); // Wie beim setzen der Leben wird hier beim fehlen der Geschwindigkeit ein zuf├Ąlliger Wert zwischen 3 und 6 definiert
		
		if( typeof args.Damage !== "undefined" )
			this.Damage = args.Damage;
		else
			this.Damage = 1; // Beim Schaden solltest Du es nicht ├╝bertreiben, da wir 5 Leben definiert haben. Ein "H├Âllen-Modus" k├Ânnte den Alien-Schiffen einen Schaden von 5 oder h├Âher erteilen, um bei nur einem Treffer ein "GameOver" auszul├Âsen
		
		if( typeof args.maxShootTimer !== "undefined" )
			this.maxShootTimer = args.maxShootTimer;
		else 
			this.maxShootTimer = Math.floor( ( Math.random() * 90 ) + 30 ); // Hier wird eine Zuf├Ąllige Variable zwischen 30 und 120 definiert um einen langsamen oder schnellen Schuss-Intervall zu definieren
		this.shootTimer = 0;
		
		var randomPositionY = Math.floor( ( Math.random() * Field.Height/2 ) + 1 );
		this.YLine = randomPositionY;
		
		this.Status = "Follow"; // Der Initial-Status des Alien-Schiffs soll auf "Follow" stehen um den Spieler zu verfolgen. In einem weiteren Teilschritt wirst Du mehr dar├╝ber erfahren
	}
	
}

 

Ohne den Update-Prozess kann das Alien wie alle anderen Objekte keine Aktionen ausf├╝hren. Die Funktion update() wird bereits in der Hauptschleife aufgerufen. Als n├Ąchstes sollten wir uns darum k├╝mmern diese Funktion zu f├╝llen.

Die erste Bedingung sollte eine Pr├╝fung der Kontroll-Variable "Destroyed" sein, um die Funktion direkt abzubrechen, sofern das Schiff bereits zerst├Ârt wurde. Dadurch sparen wir uns wertvolle Performance.

Sofern das Alien-Schiff nicht zerst├Ârt wurde soll es unserem Algorithmus zur Zerst├Ârung unseres Raumschiffs folgen. Dazu splitten wir den Code in die Funktionen move() und shoot(), die wir als Erstes Element innerhalb der Destroyed-Bedingung aufrufen. Nach diesen beiden Funktionen erstellen wir einen weiteren Algorithmus, der das Alien je nach "Bedarf" dem Spieler folgen oder fl├╝chten soll. Damit das gegnerische Objekt ein wenig mehr Dynamik erh├Ąlt soll die Y-Koordinate zuf├Ąllig ge├Ąndert werden.

class Alien {
	[...]
	
	update() {
		
		// Eine Bedingung soll pr├╝fen, ob das Raumschiff nicht zerst├Ârt wurde und der Spiel-Status nicht "GameOver" ist
		if( !GameOver && !this.Destroyed ) {
			this.move();
			this.shoot();
			
			// Durch einen Timer k├Ânnen wir dem Alien-Schiff beibringen, dass er nur schie├čen darf, wenn der Timer auf "0" steht
			if( this.shootTimer > 0 ) this.shootTimer--;
			
			// Das Alien Raumschiff soll sich mit einer 1%igen Wahrscheinlichkeit nach oben, bzw. weiter nach unten platzieren
			var randomChangeY = Math.floor( ( Math.random() * 100 ) + 1 );
			if( randomChangeY <= 1 ) {
				var randomPositionY = Math.floor( ( Math.random() * Field.Height/2 ) + 1 );
				this.YLine = randomPositionY;
			}
			
			// Hier definieren wir eine variable Wahrscheinlichkeit. Das Ziel ist es, dass das Alien mehr zum Spieler fixiert ist. Es soll dennoch mit einer geringeren Wahrscheinlichkeit fliehen k├Ânnen
			var maxRand = 2000;
			if( this.Status == "Follow" ) maxRand = 3000;
			var randomChangeStatus = Math.floor( ( Math.random() * maxRand ) + 1 );
			if( randomChangeStatus <= 5 ) {
				if( this.Status == "Escape" ) this.Status = "Follow";
				else if( this.Status == "Follow" ) this.Status = "Escape";
			}
			
		}
		
	}
	
}

 

In der Funktion move() ben├Âtigen wir einmal den Status des Aliens (Flucht oder Folgen) und addieren oder subtrahieren die Geschwindigkeit je nach Status zur X-Koordinate des Gegners.

Da sich das Alien nicht au├čerhalb des Spielfelds bewegen soll, ben├Âtigen wir eine Bedingung, die die X-Koordinate ab einem bestimmten Punkt erfassen soll. Sofern diese Bedingung zutrifft soll die Geschwindigkeit nicht mehr zur Position addiert bzw. subtrahiert werden.

class Alien {
	[...]
	
	move() {
		
		// Falls sich das Alien-Schiff rechts vom Spieler befindet, soll es bei einer Flucht weiter nach rechts steuern. Beim Folgen soll es nach rechts steuern, wenn das Schiff sich links vom Spieler befindet
		if( ( this.Status == "Follow" && ( mySpaceship.Position.X ) > ( this.Position.X + this.Position.W/2 ) ) 
		||	( this.Status == "Escape" && ( mySpaceship.Position.X + mySpaceship.Position.W/2 ) < ( this.Position.X + this.Position.W/2 ) ) ) {
			if( this.Position.X + this.Position.W < Field.Width ) 
				this.Position.X += this.Speed;
		}
		// Genaues Gegenteil von Oben: Flucht (Links vom Schiff) -> nach links bewegen; Folgen (Rechts vom Schiff) -> nach links bewegen
		else if( ( this.Status == "Follow" && ( mySpaceship.Position.X + mySpaceship.Position.W ) < ( this.Position.X + this.Position.W/2 ) ) 
		||		 ( this.Status == "Escape" && ( mySpaceship.Position.X + mySpaceship.Position.W/2 ) > ( this.Position.X + this.Position.W/2 ) ) ) {
			if( this.Position.X > 0 )
				this.Position.X -= this.Speed;
		}
		
		// Sofern sich das Schiff nicht etwa (+-5 Pixel Abweichung) auf der Y-Koordinate befindet, die in der Funktion "update" gesetzt wurde, wird es entsprechend nach oben oder unten gesteuert
		if( this.Position.Y + 5 < this.YLine ) {
			this.Position.Y += this.Speed;
		}
		else if( this.Position.Y - 5 > this.YLine ) {
			this.Position.Y -= this.Speed;
		}
		
	}
	
}

 

Die Funktion shoot() soll dem Alien erm├Âglichen den Spieler mit Projektilen zu befeuern. Durch die Variable "randomShoot" stellen wir sicher, dass das Alien-Schiff nicht kontinuierlich feuert, sondern nur mit einer 5%-igen Wahrscheinlichkeit. Zudem muss die Abkling-Zeit "shootTimer" auf 0 abgelaufen sein.

Sofern ein Projektil im negativen Bereich (also au├čerhalb der sichtbaren Bereichs) liegt, wird dieses mit den neuen Daten bef├╝llt. Sozusagen recyclen wir Projektile, die nicht mehr verwendet werden. Dadurch sparen wir uns zus├Ątzlich Ressourcen und Performance.

class Alien {
	[...]
	
	shoot() {
		
		// Damit das Raumschiff nicht ununterbrochen Feuert soll eine zuf├Ąllige Variable steuern, ob ein Schuss erfolgen wird oder nicht. 
		var randomShoot = Math.floor( ( Math.random() * 100 ) + 1 );
		
		// Hier definiere ich eine 5%ige Chance, dass ein Projektil abgefeuert werden soll. Du kannst den Wert gerne bearbeiten, um eine h├Âhere oder niedrigere Chance zu setzen, dass das Alien feuert.
		if( randomShoot <= 5 ) {
			
			// Den shootTimer sollten wir auch beachten, damit das Alien-Raumschiff sich auch an ein paar Restriktionen h├Ąlt. Du kannst die Bedingung auch l├Âschen, wenn du keinen Timer f├╝r Deine Aliens m├Âchtest.
			if( this.shootTimer <= 0 ) {
				
				var bulletPosition = {
					X: this.Position.X + this.Position.W / 2,
					Y: this.Position.Y + this.Position.H
				};
				
				// Folgende Kontroll-Variable soll uns dabei helfen herauszufinden, ob bereits ein Projektil au├čerhalb des sichtbaren Bereichs liegt. Ist das der Fall, ├╝berschreibt das neue Projektil das nicht-sichtbare, andernfalls wird ein neues erstellt.
				var bullet_on_negative = false;
				for( var i=0; i < myBullets.length; i++ )
					if( myBullets[ i ].Position.Y < -20 ) {
						$( '#bullet_'+myBullets[ i ].ID ).remove();
						myBullets[ i ] = new Bullet({ ID: i, Type: "Alien", Position: bulletPosition });
						bullet_on_negative = true;
						break;
					}
				
				if( !bullet_on_negative )
					myBullets.push( new Bullet({ Type: "Alien", Position: bulletPosition }) );
				
				// Nachdem ein Projekt abgefeuert wurde soll sich der Timer wieder zur├╝cksetzen. Andernfalls bringt uns der Timer gar nichts.
				this.shootTimer = this.maxShootTimer;
				
			}
		}
		
	}
	
}

 

Ohne einen passenden Algorithmus werden die Aliens nie erstellt. Da wir bereits einen ├Ąhnlichen Code mit den Meteoriten und den Projektilen besitzen kann dieser einfach dupliziert und angepasst werden.

(function($) {
	[...]
	
	function mainLoop( timestamp ) {
		[...]
		
		// Der Zufall soll sich mit jedem neuen Punkt, den der Spieler bekommt ein wenig erweitern. "500" soll der kleinste Wert sein, der die Variable annehmen kann
		var randNewAlien = Math.floor( ( Math.random() * Math.max( 500, ( 3000 - Score*0.1 ) ) ) + 1 );
		if( randNewAlien <= 1 ) {
			// Sofern die Zufalls-Variable "1" betr├Ągt soll ein neues, gegnerisches Raumschiff erstellt werden
			newAlien();
		}
		
		// Wie bei den anderen Objekten wird die Funktion "update" von jedem Alien-Schiff aufgerufen
		for( var i=0; i < myAliens.length; i++ )
			myAliens[ i ].update();
		
		[...]
	}
	
	function newAlien() {
		
		// Hier verwenden wir den gleichen Algorithmus wie beim Projektil, um das Alien-Raumschiff zu erstellen
		var destroyed_aliens_exists = false;
		for( var i=0; i < myAliens.length; i++ )
			if( myAliens[ i ].Destroyed ) {
				myAliens[ i ] = new Alien({ ID: myAliens[ i ].ID });
				destroyed_aliens_exists = true;
				break;
			}
		if( !destroyed_aliens_exists )
			myAliens.push( new Alien() );
		
	}
	
	[...]
})(jQuery);

 

 

Die gegnerischen Raumschiffe sollten nun auf unserem Spielfeld erscheinen. Damit der Spieler getroffen wird und die Projektile in die richtige Richtung steuern m├╝ssen wir noch die Klasse Bullet ein wenig erweitern.

Das feindliche Projektil soll keine Meteoriten treffen k├Ânnen. Dieses Verhalten k├Ânnen wir einpflegen indem wir eine ├ťberpr├╝fung in der Funktion collision() hinterlegen, die kontrolliert ob das Projektil vom Typ "Player" ist. Alle anderen Typen fliegen einfach durch die Meteoriten hindurch.

[...]

(function($) {
	[...]
	
	class Bullet {
		constructor( args ) {
			[...]
			
			// Sofern dem neuen Objekt kein Typ ├╝bergeben wurde, soll der Standard "Player" sein, da unser Raumschiff immer existiert (Ausgenommen bei "GameOver"). Andernfalls wird der ├╝bergebene Typ gesetzt (Also auch "Alien")
			if( typeof args.Type !== "undefined" ) this.Type = args.Type;
			else this.Type = 'Player';
			
			if( typeof args.Damage !== "undefined" ) this.Damage = args.Damage;
			else if( this.Type == "Player" ) {
				this.Damage = 1;
				var damageMultiplier = 1;
				for( var i=0; i < mySpaceship.PowerUps.length; i++ ) {
					if( mySpaceship.PowerUps[i].Duration > 0 && mySpaceship.PowerUps[i].Type == "Power" )
						damageMultiplier += mySpaceship.PowerUps[i].Multiplier;
				}
				if( damageMultiplier > 1 ) {
					this.Damage *= damageMultiplier;
					this.PowerupShot = true;
				}
			}
			else 
				this.Damage = 1;
			
			[...]
			
			// Das Projektil soll in nachfolgendem Snippet zum Main-Wrapper hinzugef├╝gt werden. Hier wird nun auch zwischen "Player" und "Alien" unterschieden
			if( this.Position.X != -100 && this.Position.Y != -100 ) {
				if( this.Type == "Player" ) {
					$( '#main_wrapper' ).append( '<div class="bullet_shot" data-powerup="'+this.PowerupShot+'" data-type="'+this.Type.toLowerCase()+'" style="left: '+( this.Position.X - 13 )+'px; top: '+( this.Position.Y - 13 )+'px;"></div>' );
					$( '#main_wrapper .bullet_shot:last-of-type' ).delay( 1000 ).queue( function() { $(this).remove(); } );
				}
				else if( this.Type == "Alien" ) {
					$( '#main_wrapper' ).append( '<div class="bullet_shot" data-powerup="'+this.PowerupShot+'" data-type="'+this.Type.toLowerCase()+'" style="left: '+( this.Position.X - 13 )+'px; top: '+( this.Position.Y - 13 )+'px;"></div>' );
					$( '#main_wrapper .bullet_shot:last-of-type' ).delay( 1000 ).queue( function() { $(this).remove(); } );
				}
			}
			
		}
		
		update() {
			[...]
		}
		move() {
			
			// Die Funktion wird erweitert, damit Projektile, die vom Alien-Raumschiff abgefeuert wurden in die entgegengesetzte Richtung (Zum Spieler) navigieren kann
			if( this.Type == "Player" ) this.Position.Y -= this.Speed;
			else if( this.Type == "Alien" ) this.Position.Y += this.Speed;
			
		}
		collision() {
			
			if( !this.Destroyed ) {
				for( var i=0; i < myMeteors.length; i++ )
					// Die neue Bedingung wird gesetzt um ein Alien-Projektil an einem Meteor vorbeiziehen zu lassen
					if( this.Type == "Player" )
						if( !myMeteors[ i ].Destroyed )
							if( checkCollision( this, myMeteors[ i ] ) ) {
								myBullets[ this.ID ].Destroyed = true;
								
								myMeteors[ i ].Life -= this.Damage;
								if( myMeteors[ i ].Life <= 0 ) {
									if( myMeteors[ i ].Type == "Big" )
										Score += 100;
									else 
										Score += 25;
									
									myMeteors[ i ].Destroyed = true;
									newPowerup( i );
								}
								
							}
				
				// F├╝r folgenden Code-Block duplizieren wir die Kollisions-Abfrage des Meteors und passen Sie an, damit sie f├╝r unser Alien-Raumschiff gilt
				for( var i=0; i < myAliens.length; i++ )
					if( this.Type == "Player" )
						if( !myAliens[ i ].Destroyed )
							if( checkCollision( this, myAliens[ i ] ) ) {
								myBullets[ this.ID ].Destroyed = true;
								
								myAliens[ i ].Life -= this.Damage;
								if( myAliens[ i ].Life <= 0 ) {
									// Der Score soll nat├╝rlich wesentlich h├Âher sein, als bei Meteore
									Score += 250;
									myAliens[ i ].Destroyed = true;
								}
								
							}
				
			}
			
		}
		
	}
	
})(jQuery);

 

Nachdem Du diese Änderungen an Deinem Spiel vorgenommen hast sind wir nun an unser Ziel angelangt. Ab diesem Punkt hast Du ein vollwertiges SpaceShooter-Minispiel, dass Du nach Belieben Erweitern und Pflegen kannst.

Ich hoffe, dass Dir das Programmieren Spa├č gemacht hat und das Du Dein eigenes Spiel erfolgreich programmiert hast.

 

 

Du hast Fragen oder W├╝nsche? Hinterlasse einen Kommentar oder kontaktiere mich ├╝ber das Kontaktformular und ich helfe Dir gerne nach M├Âglichkeit weiter.

Ich freue mich darauf dein eigenes SpaceShooter Spiel zu sehen, dass Du programmiert hast.

 

Codepalm
Spieleprogrammierung f├╝r Einsteiger
Teil 10: K├╝nstliche Intelligenz

Abonniere die Fanpage von Codepalm und verpasse keine Beitr├Ąge mehr

Codepalm auf Facebook
 
 
 
 
Das k├Ânnte Dir gefallen:

Spieleprogrammierung f├╝r Anf├Ąnger

EUR 39,41

Zu Amazon Mehr erfahren

Spieleprogrammierung mit Android Studio

EUR 34,90

Zu Amazon Mehr erfahren

Spieleprogrammierung mit Cocoa und OpenGL

EUR 49,70

Zu Amazon Mehr erfahren

Kommentare

Sei der Erste, der einen Kommentar erstellt!

Schreibe einen Kommentar

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