Ich möchte mit diesem Kurs zeigen, dass es entgegen anders lautenden Gerüchten sehr gut möglich ist, in Visual Basic Spiele zu programmieren, die durchaus erstaunliche Ablauf-Geschwindigkeiten erreichen.

Im letzten Teil des Kurses haben wir fast alle Module aus dem Pacman-Spiel besprochen. Nun ist das Hauptprogramm dran. Dabei besprechen wir noch das Gerüst mit den Formen und beenden damit die Entwicklung des Spiels.
Am Ende dieses Teils gibt es dann noch einige Tipps zu eigenen Spielen und Verbesserungsvorschläge.

Christian Page 07/2001

Anregungen oder Tipps an Christian Page
 
  4.1 Pacman.BAS  voriges Thema [ Top ] nächstes Thema

Die Datei PACMAN.BAS ist das Hauptprogramm und das letzte unbesprochene Modul unseres Spiels.

Ein besonders wichtiger und elementarer Teil ist die Definition unserer Konstanten. Wie wir im letzten Teil schon besprochen haben, wollen wir Konstanten benutzen, die für jeden Typ einen spezifischen Wert angeben und uns so eine einfache Identifizierung erlauben. Die Identifizierungsnummern der Objekte können wir beliebig aussuchen, wir müssen nur darauf achten, dass kein Wert doppelt vorkommt.

Da jedes Objekt als Sprite auf den Bildschirm gezeichnet wird und die Sprites durchnumeriert im Speicher liegen, müssen wir natürlich auch wissen, welches Sprite welche Nummer hat. Dabei ist es von entscheidender Wichtigkeit, in welcher Reihenfolge die Sprites in den Bildern gespeichert worden sind. Da ich Ihnen die Bilder vorgegeben habe, ist das hier natürlich kein größeres Problem. Sobald Sie aber irgendwo Sprites einfügen oder ändern, beginnt das Suchen und sie müssten jeden Wert überprüfen. Dieses Problem können wir durch die Benutzung von Konstanten umgehen. Bei größeren Spielen kann man auch die Nummern in Dateien speichern oder eine Scriptsprache schreiben, welche die richtigen Nummern holt. Für unsere Zwecke ist dies jedoch nicht erforderlich. Hier nun eine Übersicht über alle Konstanten. Ich schlage Ihnen vor, diese zu kopieren und nicht abzutippen, da eine Falscheingabe später schwer zu finden ist und zu Fehlern führen kann. Außerdem, wer will schon so viel abtippen ;-)

Global Const NR_NOTHING = 200
Global Const NR_ANIMATOR = 201

Global Const NR_PACMAN = 1
Global Const ID_PACMAN_LEFT = 24
Global Const ID_PACMAN_RIGHT = 0
Global Const ID_PACMAN_UP = 72
Global Const ID_PACMAN_DOWN = 48

Global Const NR_MONSTER_BLUE = 2
Global Const ID_MONSTER_BLUE = 96
Global Const NR_MONSTER_GREEN = 3
Global Const ID_MONSTER_GREEN = 108
Global Const NR_MONSTER_RED = 4
Global Const ID_MONSTER_RED = 120
Global Const NR_MONSTER_BROWN = 5
Global Const ID_MONSTER_BROWN = 132
Global Const NR_MONSTER_MAGENTA = 6
Global Const ID_MONSTER_MAGENTA = 144
Global Const NR_MONSTER_HIPPIE = 7
Global Const ID_MONSTER_HIPPIE = 156

Global Const NR_ITEM_COOL = 8
Global Const ID_ITEM_COOL = 172
Global Const NR_ITEM_INVISIBLE = 9
Global Const ID_ITEM_INVISIBLE = 176
Global Const NR_ITEM_TERMINATOR = 10
Global Const ID_ITEM_TERMINATOR = 180

Global Const NR_ITEM_DEAD = 11
Global Const ID_ITEM_DEAD = 184
Global Const NR_ITEM_KEY1 = 12
Global Const ID_ITEM_KEY1 = 188
Global Const NR_ITEM_KEY2 = 13
Global Const ID_ITEM_KEY2 = 192

Global Const NR_DOT = 14
Global Const ID_DOT = 168
Global Const NR_DOOR1 = 15
Global Const ID_DOOR1 = 196
Global Const NR_DOOR2 = 16
Global Const ID_DOOR2 = 197
Global Const NR_FLAG = 17
Global Const ID_FLAG = 204

Global Const NR_SWITCH_RED = 18
Global Const ID_SWITCH_RED = 240
Global Const NR_SWITCH_GREEN = 19
Global Const ID_SWITCH_GREEN = 242
Global Const NR_SWITCH_BLUE = 20
Global Const ID_SWITCH_BLUE = 244
Global Const NR_SWITCH_YELLOW = 21
Global Const ID_SWITCH_YELLOW = 246

