JavaScript/Tutorials/Spiele/Autorennen

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche


Wir entwickeln einen Konstruktor

In diesem Abschnitt wollen wir ein kleines Autorennspiel programmieren. In diesem Rennen sollen verschiedene Fahrzeuge gegeneinander antreten und eine Strecke von 100 km fahren.

Jeweils für ein Zeitintervall von 6 Minuten kann der Spieler eine Geschwindigkeit vorgeben.

Wenn ein Spieler nun einfach mit möglichst großer Geschwindigkeiten davon eilt, kann es passieren, dass er bald ohne Benzin stehen bleibt. Die Wagen bekommen nämlich zu Beginn des Rennens nur eine bestimmte Benzinmenge zur Verfügung gestellt, z. B. 20 Liter; und der Benzinverbrauch wächst mit steigender Geschwindigkeit überproportional!

Die Rennwagen wollen wir als Objekte darstellen. Natürlich gibt es in JavaScript keinen vorgefertigten Konstruktor dafür; deswegen müssen wir selbst einen solchen entwickeln.

Zunächst überlegen wir: Welche Eigenschaften soll unser Rennwagen-Objekt besitzen?

Wir beschränken uns hier auf die folgenden:

Name Bedeutung
s gefahrene Strecke in km
v Geschwindigkeit in km/h
name Name des Wagens
benzin Benzinvorrat in Liter

Nun gilt es, sich Gedanken zu den Methoden zu machen. Wir wollen hier mit zwei Methoden auskommen:

Name Bedeutung
einSchritt berechnet, wie sich die Eigenschaften s, v und benzin in einem Zeitintervall von 6 min verändern
status liefert als Rückgabewert eine Zeichenkette mit einem Statusbericht über s, v und benzin

Unser Konstruktor sieht so aus:

function Auto(startpos, benzin, startgeschwindigkeit)
{
this.s = startpos;
this.benzin = benzin;
this.v = startgeschwindigkeit;
this.name = prompt("Gib den Namen Deines Autos ein:", "");
this.step = einSchritt;
this.status = status;
}


Konstruktoren sind aufgebaut wie gewöhnliche Funktionen. In unserem Fall besitzt der Konstruktor 3 Parameter: Die Position beim Start, die Benzinmenge beim Start und die Geschwindigkeit beim Start. Was geschieht nun durch die einzelnen Anweisungen im Funktionsrumpf?

Die erste Anweisung lautet

this.s = startpos;

Hierdurch wird der Eigenschaft s des Objekts der Parameterwert startpos übergeben. Auffällig ist hier das Schlüsselwort this. Dies steht stellvertretend für den Namen der Instanz, die in unserem allgemeinen Bauplan nicht benutzt werden kann; schließlich werden wir mit diesem Bauplan i. A. verschiedene Instanzen mit verschiedenen Namen erzeugen.

Auf die gleiche Weise werden die Parameterwerte für Benzinmenge und Startgeschwindigkeit an die entsprechenden Eigenschaften unseres Auto-Objekts übergeben. Durch die Anweisung

var meinAuto = new Auto(0, 20, 0);

wird also eine Instanz unserer Auto-Klasse erzeugt, bei der die Eigenschaften s, benzin und v die Werte 0, 20 und 0 zugewiesen bekommen. Damit ist unsere Instanz aber noch nicht fertig. Durch die Anweisung …

this.name = prompt("Gib den Namen Deines Autos ein:", "");

wird zunächst nach einem Namen für das Auto gefragt und die Eingabe unter der Eigenschaft name gespeichert.

Eigenschaften lassen sich also schon bei der Erzeugung einer Instanz festlegen; sie können dabei auf zwei verschiedene Art eingegeben werden: Entweder legt man sie während der Programmierung als Parameter des Konstruktors fest, oder der Konstruktor bietet die Möglichkeit, sie während des Programmablaufs über Eingabefenster einzugeben.

Nach den Eigenschaften müssen jetzt noch die Methoden in unserem Bauplan erfasst werden. Dabei werden die Anweisungen, welche von einer solchen Methode ausgeführt werden, in eine Funktion ausgelagert. Im Fall der Methode step haben wir diese Funktion einSchritt genannt. Im Konstruktor wird jetzt nur vermerkt, dass diese Funktion einSchritt ausgeführt werden soll, wenn die Methode step aufgerufen wird:

this.step = einSchritt;

Man kann die Namen für die Funktionen und Methoden gleich wählen, so wie wir es im Fall der Methode status auch gemacht haben. Beachte, dass bei dieser Zuweisung nicht die üblichen Funktionsklammern stehen dürfen.

Nun müssen wir nur noch die beiden Funktionen einSchritt und status programmieren. Wie schon beim Konstrukor selbst benutzen wir wieder das Schlüsselwort this, um auf die Objekteigenschaften zuzugreifen.

function einSchritt()
{
var text = "neue Geschwindigkeit von ";
this.v = prompt(text + this.name, this.v);
var benzinverbrauch = Math.pow(this.v,2)*0.00015;
this.benzin = this.benzin - benzinverbrauch;
if (this.benzin < 0)
{
this.benzin = 0;
this.v = 0;
alert(this.name + " hat kein Benzin mehr")
}
this.s = this.s + this.v * 0.1;
}

