4.6 |
Pacmans
Innenleben |
[ Top ]
|
Um das
Spiel endlich zum Laufen zu bewegen, müssen wir nun die
PACMAN.BAS fertig schreiben (ich weiß, einige Leser haben
schon richtig drauf gewartet *g*). |
Fangen wir
mit PACMAN_INIT an, da diese Prozedur als erste aufgerufen
wird. Die nun folgenden Routinen müssen natürlich in das
Modul PACMAN.BAS geschrieben werden... |
|
 |
Sub
PACMAN_INIT ()
|
 |
|
Als erstes
setzten wir den Pfad. Zu Testzwecken, also während der
Entwicklung, können Sie hier einen absoluten Pfad setzen,
damit Sie nicht mit dem VB-Verzeichnis in Konflikt kommen,
was bei Version 3.0 öfters der Fall ist. Wenn Sie das Spiel
später endgültig kompilieren, sollten Sie die erste Zeile
entfernen und vor der zweiten das Hochkomma wegnehmen. |
|
 |
Pfad$
= "E:\DATEN\DOCS\VBKURS\SPIELE\PACMAN\"
'Pfad$ = CurDir$
If Right$(Pfad$, 1) <>
"\" Then
Pfad$ = Pfad$ + "\"
|
 |
|
Nun setzten
wir MAX_DIFF auf drei. Das ist der Wert für die Genauigkeit
bei der Kollisionsabfrage. Eine gewisse Toleranz ist
wichtig, um die Steuerung zu vereinfachen. |
|
 |
MAX_DIFF = 3
|
 |
|
Anschließend
laden wir alle nötigen Bilder. |
|
 |
B1 =
RES_NEW(): RES_LOAD B1, Pfad$ + "BIT\PACMAN.BMP"
M1 = RES_NEW(): RES_LOAD M1, Pfad$ +
"MASKE\PACMAN.BMP"
B2 = RES_NEW(): RES_LOAD B2, Pfad$ +
"BIT\MONSTER.BMP"
M2 = RES_NEW(): RES_LOAD M2, Pfad$ +
"MASKE\MONSTER.BMP"
B3 = RES_NEW(): RES_LOAD B3, Pfad$ +
"BIT\OBJECTS.BMP"
M3 = RES_NEW(): RES_LOAD M3, Pfad$ +
"MASKE\OBJECTS.BMP"
B4 = RES_NEW(): RES_LOAD B4, Pfad$ +
"BIT\WALLS.BMP"
M4 = RES_NEW(): RES_LOAD M4, Pfad$ +
"MASKE\WALLS.BMP"
|
 |
|
Jetzt wird
die Variable Anzahl auf 0 gesetzt. Danach zerlegen wir die
Bilder in das Sprite-Datenfeld, wie wir es bei der Demo
gemacht haben. |
|
 |
Anzahl = 0
SPRITE_SET_MESH Spr(), Anzahl, 12, 8,
20, 20, _
RESSOURCEN.RES(B1), RESSOURCEN.RES(M1)
SPRITE_SET_MESH Spr(), Anzahl, 12, 6,
20, 20, _
RESSOURCEN.RES(B2), RESSOURCEN.RES(M2)
SPRITE_SET_MESH Spr(), Anzahl, 12, 9,
20, 20, _
RESSOURCEN.RES(B3), RESSOURCEN.RES(M3)
SPRITE_SET_MESH Spr(), Anzahl, 12, 4,
20, 20, _
RESSOURCEN.RES(B4), RESSOURCEN.RES(M4)
End Sub
|
 |
|
Nun kommen
wir zu PACMAN_LOAD. Diese Prozedur hat sowohl die Aufgabe
den Hintergrund zu aktualisieren und die Werte zurückzusetzen,
als auch das Level zu laden. Daher wird die Routine etwas länger... |
|
 |
Sub PACMAN_LOAD (Datei$, BACK$)
|
 |
|
Am Anfang
blocken wir wieder alle auftretenden Fehler ab. Vielleicht
sollten Sie diese Zeile während der Testphase als Kommentar
einfügen (ein Hochkomma davor setzen), damit Sie Fehler
bemerken. |
|
 |
On
Local Error Resume Next
|
 |
|
Als nächste
Laden wir das Hintergrundbild, was in BACK$ übergeben wird: |
|
 |
MAIN.BACK.Picture = LoadPicture(Pfad$ + "ELSE\"
+ BACK$)
|
 |
|
Nun wird die
Variable COUNT_DOTS (der Zähler für die essbaren Punkte)
auf Null gesetzt, und das Level geöffnet. Beim ersten
Durchgang werden nur die Zeilen gezählt, und die Datei wird
geschlossen. Anschließend wird die Breite des Levels in
Zeichen mit der Länge einer Zeile errechnet (Spalten =
Len(A$)). |
|
 |