Global Const NR_BLOCK_RED = 22
Global Const ID_BLOCK_RED = 248
Global Const NR_BLOCK_GREEN = 23
Global Const ID_BLOCK_GREEN = 249
Global Const NR_BLOCK_BLUE = 24
Global Const ID_BLOCK_BLUE = 250
Global Const NR_BLOCK_YELLOW = 25
Global Const ID_BLOCK_YELLOW = 251

Global Const NR_TELE_RED = 26
Global Const ID_TELE_RED = 252
Global Const NR_TELE_YELLOW = 27
Global Const ID_TELE_YELLOW = 264
Global Const NR_TELE_BLUE = 28
Global Const ID_TELE_BLUE = 260
Global Const NR_TELE_GREEN = 29
Global Const ID_TELE_GREEN = 256

Global Const ID_WALL_GRAY = 276
Global Const ID_WALL_YELLOW = 280
Global Const ID_WALL_ORANGE = 284
Global Const ID_WALL_RED = 288
Global Const ID_WALL_MAGENTA = 292
Global Const ID_WALL_BLUE = 296
Global Const ID_WALL_CYAN = 300
Global Const ID_WALL_GREEN = 304
Global Const ID_WALL_BROWN = 308

Global Const ID_ENERGY = 216
Global Const ID_TIME = 233

Wir benötigen im Hauptprogramm auch noch einige globale Variablen für unser Spiel:

Global KEY_DOWN(255), KEY_LAST
Global Pfad$
Global SICHT_X, SICHT_Y

Global COUNT_DOTS
Global LEVEL_SCORE
Global LEVEL_NEXT
Global HIGHSCORE_EINTRAGEN

Das KEY_DOWN()-Datenfeld gibt für 255 verschiedene Tasten(-codes) an, ob die entsprechende Taste gedrückt ist oder nicht. KEY_LAST gibt die zuletzt betätigte Taste an. PFAD$ ist, wie im letzten Spiel auch, eine globale Variable, die den Pfad setzt, in dem die Spieldaten zu finden sind. SICHT_X und SICHT_Y geben die Scrollposition des Spielfelds und aller Objekte in X- und Y-Richtung an.

COUNT_DOTS gibt an, wie viele essbare Punkte, also diesen typischen runden gelben Dinger die in jedem PACMAN-Spiel vorkommen, sich auf dem Spielfeld befinden. Damit wird überprüft, ob der Spieler alle Punkte eingesammelt hat oder nicht. LEVEL_SCORE gibt die Punktzahl an, die insgesamt in dem Level erreicht worden ist. LEVEL_NEXT gibt an, ob der Spieler das nächste Level erreicht hat oder "gestorben" ist. Die Variable HIGHSCORE_EINTRAGEN dient dazu, der späteren Form für die Highscore-Liste mitzuteilen, ob sie prüfen soll, ob sich der Spieler eintragen kann (=1) oder ob er sie nur ansehen will (=0).

Und nun noch die wichtigsten zwei Datenfelder:

Global Spr(400) As Sprite
Global Obj(200) As ObjType

Nun haben wir für das Spiel alle wichtigen Variablen definiert und Konstanten gesetzt. Es kann also losgehen.

 
  4.2 Pacman in Form voriges Thema [ Top ] nächstes Thema

Wenn man ein Spiel schreiben will, das in einer Form und unter Windows abläuft, muss man sich zwangsweise überlegen, wie man es realisiert, so dass gleichzeitig das Menü funktioniert und das Spiel läuft. Die am leichtesten erscheinende Lösung ist vielleicht ein Timer, der beispielsweise alle 10 Millisekunden den Bildschirm aufbaut und die Ereignisse des Spiels verwaltet. Jedoch hat diese Methode auch mehrere entscheidende Nachteile:

  • Diese Methode ist langsam!

  • Sie müssen das Spiel so programmieren, dass man es über einen Zeitgeber verwaltet wird.

  • Es ist mit einigem Verwaltungsaufwand verbunden.

Die andere Alternative ist, den Spieß umzudrehen: Das Spiel läuft permanent und nur den Menüs wird bei jedem Durchlauf etwas Zeit gegeben, Ereignisse weiterzugeben. Dazu verwenden wir die DoEvents-Anweisung. Diese gibt die Kontrolle für kurze Zeit an Windows zurück und ermöglicht, dass z.B. Menüereignisse überprüft werden.

Was wir jetzt noch benötigen ist eine Variable, die angibt, welches Ereignis eingegangen ist. Dazu  definieren wir folgende Variablen und Konstanten im Deklarationsteil der PACMAN.FRM, die Sie nun neu erstellen:

Const MNU_NEW = 1
Const MNU_QUIT = 2
Const MNU_HIGH = 3
Const MNU_HELP = 4
Const MNU_INFO = 5

Dim Shared MENU_WAHL

