Banner
{ Deutsch | English }
Sokoban

Sokoban − Misel



Quellcode


Vorbetrachtung:
  1. Level laden
  2. prüfen, wohin sich der Spieler bewegen kann
  3. wenn er sich nicht bewegen kann, dann stehen bleiben
  4. wenn eine Kiste im Weg ist
    • prüfen, ob die Kiste bewegt werden kann - wenn ja, Kiste verschieben
  5. Spieler bewegen
  6. wiederholen, bis sich alle Kisten in einem Ziel befinden.

Dieses Script lädt das Level. Die Levelnummer ist im Fensternamen abgelegt. Die Daten des Raumes werden extern in Maze-Files abgelegt, um das Spiel flexibler zu machen.
Um einen Raum zu erstellen, kann man entweder den Leveleditor verwenden oder einfach mit einem ASCII-Editor die Datei manuell erstellen, wenn man die Struktur dieser Dateien verstanden hat (siehe Ende dieses Dokumentes).
<SCRIPT type="text/javascript"> var level; if (window.name=="") level=1; else level=window.name; var level_string='<SCRIPT type="text/javascript" src="' + level + '.js"><\/SCRIPT>'; document.write(level_string); </SCRIPT>
Jetzt geht's richtig los ;)
<SCRIPT type="text/javascript">
Die Eigenschaften des Raumes. Die Variablen müssen hier deklariert werden, da sie global sind. Die Werte werden von den Funktionen im Maze-File zugewiesen.
var box_image=new Image; box_image.src="box.gif"; var player_image=new Image; player_image.src="player.gif"; var wall_image=new Image; wall_image.src="wall.gif"; var free_image=new Image; free_image.src="free.gif"; var target_image=new Image; target_image.src="target.gif"; var box_in_target_image=new Image; box_in_target_image.src="box_in_target.gif"; var player_x; var player_y; var num_targets; var copyright=""; var num_rows; var num_columns;
load_level_size() wird im Maze-File definiert. Es handelt sich dabei um eine Funktion, die die Größe eines Raumes setzt. Dies muss geschehen, bevor das Spielfeld initialisiert wird. Jedes Feld wird dabei auf 0 gesetzt und anschließend auf den korrekten Wert, der im Maze-File in der load_level()-Funktion hinterlegt ist.
load_level_size(); var field=new Array(); for (var i=0; i < num_rows; i++) { field[i]=new Array(); for (var j=0; j < num_rows; j++) { field[i][j]=0; } } load_level();
Variablen, die in jedem Spiel benötigt werden.
wieviele Kisten befinden sich bereits in einem Ziel
var box_in_target=0;
0-nicht zuende, 1-zuende
var game_status=0;
Anzahl der Bewegungen
var turns=0;
die Bildwechselfunktion
function Bildwechsel(Bildnr,Bildobjekt) { window.document.images[Bildnr].src = Bildobjekt.src; }
Hier werden Tastatureingaben registriert und die move_player()-Funktion mit den entsprechenden Parametern aufgerufen.
function Tastendruck(Druck) { if (document.all) k = window.event.keyCode; else k = Druck.which; if (k == 38 || k == 87) move_player(0,-1); if (k == 37 || k == 65) move_player(-1,0); if (k == 39 || k == 68) move_player(1,0); if (k == 40 || k == 83) move_player(0,1); }
Diese Funktion tauscht das Bild eines Feldes aus.
function change_image(y,x) { var image_number='f'+y+'_'+x; switch (field[y][x]) { case 0: Bildwechsel(image_number,free_image); break; case 10: Bildwechsel(image_number,target_image); break; case 20: Bildwechsel(image_number,box_image); break; case 30: Bildwechsel(image_number,box_in_target_image); break; default: if (field[y][x]>=100) Bildwechsel(image_number,player_image); break; } }
Diese Funktion bewegt den Spieler und macht alles, was danach passiert und alles, was damit sonst noch zu tun hat.
wenn das Spiel noch läuft...
if (game_status==0) {
erhöhe den Bewegungs-Zähler
turns++;
prüfe, ob sich die neue Spielerposition noch innerhalb des Raumes befindet
if ((player_y+y>-1)&&(player_x+x>-1)&&(player_y+y<num_rows)&&(player_x+x<num_columns)) {
prüfe den Wert des neues Feldes und handelt entsprechend
switch(field[player_y+y][player_x+x]) {
∗ leeres Feld, bewege den Spieler
case 0: field[player_y][player_x]-=100; change_image(player_y,player_x); player_x += x; player_y += y; field[player_y][player_x]+=100; change_image(player_y,player_x); break;
∗ Zielfeld, bewege den Spieler
case 10: field[player_y][player_x]-=100; change_image(player_y,player_x); player_x += x; player_y += y; field[player_y][player_x]+=100; change_image(player_y,player_x); break;
∗ Feld mit einer Kiste und
∗ Feld mit einer Kiste im Ziel
prüfe, ob die Kiste bewegt werden kann (d.h. ob sich dahinter ein leeres Feld befindet) und bewege die Kiste und den Spieler (oder auch nicht)
Wenn eine Kiste auf ein Ziel bewegt wird, wird box_in_target erhöht − wird eine Kiste aus einem Ziel herausbewegt entsprechend erniedrigt.
case 20: if ((field[player_y+2*y][player_x+2*x] != 1) && (field[player_y+2*y][player_x+2*x] != 20) && (field[player_y+2*y][player_x+2*x] != 30) && (player_y+2*y > -1) && (player_x+2*x > -1) && (player_y+2*y < num_rows) && (player_x+2*x < num_columns)) { field[player_y][player_x]-=100; change_image(player_y,player_x); player_x += x; player_y += y; field[player_y][player_x]+=80; change_image(player_y,player_x); field[player_y+y][player_x+x]+=20; change_image(player_y+y,player_x+x); if (field[player_y+y][player_x+x]==30) box_in_target++; } break; case 30: if ((field[player_y+2*y][player_x+2*x] != 1) && (field[player_y+2*y][player_x+2*x] != 20) && (field[player_y+2*y][player_x+2*x] != 30) && (player_y+2*y > -1) && (player_x+2*x > -1) && (player_y+2*y < num_rows) && (player_x+2*x < num_columns)) { field[player_y][player_x]-=100; change_image(player_y,player_x); player_x += x; player_y += y; field[player_y][player_x]+=80; change_image(player_y,player_x); field[player_y+y][player_x+x]+=20; change_image(player_y+y,player_x+x); if (field[player_y+y][player_x+x]!=30) box_in_target--; } break; }
Wenn alle Kisten im Ziel sind, ist das Spiel vorüber.
if (box_in_target == num_targets)game_status=1; } }
Wenn das Spiel gewonnen wurde, wird die Levelnummer erhöht und eine Nachricht ausgegeben.
if (game_status==1) { alert('You won the game in '+turns+' moves!'); level++; window.name=level; }} </SCRIPT>
Der folgende Teil gehört in den BODY.
<SCRIPT type="text/javascript">
um Tastatureingaben zu registrieren
document.onkeydown = Tastendruck;
baut den Raum
for (var i=0; i < num_rows; i++) { for (var j=0; j < num_columns; j++) { var temp = field[i][j]; switch (temp) { case 0: document.write('<IMG alt="f" src="'+free_image.src+'" name="f'+i+'_'+j+'">'); break; case 1: document.write('<IMG alt="w" src="'+wall_image.src+'" name="f'+i+'_'+j+'">'); break; case 10: document.write('<IMG alt="f" src="'+target_image.src+'" name="f'+i+'_'+j+'">'); break; case 20: document.write('<IMG alt="w" src="'+box_image.src+'" name="f'+i+'_'+j+'">'); break; case 30: document.write('<IMG alt="w" src="'+box_in_target_image.src+'" name="f'+i+'_'+j+'">'); box_in_target++; break; default: document.write('<IMG alt="f" src="'+free_image.src+'" name="f'+i+'_'+j+'">'); break; } } document.write('<BR>'); }
setzt den Spieler in den Raum
Bildwechsel('f'+player_y+'_'+player_x,player_image); </SCRIPT>
schreibt die Copyright-Notiz
<SCRIPT type="text/javascript"> document.write("<SMALL>"+copyright+"<\/SMALL>"); </SCRIPT>
Diese Buttons sind die "Mauskontrolle" des Spiels (speziell für Opera-Nutzer, da Opera die Keyboard-Eingabe nicht unterstützt).
<TABLE> <TR> <TD> </TD> <TD> <INPUT type="button" value="&uArr; " onclick="move_player(0,-1); "> </TD> <TD> </TD> </TR> <TR> <TD> <INPUT type="button" value="&lArr; " onclick="move_player(-1,0); "> </TD> <TD> <INPUT type="button" value="&dArr; " onclick="move_player(0,1); "> </TD> <TD> <INPUT type="button" value="&rArr; " onclick="move_player(1,0); "> </TD> </TR> </TABLE>
Hier kann man das Level auswählen. Wenn die Auswahl geändert wurde, wird der Fenstername angepasst und das Fenster neu geladen. Nach dem Refresh holt sich das Script den neuen Namen und lädt das entsprechende Level.
<FORM name="test" action="">Choose your Maze: <SELECT name="level" size="1" onChange="window.name=document.test.level.options[document.test.level.selectedIndex].text; location.reload(); "> <OPTION>1</OPTION> <OPTION>2</OPTION> . . . <OPTION>19</OPTION> <OPTION>20</OPTION> </SELECT> <BR> <BR> <INPUT type="button" name="text" value="New Game" onClick="location.reload(); "> </FORM>