COUNT_DOTS = 0
Open Pfad$ + Datei$ For
Input As #1
Do
Line Input #1, A$
Zeilen = Zeilen
+ 1
Loop Until EOF(1)
Close #1
Spalten = Len(A$)
|
 |
|
Da wir die
Anzahl der Zeilen und Spalten kennen, können wir nun das
Spielfeld initialisieren. Da unsere Routine FIELD_SIZE
(bekannt aus den letzten Teilen) die Koordinaten von 0 an zählt,
müssen wir jeweils noch eine Zeile abziehen. Haben wir
beispielsweise 60 Zeilen, müssen wir eins abziehen, damit wir
ein Datenfeld von 0-59 erhalten. Das ergibt 60 - denn 0 ist
die erste Zeile. |
|
 |
FIELD_SIZE Spalten - 1, Zeilen - 1, 20, 20
|
 |
|
Nun werden
alle bisher erstellen Objekte wieder gelöscht und die
Steuerungs-Tasten für Pacman (also die Cursor-Tasten)
werden auf "ungedrückt" gesetzt. |
|
 |
For M = 0 To
UBound(Obj)
OBJ_CLEAR Obj(M)
Next M
KEY_DOWN(37) = 0
KEY_DOWN(38) = 0
KEY_DOWN(39) = 0
KEY_DOWN(40) = 0
|
 |
|
Jetzt setzen
wir unseren Objektzähler AObj auf Null und Y auf Null. Dann
erstellen wir Pacman an der Position 0,0. Wenn Sie die
Syntax von OBJ_SET nicht mehr kennen, so lesen Sie diese
bitte in den letzten Teilen nach. Hier sehen Sie auch
wieder, dass wir mit den Konstanten NR_xxxxxxx und
ID_xxxxxxx gut arbeiten können. Anschließend erhöhen wir
AObj für das nächste Objekt. Dieses Verfahren ist wichtig,
damit Pacman immer Objekt Nr. 0 ist und so leicht
angesprochen werden kann. |
|
 |
Y = 0
AObj = 0
OBJ_SET Obj(AObj), 0, 0, 100, 0, 0,
NR_PACMAN,_
Spr(ID_PACMAN_DOWN), 1
AObj = AObj + 1
|
 |
|
Die
Leveldatei wird wieder geöffnet, und wir beginnen mit dem
eigentlichen Einlesen. |
|
 |
Open
Pfad$ + Datei$ For Input As #1
Do
Line Input #1,
A$
|
 |
|
Nachdem wir
jeweils eine Zeile gelesen haben, gehen wir diese Zeile mit
einer FOR-Schleife durch. Dabei zählen wir von 0 bis Länge-1.
Das Zeichen erhalten wir mit Hilfe der MID$()-Funktion: |
|
 |
For X = 0 To Len(A$) - 1
Zeichen$ = Mid$(A$, X + 1, 1)
|
 |