Unser späteres Spiel soll dann in der Prozedur SPIEL verwaltet werden und über die Variable MENU_WAHL mitbekommen, ob und wenn ja, welcher Menüeintrag angeklickt wurde. Die Abfrage der Tastatur erfolgt natürlich wieder über DISPLAY_KEYDOWN und DISPLAY_KEYUP und wirkt sich auf das KEY_DOWN()-Datenfeld und die KEY_LAST-Variable aus.

Zuerst behandeln wir den Aufruf der Form. Der Inhalt der FORM_LOAD-Ereignisprozedur ist relativ simpel:

Sub FORM_LOAD ()
  Me.Show
  SPIEL
End Sub

Als erstes stellen wir sicher, dass die Form schon auf dem Bildschirm zu sehen ist, bevor das Spiel gestartet wird. Anschließend wird die Prozedur SPIEL aufgerufen.

Der nächste Punkt der "Tagesordnung" ist die neue PACMAN.FRM-Datei die neugestaltet werden muss.

Die Form selbst bekommt später noch ein Menü. Setzen Sie nun zwei Bildfelder und ein Bezeichnungsfeld auf die Form:

BACK (PictureBox/Bildfeld)
AutoRedraw = 1
AutoSize = 1
ScaleMode = 3
Visible = 0

DISPLAY (PictureBox/Bildfeld)
AutoRedraw = 1
ScaleMode =3
Visible=1
Picture= <pal.bmp>

STATUS (Label/Bezeichnungsfeld)
Alignment = 2
BackStyle = 1
BorderStyle = 1
Caption = "VB-Kurs PacMan - 1997 by PCDVisual@AOL.COM"

FORM
Caption = "VB-Kurs Pacman"
Icon = <pacman.ico>
Name = Main

Es folgt das Menü. Rufen Sie dazu wie bekannt den Menüeditor auf.

Caption Name (Shortcut)
&Spiel M_S_TITLE
                    &Neues Spiel M_S_NEU (Strg+N)
                    - M_S_Sep1
                    &Beenden M_S_BEENDEN (Strg+B)
&Info M_I_TITLE
                    &Highscore M_I_HIGHSCORE (Strg+H)
                    &Hilfe zum Spiel M_I_HILFE (F1)
                    - M_I_SEP1
                   &Info über M_I_INFO (Strg+I)

Jetzt zum Ereigniscode für die Form.

Sub DISPLAY_KEYDOWN (KeyCode As Integer, Shift As Integer)
   If KeyCode <= 255 Then 
     KEY_DOWN(KeyCode) = 1
     KEY_LAST = KeyCode
  End If
End Sub

Sub DISPLAY_KEYUP (KeyCode As Integer, Shift As Integer)
   If KeyCode <= 255 Then
     KEY_DOWN(KeyCode) = 0
   End If
End Sub

Die Ereignisroutinen für das Display-Bildfeld sollten Ihnen noch vom letzten Spiel bekannt sein. Hier werden Tastendrücke des Benutzers abgefangen und registriert. Sobald eine Taste gedrückt wird, setzt die Routine den entsprechenden Eintrag im KEY_DOWN()-Datenfeld auf 1 und weist der Variable KEY_LAST den KeyCode zu. Wird die Taste wieder losgelassen, so wird der Eintrag im KEY_DOWN()-Datenfeld wieder zurück auf 0 gesetzt. Somit können wir im Spiel überprüfen, welche Tasten gerade gedrückt sind und welche nicht.

Die FORM_RESIZE-Routine sorgt dafür, dass alle Elemente in der Form jederzeit die richtige Größe haben.

Sub FORM_RESIZE ()

  DISPLAY.Left = 0
  DISPLAY.Top = 0
  DISPLAY.Width = Me.ScaleWidth
  DISPLAY.Height = Me.ScaleHeight - STATUS.Height

  STATUS.Left = 0
  STATUS.Width = Me.ScaleWidth
  STATUS.Top = Me.ScaleHeight - STATUS.Height

  R = StretchBlt(DISPLAY.hDC, 0, 0, DISPLAY.ScaleWidth,_
      DISPLAY.ScaleHeight, BACK.hDC, 0, 0, BACK.ScaleWidth,_
      BACK.ScaleHeight, BIT_COPY)

  DISPLAY.Picture = DISPLAY.Image

End Sub

Das Display-Bildfeld wird auf die Koordinaten 0,0 gesetzt. Das ist die obere linke Ecke direkt unter dem Menü. Die Größe ergibt sich aus der Innengröße der Form minus die Größe des Statusfeldes. Die Breite entspricht der gesamten Breite der Form. Das Statusfenster wird immer am unteren Rand der Form positioniert. Und anschließend wird noch das Hintergrundbild gestreckt und auf das DISPLAY-Bildfeld kopiert. 

Da wir in FORM_LOAD die FORM anzeigen gibt es Probleme, sobald die Form geschlossen wird. In diesem Fall würde die FORM_LOAD-Prozedur nämlich dafür sorgen, dass die Form gleich wieder angezeigt werden würde. Aus diesem Grund wäre die Form nicht zu schließen. Daher benutzen wir die FORM_UNLOAD-Prozedur um VB explizit mitzuteilen, dass die Form nicht nur geschlossen, sondern auch das ganze Programm beendet werden soll.