Es folgt ein Beispiel für ein Maze-File.
Es besteht aus 2 Funktionen, die globale Variablen des Programms ändern. Die erste Funktion ist recht kurz, da sie nur die Dimensionen des Spielfeldes beinhaltet. Dies muss in einer Extra-Funktion erfolgen, da die Spielfeld-Daten erst geändert werden können, nachdem das Spielfeld selbst initialisiert wurde. Dazu wird aber die Größe benötigt.
function load_level_size() { num_rows=9; num_columns=9; }
Die zweite Funktion enthält weit mehr Daten.
function load_level() {
Die Spielerposition
player_x=4; player_y=4;
Die Spielfelddaten
  • 0 - leeres Feld
  • 1 - Wand
  • 10 - Ziel
  • 20 - Kiste
  • 30 - Kiste im Ziel
  • 100 - Spieler
  • 110 - Spieler im Ziel
field[0][0]=0; field[0][1]=0; field[0][2]=1; field[0][3]=1; field[0][4]=1; . . . field[8][5]=1; field[8][6]=1; field[8][7]=0; field[8][8]=0;
Die Anzahl der Ziele ist wichtig, um festzustellen, wann das Spiel vorüber ist.
num_targets=5;
Unter dem Spielfeld kann die folgende Nachricht dargestellt werden.
copyright="This is an example level"; }