|
Jetzt kommt
der längste Teil: Wir haben ein Zeichen aus einer Datei
eingelesen. Dieses Zeichen stellt ein Objekt dar. In einem SELECT-CASE-Block sehen wir nun nach, welches Zeichen es ist
und welches Objekt wir dafür setzten. Dieser Teil ist für
das Design der Level wichtig. Ich habe Ihnen folgendes
Format vorgegeben: |
Jede
Leveldatei ist, wie Sie sicher schon gemerkt haben, wie eine
Karte aufgebaut. Jedes Zeichen entspricht einem Objekt, so dass
man ein Level sehr gut und einfach mit einem Texteditor
und einer unproportionalen Schrift, wie z.B. Courier New,
erstellen kann. |
|
Was |
Zeichen |
Objekt |
Wände |
|
! |
Graue Wand |
|
" |
Gelbe Wand
(CHR$(34) ist ein Anführungszeichen |
|
§ |
Orange
Wand |
|
$ |
Rote Wand |
|
% |
Lila Wand |
|
& |
Blaue Wand |
|
/ |
Türkise
Wand |
|
( |
Grüne
Wand |
|
) |
Braune
Wand |
Pacman
& Items |
|
P |
Pacman |
|
C |
Cool-Pacman
(Zeit bleibt stehen) |
|
I |
Unsichtbarkeit |
|
T |
Terminator-Pacman
(kann Monster fressen) |
|
X |
Totenkopf
(zieht Pacman Energie ab) |
|
. |
Punkt |
|
F |
Flagge |
Monster |
|
1 |
Blau |
|
2 |
Grün |
|
3 |
Rot |
|
4 |
Braun |
|
5 |
Lila |
|
6 |
Hippie
(das soll niemanden diskriminieren, aber mir fiel
kein anderer Name ein!) |
Schlüssel |
|
k |
Schlüssel
Typ1 |
|
K |
Schlüssel
Typ 2 |
|
d |
Tür
für Schlüssel 1 |
|
D |
Tür
für Schlüssel 2 |
Schalter |
|
R |
Roter
Schalter |
|
G |
Grüner
Schalter |
|
B |
Blauer
Schalter |
|
Y |
Gelber
Schalter |
|
r |
Roter
Block |
|
g |
Grüner Block |
|
b |
Blauer
Block |
|
y |
Gelber
Block |
Teleporter |
|
7 |
Roter
Teleporter |
|
8 |
Grüner
Teleporter |
|
9 |
Blauer
Teleporter |
|
0 |
Gelber
Teleporter |
|
Wenn Sie
selbst einige Level entwerfen wollen, was ich Ihnen bei nur
drei Level empfehlen kann, sollten Sie sich die Liste oben
ausdrucken, und beim Editieren neben den Rechner legen. Sie können
natürlich auch einen Leveleditor schreiben, aber dies
wollte ich in diesem Kurs auf Grund von Zeit & Platz
nicht auch noch machen. |
Jetzt muss
jedes Spielelement, das auch oben in der Liste steht, in den
SELECT-CASE-Block hinein. Der nun folgende Teil sieht zwar
SEHR unübersichtlich aus, basiert aber im Grunde nur auf
folgenden Teilen (fett gedrucktes ändert sich von Fall zu
Fall): |
|
 |
'Für
Wände
Case <Zeichen>:
FIELD_SET x, y, 1, <Sprite>, 1
'Für Objekte
Case
<Zeichen>: OBJ_SET Obj(AObj), X * 20, Y * 20, 100,
0, 0, _
<Nr>, Spr(<Sprite>), 1: AObj=AObj+1
|
 |
|
Und mit
diesem Wissen ist es leicht einen Block zu erstellen. Als
Beispiel für das blaue Monster nehmen wir als Zeichen
"1" (aus der Liste zu entnehmen) und die
Konstanten NR_MONSTER_BLUE und ID_MONSTER_BLUE. Hier sehen
Sie, warum ich am Anfang Konstanten benutzt habe. |
|
 |
Case
"1": OBJ_SET Obj(AObj), X * 20, Y * 20, 100, 0,
0,_
NR_MONSTER_BLUE, Spr(ID_MONSTER_BLUE), 1: AObj = AObj + 1
|
 |
|
Nun an die
Arbeit! Ich werde nur auf einige besondere Fälle eingehen.
Alle anderen, die nach dem oben gezeigten Schema
funktionieren, werde ich nur auflisten. Ich schlage Ihnen vor,
den folgenden Code zu kopieren, es sei denn, Sie haben sehr
viel Lust die nächsten 15 Minuten zu tippen ;-) |
Ach ja, bei
den Wänden wird jeweils noch ein Zufallswert zwischen 0 und
4 addiert, damit wir Wände in unterschiedlichen
Schattierungen (Helligkeiten) haben. |
|
 |
Select
Case Zeichen$
|
 |
|
Es kommen
jetzt die Wände. |
|
 |
Case
"!":FIELD_SET
X, Y, 1, Spr(ID_WALL_GRAY + Fix(Rnd*4)), 1
Case
Chr$(34):FIELD_SET X,Y,1, Spr(ID_WALL_YELLOW+Fix(Rnd*4)),1
Case
"§":FIELD_SET X,Y,1, Spr(ID_WALL_ORANGE +
Fix(Rnd * 4)), 1
Case
"$":FIELD_SET X,Y,1, Spr(ID_WALL_RED + Fix(Rnd*4)), 1
Case
"%":FIELD_SET X,Y,1, Spr(ID_WALL_MAGENTA +
Fix(Rnd*4)), 1
Case
"&":FIELD_SET X,Y,1, Spr(ID_WALL_BLUE +
Fix(Rnd*4)), 1
Case
"/":FIELD_SET X,Y,1, Spr(ID_WALL_CYAN + Fix(Rnd*4)), 1
Case
"(":FIELD_SET X,Y,1, Spr(ID_WALL_GREEN + Fix(Rnd*4)), 1
Case
")":FIELD_SET X,Y,1, Spr(ID_WALL_BROWN + Fix(Rnd*4)), 1
|
 |
|
Pacman wird
einfach nur verschoben: |
|
 |
Case
"P": Obj(0).X = X * 20: Obj(0).Y = Y * 20
|
 |
|
|
 |