Sub FORM_UNLOAD (Cancel As Integer)
  End
End Sub

Jetzt müssen wir noch den Code für die Menüs schreiben:

Sub M_I_HIGHSCORE_CLICK ()
  MENU_WAHL = MNU_HIGH
End Sub

Wenn der Benutzer einen Menüeintrag anklickt wird die Variable MENU_WAHL auf einen entsprechenden Wert gesetzt. Auch hierbei benutzen wir Konstanten.

Sub M_I_HILFE_CLICK ()
 MENU_WAHL = MNU_HELP
End Sub

Sub
M_I_INFO_CLICK ()
  MENU_WAHL = MNU_INFO
End Sub

Sub
M_S_BEENDEN_CLICK ()
  MENU_WAHL = MNU_QUIT
End Sub

Sub
M_S_NEU_CLICK ()
  MENU_WAHL = MNU_NEW
End Sub

Und als letzte Routine für dieses Modul verfassen wir die zentrale Routine SPIEL, in der alle Ereignisse zusammenlaufen und die das ganze Spiel steuert.

In der obersten Zeile wird per On Local Error Resume Next dafür gesorgt, dass alle eventuell vorkommende Fehler abgefangen werden.

Sub SPIEL ()
 
  On Local Error Resume Next

Danach werden die Routinen PACMAN_INIT (Initialisierung des Spiels) und PACMAN_LOAD "LEVEL.001","BACK1.BMP" (Datei LEVEL.001 laden und als Hintergrund BACK1.BMP) aufgerufen. Am Ende wird noch einmal FORM_RESIZE aufgerufen, damit gleich am Anfang des Spiels alles richtig aussieht.

  PACMAN_INIT
  PACMAN_LOAD "LEVEL.001", "BACK1.BMP"
  FORM_RESIZE

Die Variable Level (gibt logischerweise das aktuelle Level an) wird auf 1 und die Variable StartTime (Anfangszeit) wird auf TIMER gesetzt. Aus der Anfangs- und der aktuellen Zeit können wir immer leicht errechnen, wie lange das Spiel schon läuft ohne dass wir auf einen Timer zurückgreifen müssen.

  Level = 1
  StartTime = Timer

Nun startet die Hauptschleife, in der das Spiel abläuft.

  Do

Zuerst überprüfen wir die Hintergrundmusik und starten sie ggf. wieder, wie wir es aus dem ersten Spiel kennen.

      If MIDI_PLAYING() = 0 Then
        MIDI_PLAY Pfad$ + "MUSIC1.MID"
      End If

Nun wird PACMAN_RUN aufgerufen. In dieser Routine wird später das Spiel selbst (also die Bewegungen usw.) untergebracht. Anschließend zeigen wir im Statusfenster noch die Punkte und die bisher vergangene Zeit an, die sich aus der aktuellen Zeit minus der Startzeit errechnet. Danach wird DoEvents benutzt, um Windows Zeit für andere Ereignisse zu geben.

     PACMAN_RUN
     STATUS.Caption = "Punkte: " + Str$(LEVEL_SCORE) + " Zeit:"_
                      + Str$(Fix(Timer - StartTime))

     DoEvents

Nun überprüfen wir, ob ein Menüereignis vorliegt. In diesem Fall hat MENU_WAHL einen Wert ungleich Null. Diesen fragen wir dann in einem SELECT-CASE-Block ab und reagieren entsprechend.

     If MENU_WAHL <> 0 Then
        Select Case MENU_WAHL

Wurde der Eintrag Spiel  -> Neues Spiel starten gewählt, so wird wieder das erste Level geladen und alle zugehörigen Werte werden zurückgesetzt.

            Case MNU_NEW
              PACMAN_LOAD "LEVEL.001", "BACK1.BMP"
              Level = 1
              LEVEL_SCORE = 0
              LEVEL_NEXT = 0
              FORM_RESIZE
              StartTime = Timer

Der Eintrag Spiel  -> Beenden ist schnell abgehandelt. Darauf reagiert unser Spiel mit der schlichten Anweisung END die hier nicht weiter besprochen werden muss, oder etwa doch? ;-)

            Case MNU_QUIT
              End

Als nächstes kommt der Eintrag Info -> Highscore sehen. Dieser hat zur Folge, dass die Variable HIGHSCORE_EINTRAGEN auf Null gesetzt wird (damit die Form weiß, dass der User nur mal reinsehen will) und die Form HIGHSCORE eingetragen wird.

           Case MNU_HIGH
              HIGHSCORE_EINTRAGEN = 0
              HIGHSCORE.Show 1

Der Eintrag Info -> Hilfe sorgt für das Aufrufen der Hilfe-Form.

           Case MNU_HELP
              HILFE.Show 1

Bei Info -> Info über sieht es ähnlich einfach aus:

           Case MNU_INFO
              INFO.Show 1
 
        End Select 