Die Methode einSchritt fragt zunächst einen Geschwindigkeitswert für das nächste Zeitintervall ab. Der Benzinverbrauch für das nächste Zeitintervall wird daraus über die Formel berechnet. Durch das Quadrat bei der Geschwindigkeit steigt der Benzinverbrauch überproportional; wenn die Geschwindigkeit verdoppelt wird, vervierfacht sich der Benzinverbrauch.

Dieser Benzinverbrauch wird nun von der aktuellen Benzinmenge subtrahiert. Sollte dann die so berechnete neue Benzinmenge unter 0 sein, werden Benzinmenge und Geschwindigkeit auf 0 gesetzt und es wird eine entsprechende Warnung ausgegeben.

Zuletzt wird die neue Position berechnet, dazu wird die in diesem Zeitintervall zurückgelegte Strecke über die Formel Geschwindigkeit mal Zeit berechnet und zu der letzten Position addiert. Bedenke dabei, dass das Zeitintervall hier immer 6 min = 0,1 h ist.

Die Status-Funktion ist einfacher; deswegen wird sie hier ohne weitere Kommentare angegeben.

function status()
{
var a = "Status von "+ this.name + ": \n";
a = a + "v= " + this.v + " // " ;
a = a + "s= " + this.s + " // "
a = a + "Bezinmenge = " + this.benzin + "\n";
return a;
}

Nun gilt es den Konstruktor auszutesten. Wir entwerfen ein HTML-Dokument mit zwei Schaltflächen und einem Textbereich. Mit der ersten Schaltfläche rufen wir die Funktion erzeugen auf:

function erzeugen()
{
meinAuto = new Auto(0, 20, 0);
rform.ergebnisse.value = meinAuto.status();
}
  1. Abb. 4: Hier verlangt der Konstruktor nach einer Benutzereingabe.
  2. Abb. 5: Statusbericht nach dem Erzeugen des Objekts
  3. Abb. 6: Statusbericht nach dem ersten Zeitabschnitt

Nach dem Anklicken der Erzeugen-Schaltfläche erscheint zuerst eine Eingabeaufforderung wie in Abb. 4, dann wird der erste Statusbericht geliefert (Abb. 5). Beachte, dass vor der Variablen meinAuto das Schlüsselwort var fehlt; die Variable meinAuto wird außerhalb der Funktion als globalen Variable deklariert und kann somit auch von anderen Funktionen aus angesprochen werden.

Die zweite Schaltfläche soll die Methode einSchritt aufrufen und anschließend wieder einen Statusbericht liefern:

function naechsterZyklus()
{
meinAuto.step();
rform.ergebnisse.value = meinAuto.status();
}

Nach dem Betätigen der zweiten Schaltfläche wirst du aufgefordert, die Geschwindigkeit für das nächste Zeitintervall einzugeben. Wenn du z. B. den Wert 180 eingibst, erhältst du einen Statusbericht wie in Abb. 6. Man sieht sofort, dass man bei dem Tempo die 100 km - Strecke nicht bis zum Ende durchhalten kann, der Benzinverbrauch ist zu hoch. Bei den nächsten Schritten sollte man also die Geschwindigkeit etwas kleiner wählen. An dieser Stelle sehen wir schon, wie praktisch der Umgang mit Objekten sein kann – wenn der Konstruktor erst einmal vorliegt. Um das Auto-Objekt zu erzeugen oder um es fahren zu lassen, brauchen wir nur jeweils zwei Anweisungen zu geben und erhalten obendrein noch unseren Statusbericht.

mehrere Autos

Noch deutlicher wird der Vorteil der objektorientierten Programmierung, wenn wir ein Rennen mit einer größeren Anzahl von Autos fahren wollen. In diesem Fall sehen wir die einzelnen Autos als Elemente eines Arrays mit dem Namen autos an, welches wir als globale Variable deklarieren. Die gewünschte Anzahl der Autos muss natürlich vor dem Start in einem Textfeld eingegeben werden.

var autos;
function anDenStart()
{
var bericht = "Bericht: \n \n";
var anzahl = rform.anzahl.value;
autos = new Array(anzahl);
for (var k=0; k<anzahl; k++)
{
autos[k] = new Auto(0, 20, 0);
bericht = bericht + autos[k].status() + "\n";
}
rform.ergebnisse.value=bericht;
}
function naechsterZyklus()
{
var bericht = "Bericht: \n \n";
for (var k=0; k<autos.length; k++)
{
autos[k].step();
bericht = bericht + autos[k].status() + "\n";
}
rform.ergebnisse.value = bericht;
}
Aufgabe:
  1. Erläutere die Funktion anDenStart().
  2. Vervollständige das Autorennprogramm und teste es aus. Versuche Rennstrategien zu entwickeln, die zum Sieg führen. Gibt es eine optimale Geschwindigkeit? An welchen Stellen lässt das Programm sich noch verbessern?
  3. Untersuche den Quelltext des Igel-Programms aus dem Kapitel „Wir lassen zeichnen“.
Überlege, wie man mit mehreren Igeln gleichzeitig arbeiten kann. Teste deine Überlegungen aus.

Snippets

Beispiel
var auto = {
    marke: "Audi",
    baujahr: 2007,
    hupen: function () { alert('tuuut')},
    motor: {zylinder: 4, hubraum: 1.99}
};

Die Eigenschaft motor ist ein (Unter-)Objekt mit eigenen Eigenschaften.