Case
"C":OBJ_SET Obj(AObj),X*20,Y*20,100,0,0,NR_ITEM_COOL,_
Spr(ID_ITEM_COOL), 1: AObj = AObj + 1
Case
"I":OBJ_SET Obj(AObj),X*20,Y*20,100,0,0,NR_ITEM_INVISIBLE,_
Spr(ID_ITEM_INVISIBLE), 1: AObj = AObj + 1
Case
"T":OBJ_SET Obj(AObj),X*20,Y*20,100,0,0,NR_ITEM_TERMINATOR,_
Spr(ID_ITEM_TERMINATOR), 1: AObj = AObj + 1
Case
"X":OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_ITEM_DEAD,_
Spr(ID_ITEM_DEAD), 1: AObj = AObj + 1
|
 |
|
|
 |
Case
"1":OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_MONSTER_BLUE,_
Spr(ID_MONSTER_BLUE), 1: AObj = AObj + 1
Case
"2":OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_MONSTER_GREEN,_
Spr(ID_MONSTER_GREEN), 1: AObj = AObj + 1
Case
"3":OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_MONSTER_RED,_
Spr(ID_MONSTER_RED), 1: AObj = AObj + 1
Case
"4":OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_MONSTER_BROWN,_
Spr(ID_MONSTER_BROWN), 1: AObj = AObj + 1
Case
"5":OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_MONSTER_MAGENTA,
Spr(ID_MONSTER_MAGENTA), 1: AObj = AObj + 1
Case
"6":OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_MONSTER_HIPPIE,_
Spr(ID_MONSTER_HIPPIE), 1: AObj = AObj + 1
|
 |
|
|
 |
Case
"k": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,
NR_ITEM_KEY1,_
Spr(ID_ITEM_KEY1), 1: AObj = AObj + 1
Case
"K": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,
NR_ITEM_KEY2,_
Spr(ID_ITEM_KEY2), 1: AObj = AObj + 1
Case
"d": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,
NR_DOOR1,_
Spr(ID_DOOR1), 1: AObj = AObj + 1
Case
"D": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,
NR_DOOR2,_
Spr(ID_DOOR2), 1: AObj = AObj + 1
|
 |
|
Bei den
Punkten wir zusätzlich noch COUNT_DOTS erhöht... |
|
 |
Case
".": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,
NR_DOT,_
Spr(ID_DOT), 1: AObj = AObj + 1
COUNT_DOTS = COUNT_DOTS + 1
Case"F":
OBJ_SET Obj(AObj), X*20,Y*20,100,0,0, NR_FLAG,_
Spr(ID_FLAG), 1: AObj = AObj + 1
|
 |
|
Jetzt kommen
die Schalter: |
|
 |
Case
"R": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_SWITCH_RED,_
Spr(ID_SWITCH_RED), 1: AObj = AObj + 1
Case
"G": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_SWITCH_GREEN,_
Spr(ID_SWITCH_GREEN), 1: AObj = AObj + 1
Case
"B": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_SWITCH_BLUE,_
Spr(ID_SWITCH_BLUE), 1: AObj = AObj + 1
Case
"Y": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_SWITCH_YELLOW,_
Spr(ID_SWITCH_YELLOW), 1: AObj = AObj + 1
Case
"r": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_BLOCK_RED,_
Spr(ID_BLOCK_RED), 1: AObj = AObj + 1
Case
"g": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_BLOCK_GREEN,_
Spr(ID_BLOCK_GREEN), 1: AObj = AObj + 1
Case
"b": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_BLOCK_BLUE,_
Spr(ID_BLOCK_BLUE), 1: AObj = AObj + 1
Case
"y": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,NR_BLOCK_YELLOW,_
Spr(ID_BLOCK_YELLOW), 1: AObj = AObj + 1
|
 |
|
|
 |
Case
"7":
OBJ_SET Obj(AObj), X*20,Y*20,100,0,0, NR_TELE_RED,_
Spr(ID_TELE_RED), 1: AObj = AObj + 1
Case
"8": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,
NR_TELE_GREEN,_
Spr(ID_TELE_GREEN), 1: AObj = AObj + 1
Case
"9": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,
NR_TELE_BLUE,_
Spr(ID_TELE_BLUE), 1: AObj = AObj + 1
Case
"0": OBJ_SET Obj(AObj), X*20,Y*20,100,0,0,
NR_TELE_YELLOW,_
Spr(ID_TELE_YELLOW), 1: AObj = AObj + 1
End
Select
|
 |
|
Puh, das war
ein ganz schönes Stück Arbeit. Aber dafür geht's jetzt
erst einmal etwas ruhiger zu. Wir schließen die FOR-Schleife. Anschließend wird Y um eins erhöht. Die
Abbruchbedingung der offenen DO-Schleife lautet UNTIL
EOF(1), was bedeutet, dass der ganze Block solange
durchlaufen wird, bis die Datei zu Ende ist. Dann wird sie
geschlossen, und wir sind fürs erste durch. |
|
 |
Next X
Y = Y + 1
Loop Until EOF(1)
Close #1
End Sub
|
 |
|
|