Damit wäre der CASE-Block abgeschlossen und die MNU_WAHL-Variable wird wieder auf Null zurückgesetzt.

        MENU_WAHL = 0
     End If

Die Variable LEVEL_NEXT wird von PACMAN_RUN auf 1 gesetzt. Falls der Spieler vor dem Sieg abtritt wird sie auf -1 gesetzt. Im ersten Fall wird die Level-Variable erhöht, und dann wird der Name der neuen Level-Datei errechnet. Zuerst erstellen wir aus der Variable Level einen String, schneiden die Lücke vorne ab, und setzen solange Nullen davor, bis es insgesamt ein String mit drei Zeichen ist.

Dann überprüfen wir, ob eine solche Datei (Pfad + "LEVEL." + Levelendung) existiert. Wenn ja wird sie geladen und LEVEL_NEXT wird zurückgesetzt. Wenn nein wird HIGHSCORE_EINTRAGEN auf 1 gesetzt, die Form wird angezeigt. Anschließend ruft das Programm noch die Form GELÖST auf (die bisher auch noch nicht da ist) und dann wird ein neues Spiel gestartet. Das ist die Standardprozedur, wenn der Spieler gewinnen sollte.

     If LEVEL_NEXT > 0 Then
        Level = Level + 1
        L$ = LTrim$(Str$(Level))
        If Len(L$) < 3 Then L$ = "0" + L$
        If Len(L$) < 3 Then L$ = "0" + L$
        If Dir$(Pfad$ + "LEVEL." + L$) = "" Then
           HIGHSCORE_EINTRAGEN = 1
           HIGHSCORE.Show 1
           GELÖST.Show 1
           MENU_WAHL = MNU_NEW
        Else
           PACMAN_LOAD "LEVEL." + L$, "BACK" + _
                        LTrim$(Str$(Level)) + ".BMP"
           LEVEL_NEXT = 0
           FORM_RESIZE
         End If
      End If

Sollte der Spieler verlieren, wird die VERLOREN-Form angezeigt und alle Werte werden zurückgesetzt. Zusätzlich wird hier ebenfalls die Highscoreliste aufgerufen, in der sich der Spieler eintragen kann, wenn er gut genug war :-)

      If LEVEL_NEXT < 0 Then
         LEVEL_NEXT = 0
         MENU_WAHL = MNU_NEW
         VERLOREN.Show 1
         HIGHSCORE_EINTRAGEN = 1
         HIGHSCORE.Show 1
      End If

Nun kommt natürlich noch der Abschluss der Schleife und das Ende der Prozedur.

    Loop

End Sub

Damit Sie später beim Schreiben des Hauptprogramms immer nachsehen können wie weit das Spiel läuft, werden wir nun die restlichen Fenster erstellen.

 
  4.3 Weitere Formalitäten voriges Thema [ Top ] nächstes Thema

Drei Formen können Sie praktisch nach Belieben gestalten: INFO, GELÖST und VERLOREN. Diese Fenster dienen nur dazu, eine Mitteilung auszugeben. Das einzige, was Sie hier beachten müssen ist der Name des Fensters und die Tatsache, dass Sie am besten BorderStyle auf 1 oder 3 setzten und MinButton und MaxButton auf  0, damit man die Form nicht in der Größe ändern kann.

Außerdem sollten diese Formen einen Abbruchknopf haben. Der Rest bleibt Ihnen überlassen. Ein kleiner Tipp nebenbei: Sie können ihre Fenster einfach auf dem Bildschirm zentrieren, wenn Sie folgende Zeilen in die FORM_LOAD-Ereignisprozedur setzen:

Sub FORM_LOAD ()
  Me.Left = (SCREEN.Width - Me.Width) / 2
  Me.Top = (SCREEN.Height - Me.Height) / 2
End Sub
 
  4.4 Hilfe für Spieler voriges Thema [ Top ] nächstes Thema

Anstatt hier nun auch noch einzubringen, wie man eine Hilfedatei schreibt und einbindet, habe ich eine andere, einfache Lösung vorgezogen. Die wenigen Informationen die dieses Spiel benötigt habe ich mit einem Grafikprogramm auf vier Bilder gebannt. Diese kann man nun im Hilfe-Fenster einfach per Knopfdruck wechseln.

Im oberen Teil der Form befindet sich ein Bildfeld, in das Sie schon während der Entwicklung das Bild PACMAN\ELSE\HILFE1.BMP einladen sollten, damit Sie sehen, wie groß die Bilder sind.
SEITE (PictureBox/Bildfeld)
AutoSize = True
Picture = <else\hilfe1.bmp>

ANZEIGEN (CommandButton)
Caption = "&1"
Index = 0
 
ANZEIGEN (CommandButton)
Caption = "&2"
Index = 1
 
ANZEIGEN (CommandButton)
Caption = "&3"
Index = 2
 
