Banner
{ Deutsch | English }
Snake

Snake − LX



Quellcode


Für dieses Spiel muss auch ein wenig CSS beachtet werden. Das Spiel bezieht sich auf Hintergrundfarben von Tabellenzellen, also wenn diese Farben im StyleSheet geändert werden, dann müssen auch entsprechende Werte im Skript selbst angepasst werden.
Im CSS-Teil des Skripts wird die Hintergrundfarbe des Schlangenbehälters und die Größe einer Zelle bestimmt:
<STYLE type="text/css"> td { height:15px; width:15px; background-color:#87AC3B; } </STYLE>
Eine Zelle ist also 15×15 Pixel groß. Jetzt zum eigentlichen Skript. Hier werden zuerst wieder die globalen Variablen deklariert. Eine Zählvariable
<SCRIPT type="text/javascript"> var i;
die Richtung, in die die Schlange kriecht
Richtung = '+1';
eine Variable, um einen weiteren Tastendruck zu blockieren
var block = 0;
ob das Spiel vorbei ist oder nicht
var zuEnde = 0;
eine aktuell zu bearbeitende Zelle
var Zelle;
Zähler für den Countdown
var Countdown = 3;
für eine Frucht
var Leckerli;
das aktuelle Level
var Level = 0;
Anzahl der noch zu sammelnden Früchte
var Fruechte;
weitere Variablen
var blah, fnord = false;
die Schlange
var Snake = new Array();
der Kopf der Schlange
var Kopf;
Die folgende Funktion wird aufgerufen, wenn eine Taste betätigt wurde. Sie ändert die Richtung wenn eine bestimmte Cursortaste gedrückt wird.
function Tastendruck(Druck) { if (document.all) k = window.event.keyCode; else k = Druck.which; if (k == 37 && !block && Richtung != '+1') { Richtung = '-1'; block = 1; } if (k == 38 && !block && Richtung != '+20') { Richtung = '-20'; block = 1; } if (k == 39 && !block && Richtung != '-1') { Richtung = '+1'; block = 1; } if (k == 40 && !block && Richtung != '-20') { Richtung = '+20'; block = 1; } }
Es folgen ein paar Arrays, die Bilder beinhalten, die zu bestimmten Zeiten im Spielfeld dargestellt werden. Dazu gehören die Zahlen des Countdowns (C1 bis C3), der Smiley wenn ein Level geschafft wurde, der traurige Smiley wenn das Spiel verloren ist und 2 weitere Smileybilder, aus denen ein animierter jubelnder Smiley generiert wird, wenn das Spiel entgültig geschafft ist. Die Werte der Arrays lasse ich hier weg, sie können im Quelltext des Spiels nachgelesen oder selbst erstellt werden.
var C3 = [...]; var C2 = [...]; var C1 = [...]; var Smiley = [...]; var Frowny = [...]; var Jubel1 = [...]; var Jubel2 = [...];
Anschließend folgen die Arrays der jeweiligen Levels.
var Wall0 = []; var noFruit0 = []; var Wall1 = [...]; var noFruit1 = [...]; var Wall2 = [...]; var noFruit2 = [...]; . . . var Wall13 = [...]; var noFruit13 = [...]; var Wall14 = [...]; var noFruit14 = [...];
Die folgende Funktion wird aufgerufen, wenn ein neues Spiel gestartet wird.
function Start() {
Als erstes wird der Neues Spiel-Button versteckt und die Nummer des aktuellen Levels auf die Seite geschrieben. Weiterhin wird block auf 1 gesetzt, wodurch verhindert wird, dass weitere Tastenanschläge registriert werden.
document.getElementById('nG').style.visibility = 'hidden'; document.getElementById('Level').firstChild.nodeValue = 'Level ' + (Level + 1); block = 1;
Wenn die Countdown-Variable einen Wert beinhaltet und gleichzeitig fnord==0 ist (d.h. wenn keine Pause ist), so läuft der Countdown. Das Bild wird gelöscht (der Aufruf von reset()), die aktuelle Zahl des Countdowns wird auf den Bildschirm geschrieben und die Countdown-Variable um einen Wert erniedrigt. Anschließend wird Start() erneut nach 1 Sekunde aufgerufen.
if (Countdown && !fnord) { reset(); for (i = 0; i < eval('C' + Countdown).length; i++) { Zelle = 'Zelle' + eval('C' + Countdown)[i]; document.getElementById(Zelle).style.backgroundColor = 'black'; } Countdown--; setTimeout("Start()", 1000); }
Ist der Countdown hingegen bei 0 angelangt, so wird das eigentliche Level gestartet. Dazu wird ebenfalls der Bildschirm gelöscht und die Zahl der zu sammelnden Früchte auf 10 gesetzt.
else if (!fnord) { reset(); Fruechte = 10; document.getElementById('frucht').value = Fruechte;
Der Snake[]-Array wird gelöscht und die Position der Schlange und ihres Kopfes im neuen Spielfeld in die dazugehörigen Variablen geschrieben.
while (Snake[0]) Snake.pop(); Snake.push(198, 199, 200, 201, 202, 203, 204, 205, 206); Kopf = 206;
Die Tastensperre wird wieder aufgehoben, die Richtung der Schlange nach dem Start festgelegt eine Frucht generiert (mit Happen()), die Hindernisse des aktuellen Levels gezeichnet (mit Hindernisse()), der Pause-Button versteckt und die Verlaufsfunktion aufgerufen.
block = 0; zuEnde = 0; Richtung = '+1'; Happen(); Hindernisse(); document.getElementById('pause').style.visibility = 'hidden'; Verlauf(); } }
Die Funktion Pause() dient dazu, zwischen den Levels das Spiel anzuhalten. Zu diesem Zweck dient die Variable fnord, auf die hin in der bereits betrachteten Funktion Start() geprüft wird. Ist diese Variable gleich 1, so wird das Spiel angehalten, ist sie 0, so läuft es weiter. Dementsprechend wird der Pause-Button in dieser Funktion auch beschriftet. Der Countdown wird nach jeder Betätigung des Pause-Buttons ebenfalls wieder auf 3 initialisiert.
function Pause() { fnord = !fnord; if (fnord) document.getElementById('pause').value = "Weiter"; else document.getElementById('pause').value = "Pause"; Countdown = 3; Start(); }
Mit Happen() wird eine Frucht generiert. Da das Spielfeld 220 Zellen hat, wird zu diesem Zweck eine Zufallszahl zwischen 0 und 219 erstellt. Anschließend wird kontrolliert, ob in der Zelle dieser Zufallszahl entweder ein Teil des Schlangenkörpers, eine Wand oder ein Bereich liegt, in dem keine Frucht positioniert werden darf (weil dieser Bereich mit der Schlange nicht erreichbar wäre). Trifft einer dieser Fälle ein, so wird die Funktion erneut aufgerufen, wenn nicht, dann wird die Zelle der Frucht rot gefärbt.
function Happen() { Leckerli = Math.floor((Math.random() * 1000) % 220); for (i = 0; i < Snake.length; i++) { if (Leckerli == Snake[i]) Happen(); } for (i = 0; i < eval('Wall' + Level).length; i++) { if (Leckerli == eval('Wall' + Level)[i]) Happen(); } for (i = 0; i < eval('noFruit' + Level).length; i++) { if (Leckerli == eval('noFruit' + Level)[i]) Happen(); } document.getElementById('Zelle' + Leckerli).style.backgroundColor = 'red'; }
Hindernisse() färbt die Zellen des Wall[]-Arrays grau ein.
function Hindernisse() { for (i = 0; i < eval('Wall' + Level).length; i++) { document.getElementById('Zelle' + eval('Wall' + Level)[i]).style.backgroundColor = '#444444'; } }
Kommen wir zum Herz des Spiels: Der Verlaufsfunktion. Diese Funktion generiert den Takt des Spiels. Sie sorgt dafür, dass die Schlange immer in Bewegung ist und hier werden auch die wichtigsten Ereignisse geprüft.
function Verlauf() {
Zuerst wird bestimmt, wo der Kopf der Schlange sich nach dem nächsten Takt befinden wird. Dazu wird die Variable Richtung zu Kopf addiert. Anschließend wird geprüft, ob sich der Kopf dann noch innerhalb des Spielfeldes befindet, oder ob eine Wand berührt wurde. Es folgt die Prüfung, ob die Schlange sich dabei selbst anbeißt und abschließend noch der Check, ob auch kein Hindernis (Wand) angefressen wird.
Kopf = eval(Kopf + Richtung); if (Kopf < 0 || Kopf > 219 || (!(Kopf%20) && Richtung == '+1') || (!((Kopf+1)%20) && Richtung == '-1')) zuEnde = 1; for (i = 1; i < Snake.length; i++) { if (Kopf == Snake[i]) zuEnde = 1; } for (i = 0; i < eval('Wall' + Level).length; i++) { if (Kopf == eval('Wall' + Level)[i]) zuEnde = 1; }
Treten all diese Fälle nicht ein, so wird geprüft, ob die Schlange die Frucht soeben gegessen hat. Falls nicht, so wird das erste Element des Snake[]-Arrays entfernt und der Kopf als letztes Element angefügt (die Schlange kriecht sozusagen ein Feld nach vorn). Falls doch eine Frucht gefressen wurde, so wird die Anzahl der noch zu sammelnden Früchte um 1 erniedrigt und lediglich der Kopf an Snake[] angefügt ohne ein Element zu entfernen (die Schlange wächst ja mit jeder gefressenen Frucht). Abschließend wird natürlich eine neue Frucht erzeugt.
if (!zuEnde) { if (Kopf != Leckerli) { blah = Snake.shift(); Snake.push(Kopf); } else { Fruechte--; document.getElementById('frucht').value = Fruechte; Snake.push(Kopf); Happen(); }
Nun muss die Schlange natürlich auch dargestellt werden...
Schlange_malen();
Nun wird festgestellt, ob noch weitere Früchte zu sammeln sind. Wenn ja, dann wird die Tastensperre aufgehoben für den nächsten Zug und die Funktion nach 300 Millisekunden wieder aufgerufen (die Schlange vollführt also etwas mehr als 3 Bewegungen pro Sekunde).
if (Fruechte) { block = 0; setTimeout("Verlauf()",300); }
Sind hingegen schon alle benötigten Früchte eingesammelt, so wird die Smileygrafik ausgegeben, die Tastatursperre eingeschaltet und die Levelnummer erhöht. Ist das 15. Level bereits geschafft, so wird die Jubelanimation gestartet, wenn nicht wird der Countdown wieder auf 3 initialisiert und die Start()-Funktion aufgerufen.
else { Smiley_malen(); block = 1; Level++; if (Level == 15) setTimeout("Jubel(1)",500); else { Countdown = 3; if (!fnord) setTimeout("Start()",2000); } } }
Dieser Zweig tritt in Kraft, falls doch die Schlange die Wand, ein Hindernis oder sich selbst angefressen hat. In diesem Fall wird der traurige Smiley ausgegeben und der Countdown für ein eventuelles nächstes Spiel wieder zurückgesetzt.
else { setTimeout("Frowny_malen()",500); Countdown = 3; } }
Die folgende Funktion stellt den animierten Jubel-Smiley dar. Dieser besteht aus 2 Einzelbildern, die in einem Abstand von 150 Millisekunden ausgetauscht werden.
function Jubel(x) { document.getElementById('nG').style.visibility = 'visible'; reset(); if (x) { for (i = 0; i < Jubel1.length; i++) { Zelle = 'Zelle' + Jubel1[i]; document.getElementById(Zelle).style.backgroundColor = 'black'; } } else { for (i = 0; i < Jubel2.length; i++) { Zelle = 'Zelle' + Jubel2[i]; document.getElementById(Zelle).style.backgroundColor = 'black'; } } x++; x %= 2; setTimeout("Jubel("+x+")",150); }
Diese Funktion stellt den Smiley auf dem Bildschirm dar. Außerdem wird zuEnde auf 0 gesetzt, da das aktuelle Level damit abgeschlossen ist.
function Smiley_malen() { reset(); document.getElementById('pause').style.visibility = 'visible'; for (i = 0; i < Smiley.length; i++) { Zelle = 'Zelle' + Smiley[i]; document.getElementById(Zelle).style.backgroundColor = 'black'; } zuEnde = 0; }
Hier wird ein trauriger Smiley dargestellt.
function Frowny_malen() { reset(); document.getElementById('nG').style.visibility = 'visible'; for (i = 0; i < Frowny.length; i++) { Zelle = 'Zelle' + Frowny[i]; document.getElementById(Zelle).style.backgroundColor = 'black'; } }
Die folgende Funktion zeichnet die Schlange. Dazu wird zuerst die Zelle des ersten Elements des Snake[]-Arrays grün gefärbt. Anschließend wird der Rest des Arrays geschwärzt.
function Schlange_malen() { Zelle = 'Zelle' + Snake[0]; document.getElementById(Zelle).style.backgroundColor = '#87AC3B'; for (i = 1; i < Snake.length; i++) { Zelle = 'Zelle' + Snake[i]; document.getElementById(Zelle).style.backgroundColor = 'black'; } }
reset() löscht den Bildschirminhalt (durch färben der Zellen mit der grünen Hintergrundfarbe).
function reset() { for (i = 0; i < 220; i++) { Zelle = 'Zelle' + i; document.getElementById(Zelle).style.backgroundColor = '#87AC3B'; } } </SCRIPT>
Zum HTML-Teil des Spiels. Zuoberst wird ein Textelement benötigt, in welches die aktuelle Levelnummer geschrieben wird. Entscheidend hier ist weniger, welches Element dafür gewählt wird, als die Angabe id="Level", über die der Zugriff aus den oberen Funktionen erfolgt. Hier habe ich den H2-Tag gewählt, da die Vorformatierung ungefähr dem entspricht, wie die Schrift dargestellt werden sollte:
<H2 id="Level">Level 1</H2>
Ebenfalls im BODY erfolgt die Abfrage, ob eine Taste gedrückt wurde...
<SCRIPT type="text/javascript"> document.onkeydown = Tastendruck;
... und es wird hier das Spielfeld per Skript erzeugt. Dies könnte auch hardcoded werden, allerdings halte ich diese Variante für eleganter. Dazu werden zuerst Variablen für Zeilen und Spalten und eine Zählvariable erstellt, mit deren Hilfe dann der nötige HTML-Code erzeugt wird. Wichtig an dieser Stelle ist wieder die id="", die für jede Zelle einen bestimmten Wert haben muss, der sich aus dem Wort "Zelle" und einer fortlaufenden Nummer zusammensetzt.
var Zeilen, Spalten; var Index = 0; document.write('<TABLE cellspacing="0" cellpadding="0">'); for (Zeilen = 0; Zeilen &lt; 11; Zeilen++) { document.write('<TR>'); for (Spalten = 0; Spalten &lt; 20; Spalten++) { document.write('<TD id="Zelle' + Index + '"><\/TD>'); Index++; } document.write('<\/TR>'); } document.write('<\/TABLE>'); </SCRIPT>
Weiterhin wird ein Eingabefeld benötigt, in welchem angezeigt wird, wieviele Früchte der Spieler im aktuellen Level noch einsammeln muss.
<INPUT type="text" id="frucht" value="0" size="3">
Abschließend folgen noch 2 Buttons: einer mit dem ein Spiel gestartet wird und ein Button, mit dem das Spiel zwischen 2 Levels angehalten werden kann. Die Angabe this.blur() im Event-Handler sorgt dafür, dass der Button nachdem er gedrückt wurde, nicht mehr auswählbar ist. Daher kann er, auch wenn er per CSS vorher versteckt wurde, auch nicht mehr mit der Leertaste betätigt werden.
<INPUT type="button" id="nG" value="New Game" onClick="this.blur(); Level=0; Start(); "> <INPUT type="button" id="pause" value="Pause" onClick="this.blur(); Pause(); ">