ANZEIGEN (CommandButton)
Caption = "&4"
Index = 3
 
ZURÜCK (CommandButton)
Caption = "&Zurück"

FORM
Caption = "Hilfe"
BorderStyle = 3
MinButton = 0
MaxButton = 0
Achten Sie darauf, dass die vier ANZEIGEN-Knöpfe ein Steuerelemente-Datenfeld sind. Die gleichen Namen oben sind also gewollt!

Sobald die Form aufgerufen wird, soll sie als erstes auf dem Schirm zentriert werden. Dies erreichen wir wieder durch die beiden Zeilen in FORM_LOAD. Der Algorithmus, der dahinter steckt, sollte eigentlich aus der Mathematik bekannt sein. Wir teilen die Differenz zwischen der Größe des Bildschirms und der Form durch zwei und erhalten die Koordinaten der oberen, linken Ecke.

Sub FORM_LOAD ()
  Me.Left = (SCREEN.Width - Me.Width) / 2
  Me.Top = (SCREEN.Height - Me.Height) / 2
End Sub

Wenn der Benutzer nun auf einen der Knöpfe klickt, muss das entsprechende Bild geladen werden (nun gut, man hätte auch vier Bildfelder in die Form setzen können - aber machen wir es mal auf diese Weise). Dazu können wir einfach den Index verwenden:

Sub ANZEIGEN_CLICK (Index As Integer)
  File$ = Pfad$ + "ELSE\HILFE" + LTrim$(Str$(Index + 1)) + ".BMP"
  SEITE.Picture = LoadPicture(File$)
End Sub
Die Index-Nummer wird um 1 erhöht und in einen String umgewandelt. Dann wird das vorstehende Leerzeichen abgeschnitten. Davor wird nun der komplette Pfad und der Anfang des Dateinamen gesetzt. Anschließend wird noch ".BMP" angehängt und der Dateiname ist fertig.
Nun wird mit LoadPicture(<Datei>) das entsprechende Bild in das Bildfeld SEITE geladen.

Und noch der Code für den ZURÜCK-Knopf, der keine besondere Herausforderung mehr ist ...

Sub ZURÜCK_CLICK ()
  Unload Me
End Sub
 
  4.5 Für die besten Spieler [Highscore-Liste] voriges Thema [ Top ] nächstes Thema

In vielen guten Actionspielen gibt es Highscore-Listen, die für den Spieler einen Anreiz darstellen, möglichst viele Punkte zu holen. Auch in diesem Spiel wollen wir darauf eingehen. Wenn Sie Lust haben, können Sie ja versuchen, eine ähnliche Liste in das Actionspiel (aus dem ersten Teil über Spiele) einzubinden.

Setzen Sie oben in die Form das Logo (PACMAN\ELSE\LOGO.BMP). Darunter setzten Sie sechs Label mit dem Namen SPIELER und den Indizes 0-6 (also ein Steuerelemente-Datenfeld). Dass wir damit insgesamt 7 Einträge haben spielt keine Rolle. Wenn Sie wollen, können Sie auch daraus 10 oder mehr machen - das wäre doch schon eine gute Hausaufgabe oder? :-) Das Feld mit Index 0 muss oben stehen, die anderen kommen darunter. Wie in dem Bild oben können Sie die Felder auch farbig verschieden gestalten. Was Sie in der Felder hineinschreiben ist egal, da der Inhalt sowieso mit den Daten aus der Highscore-Liste überschrieben wird. Daneben können Sie einen Trennstrich ziehen. Auf die rechte Seite kommen ebenfalls sechs Felder, diesmal mit dem Namen PUNKTE und wieder den Indizes 0-6. Also stehen jeweils ein Label von SPIELER und ein Label von PUNKTE nebeneinander, die den gleichen Index haben. Das ist wichtig, damit auch dem Namen die richtige Punktzahl zugeordnet wird. Sie sollten die Ausrichtung des Textes bei den Punkte auf "1 - Rechts" stellen, da das einfach besser aussieht.
Unten auf die Form setzen Sie einen Knopf (Name=OK, Caption="&Ok"). Die Form selbst wird mit "Highscore" betitelt und bekommt einen Rahmen, den man nicht verändern kann (BorderStyle = 3, MinButton=0, MaxButton = 0). Die Form erhält den Namen HIGHSCORE.
Eine Highscore-Liste, zumindest eine einfache Version wie diese hier, arbeitet vom Prinzip her so:
Man hat eine Liste mit Namen und eine zugehörige Liste mit Punktzahlen. Wenn der Benutzer "stirbt" bzw. gewinnt, kann er sich eintragen. Das Programm sucht anhand seiner Punktzahl, den wievielten Platz er belegt. Der Rest der Liste wird nach unten geschoben, und sein Name und seine Punktzahl werden eingefügt.
Daran kann man schon sehen, dass wir zwei Datenfelder definieren müssen: Eins für die Namen und eins für die Punkte (es sei denn, wir benutzen wieder die TYPE-Anweisung aber man muss es ja nicht übertreiben). Also definieren wir beide im Deklarationsteil der Form. Wir geben als Index 6 an, da wir damit Einträge von 0-6 definieren und so auch wieder sieben Einträge erhalten. Wer mehr haben will kann wie auch bei der Form einfach mehr eingeben, z.B. 10.
Dim Shared N$(6), P(6)

Das N$()-Datenfeld fasst logischerweise die Namen, das P()-Datenfeld die Punktzahlen auf. Nun müssen wir in FORM_LOAD die alte Highscore-Liste laden, die Punktzahl prüfen und die neue Liste dann ausgeben.

Am Anfang zentrieren wir wieder die Form auf dem Schirm.

Sub FORM_LOAD ()
 
  Me.Left = (SCREEN.Width - Me.Width) / 2
  Me.Top = (SCREEN.Height - Me.Height) / 2

Nun sehen wir mit DIR$(<Datei>) nach, ob schon eine Highscore-Datei vorhanden ist. DIR$ ist eine der vielen Dateifunktionen von Visual Basic. Sie liefert die erste gefundene Datei eines Suchmusters zurück. Wir können z.B.  Ergebnis$=DIR$("*.TXT") benutzen um die erste Textdatei im Verzeichnis zu finden. Wenn das nicht reicht, können wir mit Ergebnis$=DIR$() die nächste Datei solange suchen, bis DIR$ uns einen leeren String zurückgibt. Dann ist keine weitere Datei mehr da. In diesem Fall nutzen wir das aus, um zu prüfen, ob eine ganz bestimmte Datei, nämlich <Pfad>\SCORE.DAT vorhanden ist. Wenn ja, wird diese Datei geöffnet und die Highscores werden eingelesen.

  If Dir$(Pfad$ + "SCORE.DAT") <> "" Then
     Open
Pfad$ + "SCORE.DAT" For Input As #1
     For M = 0 To 6
        Input #1, N$(M), P(M)
     Next M
     Close #1
  Else

Wenn die Datei nicht vorhanden ist wird eine neue Highscore-Liste erstellt, in der schon einige Namen und Punktzahlen voreingestellt sind, um Ansporn zu geben, diese Leute zu schlagen und Platz eins zu erobern:

     For M = 0 To 6
        N$(M) = "PCD Visual"
        P(M) = (6 - M) * 100
      Next M
  End If

Das ich in diesem Fall meine E-Mail-Adresse benutzt habe ist nebensächlich, ich trage es Ihnen bestimmt nicht nach, wenn Sie hier etwas anderes einsetzten ;-)

Als nächstes wird geprüft, ob der Benutzer sich eintragen kann (also ob er "gestorben" ist oder gewonnen hat). In diesem Fall geht die eigentliche Arbeit los.

  If HIGHSCORE_EINTRAGEN Then

Wir durchlaufen nun mit einer Schleife alle Einträge von 0 bis 6. Jedes Mal prüfen wir, ob die Punktzahl dieses Eintrags kleiner oder gleich der des Spielers ist. In diesem Fall erobert nämlich der Spieler den Platz. Das wird hierbei von oben nach unten vorgehen ist wichtig, da sonst der Name des Spieler an alle Plätze unterhalb der eigentlich Position eingetragen werden würde.

      For M = 0 To 6
         If P(M) <= LEVEL_SCORE Then

Haben wir nun den Platz gefunden schieben wir alle Einträge darunter eins nach unten. Dies machen wir, indem wir von 6 an aufwärts zählen, bis wir einen Eintrag unter dem des Users sind. Dabei kopieren wir immer den oberen nach unten:

1. Name A
2. Name B
3. Name C
4. Name D

Der User kann sich auf Platz eins in unserem Schema eintragen, daher gehen wir von 4 nach 2 alle durch und machen folgendes:

1. Name A
2. Name B
3. Name C
  (wird nach unten kopiert)
4. Name C
Beim nächsten Schleifendurchgang sieht es dann so aus:
1. Name A
2. Name B
  (wird nach unten kopiert)
3. Name B
4. Name C
Und schließlich:
1. Name A
  (wird nach unten kopiert)
2. Name A
3. Name B
4. Name C

Nun sind alle Namen, Name A einschließlich, nach unten verschoben und wir können Platz 1 überschreiben. Damit wäre unsere Aufgabe erfüllt:

1. User
2. Name A
3. Name B
4. Name C
Nun zur Umsetzung in das Spiel:
          For M2 = 6 To M + 1 Step -1
             N$(M2) = N$(M2 - 1)
             P(M2) = P(M2 - 1)
          Next M2

Nachdem die Namen verschoben sind, können wir den User informieren, nach seinem Namen fragen, und diesen dann gleich samt seiner Punktzahl auf den errungenen Platz eintragen. Anschließend müssen (!!) wir die Schleife verlassen, damit er sich nicht auch noch auf den unteren Plätzen einträgt.

          A$ = InputBox$("Herzlichen Glückwunsch! Sie_
               können sich in der Highscore-Liste_
               eintragen!!", "HIGHSCORE!", "NoBody")
          N$(M) = A$
          P(M) = LEVEL_SCORE
          Exit For
        End If
     Next M
   End If

Jetzt übernehmen wir die aktuelle Liste in die Form. Hier sehen Sie jetzt auch, warum es so wichtig ist, dass die Steuerelementefelder in der Form oben den Index 0 haben und die unteren größere Werte:

  For M = 0 To 6
      SPIELER(M).Caption = N$(M)
      PUNKTE(M).Caption = Str$(P(M))
   Next M
End Sub

Wir können einfach mit einer Schleife alle Werte übernehmen und sind fertig :-)

Wenn der Benutzer genug von dem Bildschirm hat klickt er auf OK und die Form wird mit folgendem Code vom Bildschirm geputzt:

Sub OK_CLICK()
  Unload Me
End Sub
Moment! Ging das nicht etwas zu schnell? Habe ich da nicht etwas vergessen? Ach ja richtig, wir müssen die Highscore-Liste auch wieder speichern. Nun könnten wir den Code in FORM_LOAD hinein setzen, ich habe mich aber, ganz eigenmächtig, dafür entschlossen, einen anderen Weg zu nehmen, bei dem Sie auch gleich noch sehen, wozu FORM_UNLOAD gut sein kann: Wenn der Benutzer die Form schließt oder die Form auf irgendeine andere Art geschlossen werden soll, wird zuerst das FORM_UNLOAD Ereignis aufgerufen. Diesem wird ein Wert, Cancel%, übergeben. Wenn wir diesen Wert innerhalb der Ereignisprozedur auf 1 setzten, wird die Form nicht verschwinden, da wir damit ausdrücken, dass sie noch da bleiben muss. Hier machen wir uns die Prozedur allerdings nur zu nutze, indem wir dort den Speicher-Code eintragen:
Sub FORM_UNLOAD (Cancel As Integer)

   Open Pfad$ + "SCORE.DAT" For Output As #1
   For M = 0 To 6
      Print #1, N$(M); ","; P(M)
   Next M
   Close #1

End Sub

Es wird wieder die Datei SCORE.DAT geöffnet und die Namen und Punkte werden eingetragen. Damit wir beim Öffnen mit Input #1, N(m), P(m) arbeiten können ist es wichtig, dass wir beim Speichern auch ein Komma einbringen. Dies machen wir, indem wir einen String mit einem Komma zwischen den Werten speichern lassen. Fertig :-)

 
  4.6 Pacmans Innenleben voriges Thema [ Top ] nächstes Thema

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

Und nun die Extras:

  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

Ah! Die Monster kommen!!

  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

Schlüssel:

  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

Und die Teleporter...

  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
 
  4.7 Pacmans Steuerpult voriges Thema [ Top ] Seitenende

So, nun eine etwas einfachere Prozedur: PACMAN_RUN. Diese Routine wird bei jedem Durchlauf der Hauptschleife aufgerufen. Hier sitzt die Steuerung von PACMAN:

Sub PACMAN_RUN ()

Als erstes wird die Geschwindigkeit von Pacman auf 0,0 zurückgesetzt.

  Obj(0).SX = 0
  Obj(0).SY = 0

Nun wird geprüft, welche der Cursor-Tasten gedrückt ist. Wenn oben gedrückt ist, und der Block über Pacman frei zugänglich ist (wird mit OBJ_UP geprüft), wird die Geschwindigkeit von Pacman in Y-Richtung dorthin gehend gesetzt. Das gleiche gilt für links, rechts und unten. Übrigens können Sie die Tastencodes einfach herausbekommen, wenn Sie ein Programm schreiben, indem in der FORM_KEYDOWN Routine der Tastencode auf die Titelseite des Fenster (Me.Caption = STR$(KeyCode)) geschrieben wird.

  If KEY_DOWN(38) And OBJ_UP(Obj(0), 4) Then Obj(0).SY = -4
   If KEY_DOWN(40) And OBJ_DOWN(Obj(0), 4) Then Obj(0).SY = 4
  If KEY_DOWN(37) And OBJ_LEFT(Obj(0), 4) Then Obj(0).SX = -4
  If KEY_DOWN(39) And OBJ_RIGHT(Obj(0), 4) Then Obj(0).SX = 4

Nach der Steuerung rufen wir PACMAN_SHOW auf, die den Bildschirm zeichnet:

  PACMAN_SHOW

End Sub
 
zum Seitenanfang Kapitel 4.8
 

[ Bücherecke | Up-/Download | Tipps & Tricks | Projekte | Feedback | Spielplatz ]
[ Links | DirectX | Komponenten | Stichworte | Suchen | Startseite | Chat ]
[ Gewinnspiele | Impressum | Forum | Forum-Archiv ]
Letzte Aktualisierung: Freitag, 12. Juli 2002