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.8 Auf den Bildschirm Zur Übersicht [ Top ] nächstes Thema

Diese Routine sorgt dafür, dass der gesamte Bildschirm gezeichnet wird und alle auftretenden Ereignisse weitergereicht werden.

Sub PACMAN_SHOW ()

Die SICHT-Koordinaten des Spielfelds errechnen sich aus der Hälfte des Bildfeldes minus Pacman's Koordinaten. Das sorgt dafür, dass die Karte um Pacman zentriert wird.

  SICHT_X = Fix(MAIN.DISPLAY.ScaleWidth / 2 - Obj(0).X)
  SICHT_Y = Fix(MAIN.DISPLAY.ScaleHeight / 2 - Obj(0).Y)

Die Karte soll aber nicht über die Ränder hinaus gescrollt werden. Daher fragen wir hier ab, ob der SICHT-Koordinaten kleiner als Null, bzw. jeweils größer als die Breite/Höhe des Bildfeldes sind, und ändern sie ggfls.

  If SICHT_X > 0 Then SICHT_X = 0
  If SICHT_Y > 0 Then SICHT_Y = 0
  If SICHT_X + ((FIELD_X + 1)*FIELD_W-MAIN.DISPLAY.ScaleWidth) <0_
     Then SICHT_X = -((FIELD_X + 1) * FIELD_W -_
  MAIN.DISPLAY.ScaleWidth)
  If SICHT_Y + ((FIELD_Y + 1)*FIELD_H- MAIN.DISPLAY.ScaleHeight)_
     <0 Then SICHT_Y = -((FIELD_Y + 1) * FIELD_H -_
     MAIN.DISPLAY.ScaleHeight)

Nun kann das Bildfeld gelöscht und das Spielfeld gezeichnet werden:

  MAIN.DISPLAY.Cls
  FIELD_DRAW MAIN.DISPLAY, SICHT_X, SICHT_Y

Im Anschluss daran werden alle Objekte gezeichnet:

   For M = 0 To UBound(Obj)
     OBJ_DRAW Obj(M), SICHT_X, SICHT_Y, MAIN.DISPLAY
   Next M

Nun ist das Bild fertig, aber wir müssen noch die Ereignisse auslösen, die unseren bunten Bildschirm erst zum Leben erwecken. Daher lösen wir in einer Schleife bei allen Objekten das ID_MOVE-Ereignis aus, das den Objekten die Chance gibt, sich zu bewegen.

  For M = 0 To UBound(Obj)
     OBJ_EVENT Obj(M), ID_MOVE, ObjDummy, Obj(M).SX, Obj(M).SY

Für den Fall, dass wir gerade unseren PACMAN erwischt haben, prüfen wir eine Kollision mit allen anderen Objekten auf der Karte. Sonst prüfen wir, ob sich das Objekt bewegt. Wenn das der Fall ist, prüfen wir auf Kollision mit allen blockierenden Gegenständen, durch die auch Monster nicht hindurch kommen.

     If Obj(M).T = NR_PACMAN Then
        For M2 = M + 1 To UBound(Obj)
           OBJ_COLL_OBJ Obj(M), Obj(M2)
        Next M2
     Else
        If
Obj(M).SX <> 0 Or Obj(M).SY <> 0 Then
           For M2 = 0 To UBound(Obj)
              Need = 0
              Select Case Obj(M2).T
                 Case NR_BLOCK_RED: Need = 1
                 Case NR_BLOCK_BLUE: Need = 1
                 Case NR_BLOCK_GREEN: Need = 1
                 Case NR_BLOCK_YELLOW: Need = 1
                 Case NR_DOOR1: Need = 1
                 Case NR_DOOR2: Need = 1
              End Select
               If Need Then OBJ_COLL_OBJ Obj(M), Obj(M2)
           Next M2
        End If
     End If

  Next M

Wenn Pacman "Extras" aufgenommen hat, wird die Zeit errechnet, die noch zur Verfügung steht.

  PowerUp = 0
  E = OBJ_ITEM_ENERGY_GET(Obj(0), NR_ITEM_COOL)
   If E > PowerUp Then PowerUp = E
  E = OBJ_ITEM_ENERGY_GET(Obj(0), NR_ITEM_INVISIBLE)
   If E > PowerUp Then PowerUp = E
  E = OBJ_ITEM_ENERGY_GET(Obj(0), NR_ITEM_TERMINATOR)
   If E > PowerUp Then PowerUp = E

Dann wird daraus errechnet, welches Bild der Sanduhr genommen wird, die in der linken oberen Ecke angezeigt wird. Da wir sechs Bilder haben und jedes PowerUp normal 300 Durchgänge hält können wir das Bild mit 6/300*PowerUp errechnen. Dementsprechend wird es ausgegeben.

  If PowerUp > 0 Then
     PowerUp = 6 / 300 * PowerUp
     If PowerUp <= 6 Then
        SPRITE_DRAW Spr(ID_TIME+6-PowerUp),25,5,MAIN.DISPLAY
     End If
  End If

Ähnlich sieht es auch bei der Energie aus. Auch sie wird mittels eines Sprites in der Bildfeldecke angezeigt. Unterschreitet die Energie gar 0 wird LEVEL_NEXT auf -2 gesetzt, um dem Hauptprogramm den "Tod" des Spielers mitzuteilen.

  If Obj(0).E >= 0 Then
     Energy = 16 / 100 * Obj(0).E
     SPRITE_DRAW Spr(ID_ENERGY+16-Energy), 5, 5, MAIN.DISPLAY
  Else
     LEVEL_NEXT = -2
  End If

Zum Schluss wird noch das Bildfeld "refresht", damit unsere Änderungen auch zu sehen sind.

  MAIN.DISPLAY.Refresh

End Sub
 
  4.9 Weltbewegende Ereignisse voriges Thema [ Top ] nächstes Thema

Wie im letzten Teil besprochen, basiert Pacman auf einer selbstgestrickten Ereignissteuerung. Jedes Mal, wenn wir OBJ_EVENT aufrufen, wird das Ereignis an eine, von Spiel zu Spiel andere, USE_EVENT-Routine weitergeleitet. Diese Routine sorgt nun für die Bewegung der Spieler und Monster und für den ganzen Rest.

Die USER_EVENT-Routine für unser Pacman-Spiel ist nicht besonders schwer. Für den Fall, dass das übergebene Objekt den Typ 0 hat, wird die Routine abgebrochen, da leere Objekte nicht berücksichtigt werden. Im anderen Fall wird das Ereignis an mehrere Unterprozeduren weitergeleitet, von denen jede die Ereignisse für ganz bestimmte Bereiche verwaltet. Wir hätten zwar auch alles in diese Routine hinein schreiben können, allerdings wäre sie dann sehr groß und unübersichtlich geworden.

Sub USER_EVENT (Self As ObjType,MESSAGE,Other As ObjType,Par1,Par2)
  If Self.T = 0 Then Exit Sub
  USER_EVENT_PACMAN Self, MESSAGE, Other, Par1, Par2
  USER_EVENT_DOORS Self, MESSAGE, Other, Par1, Par2
  USER_EVENT_ITEM Self, MESSAGE, Other, Par1, Par2
  USER_EVENT_MONSTER Self, MESSAGE, Other, Par1, Par2
  USER_EVENT_SWITCH Self, MESSAGE, Other, Par1, Par2
  USER_EVENT_TELE Self, MESSAGE, Other, Par1, Par2
End Sub
 
  4.9.1 Pacmans Ereignisse voriges Thema [ Top ] nächstes Thema

Die erste untergeordnete Ereignisprozedur ist OBJ_EVENT_PACMAN. Diese Prozedur verwaltet nur PACMAN selbst, die "essbaren" Punkte und die Flagge.

Der Prozedurkopf ist relativ simpel und auch eigentlich schon bekannt. Es werden wieder die gleichen Variablen wie bei OBJ_EVENT übergeben. Anschließend öffnen wir einen SELECT-CASE-Block, in dem wir den Typ des übergebenen Objekts auswählen.

Sub USER_EVENT_PACMAN (Self As ObjType,MESSAGE,Other As ObjType, _
    Par1, Par2)

  Select Case Self.T

Nun kommt der Teil für Pacman. Hier wählen wir wieder per SELECT CASE das aufgetretene Ereignis aus:

     Case NR_PACMAN:     'Pacman
        Select Case MESSAGE

Jetzt kommt als erstes das DRAW-Ereignis. Das Einrücken müssen Sie in diesem Fall selbst noch korrekt erledigen, da sonst fast alle Zeilen länger gewesen wären als die Seitenbreite.

           Case ID_DRAW

Am Anfang verwenden wir OBJ_ANIMATE um die Reservewerte R1 und R2 als Spritezähler zu verwenden. R1 wird solange erhöht, bis es den Wert 3 hat. Dann wird es zurückgesetzt und R2  wird erhöht. Wenn R2 den Wert 3 erreicht, wird es auf 0 gesetzt. Damit haben wir in R2 die Animationsphase (zwischen 0 und 3), die sich alle vier Durchgänge ändert. So bekommt man einfach langsamere Animationen hin. Dann werden noch R3 auf 0 gesetzt und V (Sichtbar) auf 1.

              OBJ_ANIMATE Self.R1, 3, Self.R2, 3

              Self.R3 = 0
              Self.V = 1

Nun wird überprüft, ob PACMAN irgendwelche Extras bei sicht hat. Zuerst schauen wir nach, ob er das COOL-Item hat (Zeit bleibt stehen) und die Energie dieses Items größer als Null ist. Wenn ja, verringern wir die "Energie" dieses Items,  und R3 wird auf 12 gesetzt. Der Reservewert R3 wird bei Pacman dazu verwendet, zu bestimmen, in welchem Modus sich Pacman befindet. Dieser Wert wird immer zur Spritenummer hinzuaddiert. Da das COOL-Item die Zeit anhalten soll, so lange es wirkt, setzten wir alle anderen Objekte außer Pacman auf die letzte Position zurück. Ist das Item dann leer, wird es per OBJ_ITEM_DROP aus dem Inventar gestrichen.

     If OBJ_ITEM_HAVE(Self, NR_ITEM_COOL) Then
        Energy = OBJ_ITEM_ENERGY_GET(Self, NR_ITEM_COOL)
        If Energy > 0 Then
           OBJ_ITEM_ENERGY_SET Self, NR_ITEM_COOL, Energy - 1
           Self.R3 = 12
           For M = 0 To UBound(Obj)
              If Obj(M).T <> NR_PACMAN Then
                 Obj(M).X = Obj(M).OX
                 Obj(M).Y = Obj(M).OY
              End If
            Next M
        Else
           OBJ_ITEM_DROP Self, NR_ITEM_COOL
         End If
      End If

Etwas einfacher ist es bei dem Unsichbar-Item. Hier gilt zwar das gleiche System mit der Energie, jedoch wird als Auswirkung einfach nur Self.V auf 0 gesetzt, das Pacman für alle Monster unsichtbar macht. Außerdem haben wir hier einen anderen Wert für R3, damit Pacman auch auf dem Spielfeld unsichtbar erscheint.

     If OBJ_ITEM_HAVE(Self, NR_ITEM_INVISIBLE) Then
        Energy = OBJ_ITEM_ENERGY_GET(Self, NR_ITEM_INVISIBLE)
        If Energy > 0 Then
           OBJ_ITEM_ENERGY_SET Self, NR_ITEM_INVISIBLE, Energy - 1
           Self.R3 = 8
           Self.V = 0
        Else
           OBJ_ITEM_DROP Self, NR_ITEM_INVISIBLE
        End If
      End If

Bei dem Terminator-Item wird R3 auf 4 gesetzt und die Pacman-Geschwindigkeit mit 1,5 multipliziert. Die Auswirkungen werden an einer anderen Stelle berücksichtigt.

     If OBJ_ITEM_HAVE(Self, NR_ITEM_TERMINATOR) Then
        Energy = OBJ_ITEM_ENERGY_GET(Self, NR_ITEM_TERMINATOR)
        If Energy > 0 Then
           OBJ_ITEM_ENERGY_SET Self, NR_ITEM_TERMINATOR, Energy - 1
           Self.R3 = 4
           Self.SX = Self.SX * 1.5
           Self.SY = Self.SY * 1.5
        Else
           OBJ_ITEM_DROP Self, NR_ITEM_TERMINATOR
        End If
     End If

Zum Schluss wird dann der FRAME aus der ID + dem Reservewert R3 errechnet. Zusätzlich wird geprüft, in welche Richtung Pacman sieht. Schaut er nach oben wird PACMAN_UP als Grundnummer benutzt usw.

  Self.FRAME = Spr(ID_PACMAN_DOWN + Self.R3)
  If Self.SY<0 Then Self.FRAME=Spr(ID_PACMAN_UP+Self.R2 + Self.R3)
  If Self.SY>0 Then Self.FRAME=Spr(ID_PACMAN_DOWN+Self.R2+Self.R3)
  If Self.SX>0 Then Self.FRAME=Spr(ID_PACMAN_RIGHT+Self.R2+Self.R3)
  If Self.SX<0 Then Self.FRAME=Spr(ID_PACMAN_LEFT+Self.R2+Self.R3)

Das war auch schon das komplette Ereignis zum Anzeigen von PACMAN in allen Lebenslagen. Jetzt fehlen uns noch andere wichtige Ereignisse: ID_MOVE gibt an, dass sich das Objekt bewegen soll, und zwar um die übergebenen Werte Par1 (=X-Bewegung) und Par2 (=Y-Bewegung). Vorher wird hier, wie bei den Monstern auch, die Position gespeichert, damit sie später wieder geladen werden kann. Nach der Bewegung wird OBJ_COLL_FIELD aufgerufen, um zu prüfen, ob PACMAN immer noch auf einem leeren Feld steht.

  Case ID_MOVE
     Self.OX = Self.X
     Self.OY = Self.Y
     Self.X = Self.X + Par1
     Self.Y = Self.Y + Par2
     OBJ_COLL_FIELD Self

Ist dies nicht der Fall, ruft die Routine OBJ_COLL_FIELD das Ereignis ID_COLL_FIELD auf. Dieses sieht bei den Akteuren immer gleich aus:

  Case ID_COLL_WALL
     Self.X = Self.OX
     Self.Y = Self.OY
     Self.SX = Self.SX / 2
     Self.SY = Self.SY / 2
     OBJ_EVENT Self, ID_MOVE, ObjDummy, Self.SX, Self.SY

Die Position wird zurückgesetzt und die Geschwindigkeit halbiert. Anschließend wird noch einmal ID_MOVE aufgerufen, um zu testen, ob sich das Objekt wenigstens etwas weiterbewegen kann.

Damit wäre PACMAN selbst auch schon abgehandelt. Das war doch nicht so schwer, oder?

Nun zu den "essbaren" Punkten, die PACMAN während des Spiels einsammeln muss:

  Case NR_DOT:        'Punkte
     Select Case MESSAGE

Bei einer Kollision wird geprüft, ob PACMAN der Auslöser war. In dem Fall sucht die Routine, ob PACMAN bereits über ein Item NR_DOT verfügt. Wenn ja wird der Wert eingelesen, erhöht und dann wieder zurückgeschrieben. Wenn nein, wird dieses Item nun mit der "Energie" (was ja nicht unbedingt für Energie im herkömmlichen Sinne steht, sondern in diesem Fall eher Quantität bedeutet) 1 angelegt. Anschließend bekommt PACMAN noch 5 Punkte, und eine WAVE-Datei wird abgespielt. Am Ende wird das Objekt gelöscht, damit der Punkt nicht noch einmal eingesammelt werden kann.

      Case ID_COLL
         If Other.T = NR_PACMAN Then
            Dots = OBJ_ITEM_ENERGY_GET(Other, NR_DOT) + 1
            OBJ_ITEM_ENERGY_SET Other, NR_DOT, Dots
            OBJ_CLEAR Self
            LEVEL_SCORE = LEVEL_SCORE + 5
            WAV_PLAYBACK Pfad$ + "DOT.WAV"
         End If

Das Zeichnen verläuft denkbar einfach. Wir haben auch wieder einen Animationszähler (R2 nimmt Werte zwischen 0 und 3 an) und addieren den zu ID_DOT, was ja die Position der Punkte im Sprite-Datenfeld ist. Nun wird der Frame aktualisiert.

      Case ID_DRAW
         OBJ_ANIMATE Self.R1, 0, Self.R2, 3
         Self.FRAME = Spr(ID_DOT + Self.R2)

  End Select

Die Flagge, die das Ziel markiert, ist auch nicht mehr schwer zu realisieren:

  Case NR_FLAG:        'Flagge
     Select Case MESSAGE

Bei einer Kollision mit der Flagge wird geprüft, ob Pacman das Item NR_DOT bei sich hat und ob dessen "Energie" genauso groß ist, wie die Anzahl der Punkte insgesamt. In diesem Fall bekommt er 50 Punkte und LEVEL_NEXT wird auf 1 gesetzt, um dem Hauptprogramm mitzuteilen, dass das Level erledigt ist.

       Case ID_COLL
         If Other.T = NR_PACMAN Then
            If COUNT_DOTS=OBJ_ITEM_ENERGY_GET(Other, NR_DOT) Then
               LEVEL_NEXT = 1
               LEVEL_SCORE = LEVEL_SCORE + 50
            End If
         End If

Beim Zeichnen werden zwei Fälle unterschieden: Wenn alle Punkte gesammelt worden sind bewegt sich die Flagge, wird also per Animationszähler verändert, Wenn nicht, wird nur das erste Bild angezeigt. Daran kann der Spieler dann schon immer sehen, ob er sich zum Ausgang begeben kann oder nicht.

      Case ID_DRAW

         If COUNT_DOTS = OBJ_ITEM_ENERGY_GET(Obj(0), NR_DOT) Then
            OBJ_ANIMATE Self.R1, 1, Self.R2, 9
            Self.FRAME = Spr(ID_FLAG + Self.R2)
         Else
            Self.FRAME = Spr(ID_FLAG)
          End If

      End Select

Und nun das letzte Objekt aus USER_EVENT_PACMAN: Der Animator. Dieses Objekt dient dazu, eine Animation darzustellen. Das Objekt agiert und reagiert nicht. Es wird z.B. benutzt, wenn ein Monster stirbt, die "Überreste" darzustellen ohne einen eigenen Typ zu definieren. Das Objekt stellt immer eine Animation zwischen R3+0 und R3+3 dar. Darum muss der Wert R3 gesetzt werden, bevor man ein solches Objekt erstellt.

    Case NR_ANIMATOR:
        Select Case MESSAGE

          Case ID_DRAW
             OBJ_ANIMATE Self.R1, 0, Self.R2, 3
             Self.FRAME = Spr(Self.R3 + Self.R2)

        End Select
 
  4.9.2 Schlüsselerlebnisse voriges Thema [ Top ] nächstes Thema

Die Routine USER_EVENT_DOORS umfasst natürlich alle Tür- und Schlüssel-Objekte. Mehr gibt es da eigentlich nicht zu sagen.

Sub USER_EVENT_DOORS (Self As ObjType, MESSAGE, Other As ObjType,_
                      Par1, Par2)

   Select Case Self.T

Ein Schlüssel reagiert bei Kollision nur auf PACMAN. In dem Fall wird nachgesehen, wie viele Schlüssel dieser Art der Spieler bei sich trägt; dieser Wert wird erhöht und zurückgeschrieben. Dabei wird dann noch eine Sounddatei abgespielt. Auch die Schlüssel löschen sich danach selbst, damit sich der Spieler nicht unendlich daran bedienen kann.

  Case NR_ITEM_KEY1
      Select Case MESSAGE

       Case ID_COLL
          If Other.T = NR_PACMAN End If
             Anzahl = OBJ_ITEM_ENERGY_GET(Other, NR_ITEM_KEY1) + 1
             OBJ_ITEM_ENERGY_SET Other, NR_ITEM_KEY1, Anzahl
             OBJ_CLEAR Self
             WAV_PLAYBACK Pfad$ + "ITEM.WAV"
          End If

Das Zeichnen des Schlüssels ist wohl keiner Erklärung mehr bedürftig, oder?

      Case ID_DRAW
         OBJ_ANIMATE Self.R1, 3, Self.R2, 3
         Self.FRAME = Spr(ID_ITEM_KEY1 + Self.R2)

     End Select

Auch die Programmierung des zweiten Schlüssel ist kein großes Geheimnis mehr...

  Case NR_ITEM_KEY2
     Select Case MESSAGE
        Case ID_COLL
           If Other.T = NR_PACMAN Then
              Anzahl = OBJ_ITEM_ENERGY_GET(Other, NR_ITEM_KEY2) + 1
              OBJ_ITEM_ENERGY_SET Other, NR_ITEM_KEY2, Anzahl
              OBJ_CLEAR Self
              WAV_PLAYBACK Pfad$ + "ITEM.WAV"
           End If
        Case ID_DRAW
           OBJ_ANIMATE Self.R1, 3, Self.R2, 3
           Self.FRAME = Spr(ID_ITEM_KEY2 + Self.R2)
      End Select

Nun zu den Türen. Bei der Tür läuft es anders herum: Bei Berührung von PACMAN wird geprüft, ob ein Schlüssel da ist. Wenn ja, wird er entfernt bzw. die "Energie" (hierbei also die Quantität) wird um 1 verringert. Dann löscht sich die Tür selbst um den Weg freizugeben und gibt einen Sound aus. Wenn der Spieler keinen passenden Schlüssel hat wird er mit Hilfe der gesicherten Koordinaten OX und OY zurückgesetzt und gestoppt.

  Case NR_DOOR1
     Select Case MESSAGE

        Case ID_COLL
           If Other.T = NR_PACMAN Then
              If OBJ_ITEM_HAVE(Other, NR_ITEM_KEY1) Then
                 Anzahl=OBJ_ITEM_ENERGY_GET(Other,NR_ITEM_KEY1)-1
                 OBJ_ITEM_ENERGY_SET Other, NR_ITEM_KEY1, Anzahl
                 OBJ_CLEAR Self
                 WAV_PLAYBACK Pfad$ + "TÜR.WAV"
              Else
                 Other.X = Other.OX
                 Other.Y = Other.OY
                 Other.SX = 0
                 Other.SY = 0
              End If
           End If

    End Select

Die zweite Tür läuft parallel, ist aber natürlich nur mit dem 2. Schlüsseltyp "kompatibel".

  Case NR_DOOR2
     Select Case MESSAGE

        Case ID_COLL
           If Other.T = NR_PACMAN Then
              If OBJ_ITEM_HAVE(Other, NR_ITEM_KEY2) Then
                 Anzahl=OBJ_ITEM_ENERGY_GET(Other,NR_ITEM_KEY2)-1
                 OBJ_ITEM_ENERGY_SET Other, NR_ITEM_KEY2, Anzahl
                 OBJ_CLEAR Self
                 WAV_PLAYBACK Pfad$ + "TÜR.WAV"
              Else
                 Other.X = Other.OX
                 Other.Y = Other.OY
                 Other.SX = 0
                 Other.SY = 0
              End If
           End If

     End Select
 
  4.9.3 Hier gibt´s was extra voriges Thema [ Top ] nächstes Thema

Die Extras werden in der Prozedur USER_EVENT_ITEM behandelt. Der Kopf der Prozedur ist klar.

Sub USER_EVENT_ITEM (Self As ObjType, MESSAGE, Other As ObjType,_
                    Par1, Par2)

  Select Case Self.T

Das COOL-Powerup (oder Speed-PowerUp) stoppt die Zeit. Aber an dieser Stelle ist das unwichtig, da sich das Objekt hier ja nur in "instant"-Form auf dem Bildschirm befindet. Wenn PACMAN es jedoch aufnimmt, sorgt es dafür, dass alle anderen aktiven Extras fallengelassen werden (nach dem Motto:" Bin ich nicht Dein Lieblings-PowerUp?"). Dann löscht es sich selbst und fügt sich dem Inventar zu. Dabei setzt es die Energie auf 300 (in diesem Fall ist "Energie" die Zeit, die das Extra noch anhält). Zum Schluss wird noch eine WAVE-Datei abgespielt. Das ID_DRAW-Event ist hier fast genauso wie bei den Schlüsseln, dem Animator und den Türen. Daher dazu keine weiteren Ausführungen.

    Case NR_ITEM_COOL:  'Speed-PowerUp
       Select Case MESSAGE
          Case ID_COLL
             If Other.T = NR_PACMAN Then
                OBJ_ITEM_DROP Other, NR_ITEM_COOL
                OBJ_ITEM_DROP Other, NR_ITEM_INVISIBLE
                OBJ_ITEM_DROP Other, NR_ITEM_TERMINATOR
                OBJ_ITEM_GET Other, NR_ITEM_COOL, 300
                OBJ_CLEAR Self
                LEVEL_SCORE = LEVEL_SCORE + 20
                WAV_PLAYBACK Pfad$ + "ITEM.WAV"
              End If
           Case ID_DRAW
             OBJ_ANIMATE Self.R1, 3, Self.R2, 3
             Self.FRAME = Spr(ID_ITEM_COOL + Self.R2)
       End Select

Die meisten anderen PowerUps sehen ähnlich aus. Daher liste ich sie nur noch der Vollständigkeit halber auf und beschreibe als nächstes nur den Totenkopf, da er eine andere Auswirkung hat.

    Case NR_ITEM_INVISIBLE:  'Unsichtbar-PowerUp
       Select Case MESSAGE

          Case ID_COLL
              If Other.T = NR_PACMAN Then
                OBJ_ITEM_DROP Other, NR_ITEM_COOL
                OBJ_ITEM_DROP Other, NR_ITEM_INVISIBLE
                OBJ_ITEM_DROP Other, NR_ITEM_TERMINATOR
                OBJ_ITEM_GET Other, NR_ITEM_INVISIBLE, 300
                OBJ_CLEAR Self
                LEVEL_SCORE = LEVEL_SCORE + 10
                WAV_PLAYBACK Pfad$ + "ITEM.WAV"
             End If

          Case ID_DRAW
             OBJ_ANIMATE Self.R1, 3, Self.R2, 3
             Self.FRAME = Spr(ID_ITEM_INVISIBLE + Self.R2)

     End Select

  Case NR_ITEM_TERMINATOR:  'Terminator-PowerUp
     Select Case MESSAGE
        Case ID_COLL

           If Other.T = NR_PACMAN Then
              OBJ_ITEM_DROP Other, NR_ITEM_COOL
              OBJ_ITEM_DROP Other, NR_ITEM_INVISIBLE
              OBJ_ITEM_DROP Other, NR_ITEM_TERMINATOR
              OBJ_ITEM_GET Other, NR_ITEM_TERMINATOR, 300
              OBJ_CLEAR Self
              LEVEL_SCORE = LEVEL_SCORE + 10
              WAV_PLAYBACK Pfad$ + "ITEM.WAV"
           End If

        Case ID_DRAW
           OBJ_ANIMATE Self.R1, 3, Self.R2, 3
           Self.FRAME = Spr(ID_ITEM_TERMINATOR + Self.R2)

    End Select

Der Totenkopf hat etwas andere Auswirkungen: Er zieht PACMAN 80 Energiepunkte ab, was mehr als die Hälfte ist. Sollte der Spieler also zwei dieser Kollegen berühren, stattet er den ewigen Jagdgründen einen Besuch ab. Natürlich wird auch dieses Objekt gelöscht und ein Ton ausgegeben.

    Case NR_ITEM_DEAD:  'Todes-"PowerUp"
       Select Case MESSAGE

          Case ID_COLL
             If Other.T = NR_PACMAN Then
                Other.E = Other.E - 80
                OBJ_CLEAR Self
                WAV_PLAYBACK Pfad$ + "ITEM.WAV"
              End If

          Case ID_DRAW
             OBJ_ANIMATE Self.R1, 3, Self.R2, 3
             Self.FRAME = Spr(ID_ITEM_DEAD + Self.R2)

       End Select
 
  4.9.4a Monster voriges Thema [ Top ] nächstes Thema
Sub USER_EVENT_MONSTER (Self As ObjType, MESSAGE, Other As _
                        ObjType, Par1, Par2)

  Select Case Self.T

Die Steuerung der Monster ist nicht so leicht wie die von Pacman, da wir hier jetzt eine Art KI, eine künstliche Intelligenz, programmieren müssen - wenn die KI nicht gut ist, könnte man auch von einer "krankhaften Intelligenz" reden. Daher müssen wir hier einiges tun, damit die Monster nicht immer gegen die Wände laufen, oder gar auf Artgenossen losgehen.

     Case NR_MONSTER_BLUE

        Select Case MESSAGE
           Case ID_DRAW
              MONSTER_DRAW Self, ID_MONSTER_BLUE
           Case ID_MOVE
              MONSTER_MOVE Self, Par1, Par2, 2
              MONSTER_HUNT Self, 20, 2
           Case ID_COLL, ID_COLL_WALL
              MONSTER_COLL Self, Other, 2, 2, ID_MONSTER_BLUE
        End Select

Wie Sie sehen, besteht die Steuerung eines Monsters nur aus dem Aufrufen von Subprozeduren, denen von Monster zu Monster andere Werte übergeben werden. Ich werde nun kurz die Syntax dieser Funktionen ansprechen und mit der Monster-Prozedur (wie schön zweideutig) fortfahren.

  MONSTER_COLL <Objekt>, <Anderes>, <Stärke>, <Speed>, <Frame>
  MONSTER_DRAW <Objekt>, <Frame>
  MONSTER_HUNT <Objekt>, <Sichtweite>, <Speed>
  MONSTER_MOVE <Objekt>, <X>, <Y>, <Speed>

<Objekt> 
<Anderes>
<Stärke>
<Speed>
<Sichtw.>
<Frame> 

steht für das eigene Objekt, daher hier Self eintragen
steht für das andere Objekt, also Other eintragen
ist die Schlagkraft gegenüber PACMAN
ist die normale Geschwindigkeit
wie viele Blöcke weit das Monster "sehen" kann
ist das Anfangsbild als NR_xxxxxxx

Nun kommen noch die anderen Monster-Typen, die sich vom ersten nur durch andere Zahlen für Geschwindigkeit, Sichtweite und Stärke unterscheiden. Außerdem ist natürlich noch der Grund-Frame anders. Aber von Prinzip her gleich, was soviel bedeutet wie: kein Kommentar ;-)

  Case NR_MONSTER_RED
     Select Case MESSAGE
        Case ID_DRAW
           MONSTER_DRAW Self, ID_MONSTER_RED
        Case ID_MOVE
           MONSTER_MOVE Self, Par1, Par2, 3
           MONSTER_HUNT Self, 40, 3
        Case ID_COLL, ID_COLL_WALL
           MONSTER_COLL Self, Other, 4, 3, ID_MONSTER_RED
    End Select

  Case NR_MONSTER_BROWN
     Select Case MESSAGE
        Case ID_DRAW
           MONSTER_DRAW Self, ID_MONSTER_BROWN
        Case ID_MOVE
           MONSTER_MOVE Self, Par1, Par2, 3
           MONSTER_HUNT Self, 80, 3
        Case ID_COLL, ID_COLL_WALL
           MONSTER_COLL Self, Other, 3, 3, ID_MONSTER_BROWN
     End Select

  Case NR_MONSTER_MAGENTA
     Select Case MESSAGE
        Case ID_DRAW
           MONSTER_DRAW Self, ID_MONSTER_MAGENTA
        Case ID_MOVE
           MONSTER_MOVE Self, Par1, Par2, 3
           MONSTER_HUNT Self, 80, 5
        Case ID_COLL, ID_COLL_WALL
           MONSTER_COLL Self, Other, 8, 3, ID_MONSTER_MAGENTA
     End Select

  Case NR_MONSTER_HIPPIE
     Select Case MESSAGE
        Case ID_DRAW
           MONSTER_DRAW Self, ID_MONSTER_HIPPIE
        Case ID_MOVE
           MONSTER_MOVE Self, Par1, Par2, 3
           MONSTER_HUNT Self, 100, 6
        Case ID_COLL, ID_COLL_WALL
           MONSTER_COLL Self, Other, 20, 4, ID_MONSTER_HIPPIE
     End Select
 
  4.9.4b Monster-Innereien voriges Thema [ Top ] nächstes Thema

Fangen wir nun mit MONSTER_COLL an. Diese Routine wird immer dann aufgerufen, wenn eine Kollision eines Monsters mit PACMAN vorliegt. Hierbei wird geprüft, ob PACMAN gerade ein "Terminator" ist und das Monster trifft, oder ob das Monster ihm was antut. Der Prozedur werden beide Objekte, die Geschwindigkeitund die Schlagkraft des Monsters übergeben.

Sub MONSTER_COLL(Self As ObjType,Other As ObjType,Hit,Speed,FRAME)

Als erstes wird geprüft, ob es sich um PACMAN handelt. In diesem Fall wird nachgefragt, ob er das Terminator-Item bei sich hat. Wenn ja, bekommt das Monster 10 Energiepunkte abgezogen und eine Sounddatei wird ausgegeben. Stirbt das Monster dabei, wird es in einen Animator umgewandelt, der die Sprites FRAME+8 bis FRAME+8+3 abspielt. Außerdem bekommt PACMAN Punkte gutgeschrieben, die sich aus der doppelten Schlagkraft des Monsters ergeben. Ist PACMAN jedoch im Normalmodus, bekommt er die Schlagkraft des Monsters von der Energie abgezogen.

  If Other.T = NR_PACMAN Then
     If OBJ_ITEM_HAVE(Other, NR_ITEM_TERMINATOR) Then
        Self.E = Self.E - 10
        WAV_PLAYBACK Pfad$ + "DOT.WAV"
        If Self.E < 0 Then
           Self.T = NR_ANIMATOR
           Self.R3 = FRAME + 8
           LEVEL_SCORE = LEVEL_SCORE + Hit * 2
           WAV_PLAYBACK Pfad$ + "MONSTER.WAV"
        End If
     Else
        Other.E = Other.E - Hit
     End If
   Else

Handelt es sich bei dem anderen Objekt nicht um PACMAN, wird die Position unter Verwendung der Sicherungskoordinaten OX und OY zurückgesetzt, und die Prozedur MONSTER_RANDOM_SPEED wird aufgerufen. Diese Prozedur wird mehrfach benötigt und sorgt dafür, dass das Monster einen anderen Weg auslöst.

     Self.X = Self.OX
     Self.Y = Self.OY
     MONSTER_RANDOM_SPEED Self, Speed
   End If
End Sub

In MONSTER_RANDOM_SPEED bekommt das Monster zufällig eine neue Geschwindigkeit in eine Richtung. Zugegeben, dieses Verfahren ist sehr primitiv. Man hätte besser prüfen sollen, wohin das Monster ausweichen kann, aber dieses Verfahren funktioniert auch. Es wird hier einfach eine Zufallszahl zwischen 0 und 3 ausgesucht. Danach wird die Richtung entschieden. Die Geschwindigkeit entspricht der übergebenen, damit die verschiedenen Monstergattungen sich auch verschieden schnell bewegen.

Sub MONSTER_RANDOM_SPEED (Self As ObjType, S)
  A = Fix(Rnd * 4)
  Select Case A
     Case 0: Self.SX = -S: Self.SY = 0
     Case 1: Self.SX = 0: Self.SY = -S
     Case 2: Self.SX = S: Self.SY = 0
     Case 3: Self.SX = 0: Self.SY = S
  End Select
End Sub

Die nächste Routine auf dem Plan heißt MONSTER_DRAW. Sie ist eigentlich schnell abgehandelt:  Per OBJ_ANIMATE wird das Objekt animiert. Weiterhin wird nachgesehen, ob PACMAN das Terminator-Extra hat oder nicht. Wenn ja wird das Monster flüchtend dargestellt. Wenn nein, sieht es normal aus. Hier wird auch überprüft, ob die Geschwindigkeit aus irgendeinem Grund 0 beträgt. Da sich die Monster immer bewegen sollen (keine Angst, die brauchen schon keinen Schlaf) wird dann wieder MONSTER_RANDOM_SPEED aufgerufen.

Sub MONSTER_DRAW (Self As ObjType, Default)

  OBJ_ANIMATE Self.R1, 3, Self.R2, 3

  If OBJ_ITEM_HAVE(Obj(0), NR_ITEM_TERMINATOR) Then
     Self.FRAME = Spr(Default + Self.R2 + 4)
  Else
     Self.FRAME = Spr(Default + Self.R2)
  End If

  If Self.SX = 0 And Self.SY = 0 Then
     MONSTER_RANDOM_SPEED Self, 2
  End If

End Sub

Nun kommen wir zur eigentlichen Monster-KI. MONSTER_HUNT wird immer dann aufgerufen, wenn ein Monster sich bewegt. Es bekommt die Sichtweite und Geschwindigkeit übergeben und prüft, ob sich PACMAN in Reichweite befindet. (Bei dem folgend Listing sind die Einrückungsabstände kleiner als sonst, damit keine Zeile breiter als die Seite ist. Schließlich ist die Leserlichkeit hier wichtiger als der Ordnungssinn, oder?)

Sub MONSTER_HUNT (Self As ObjType, View, Speed)

Gleich am Anfang wird geprüft, ob das Objekt 0, als unser Kollege PACMAN sichtbar ist. Wenn nicht, hat er das Unsichtbar-Extra, und darf nicht gejagt werden, daher EXIT SUB.

  If Obj(0).V = 0 Then Exit Sub

Und nun schauen wir nach, ob PACMAN sowohl in X- als auch in Y-Richtung innerhalb des Sichtfeldes ist. Wenn nicht, können wir uns nämlich die Arbeit sparen. Es ist übrigens wichtig, solche Fälle vorher abzuchecken, um Rechenzeit zu sparen.

  If Abs(Obj(0).X - Self.X) < View Then
  If
Abs(Obj(0).Y - Self.Y) < View Then

Nun setzen wir die Monstergeschwindigkeit zurück. Wir errechnen die Position von PACMAN und dem Monster, wobei wir die Koordinaten jeweils durch 2 teilen. Dies hat folgenden Hintergedanken: Solange die X-Koordinate von PACMAN kleiner als die des Monsters ist läuft das Monster nach links. Aber es kann ja sein, dass das Monster so schnell ist, dass es PACMAN nicht genau trifft und zu weit rennt! In dem Fall würde es normal wieder zurücklaufen. Das Monster würde sich ständig hin und her bewegen. Das können wir so umgehen, indem wir nicht in Pixel rechnen sondern in Schritten.

     Self.SX = 0
     Self.SY = 0

     PacX = Fix(Obj(0).X / Speed)
     PacY = Fix(Obj(0).Y / Speed)
     MyX = Fix(Self.X / Speed)
     MyY = Fix(Self.Y / Speed)

Die eigentliche KI besteht darin, zu prüfen ob PACMAN für das Monster gefährlich ist oder umgekehrt, um dann eine entsprechende Bewegung einzuleiten. Wenn PACMAN harmlos ist wird geprüft, in welcher Richtung er sich befindet UND ob man dorthin gehen kann. Beim anderen Fall wird geprüft, ob man von PACMAN weggehen kann.

     If OBJ_ITEM_HAVE(Obj(0), NR_ITEM_TERMINATOR) = 0 Then
        If
PacX < MyX And OBJ_LEFT(Self, 20) Then Self.SX = -Speed
        If PacX > MyX And OBJ_RIGHT(Self, 20) Then Self.SX = Speed
        If PacY < MyY And OBJ_UP(Self, 20) Then Self.SY = -Speed
        If PacY > MyY And OBJ_DOWN(Self, 20) Then Self.SY = Speed
     Else
        If PacX < MyX And OBJ_RIGHT(Self, 20) Then Self.SX = Speed
        If PacX > MyX And OBJ_LEFT(Self, 20) Then Self.SX = -Speed
        If PacY < MyY And OBJ_DOWN(Self, 20) Then Self.SY = Speed
        If PacY > MyY And OBJ_UP(Self, 20) Then Self.SY = -Speed
     End If

Nach diesem Abschnitt wird noch getestet, ob kein Fall zutrifft. Dann wird nämlich wieder die bekannte Routine MONSTER_RANDOM_SPEED aufgerufen.

      If Self.SX = 0 And Self.SY = 0 Then
         MONSTER_RANDOM_SPEED Self, Speed
      End If
    End If
  End If

End Sub

Und nun zur letzten MONSTER-Routine: MONSTER_MOVE wird dann aufgerufen, wenn sich das Monster bewegen soll. Hier wird etwas mehr getan als bei MONSTER_RANDOM_MOVE.

Sub MONSTER_MOVE (Self As ObjType, Par1, Par2, Speed)

Als erstes sichern wir die Koordinaten. Dann bewegen wir das Monster weiter und prüfen auf Kollisionen mit dem Spielfeld.

  Self.OX = Self.X
  Self.OY = Self.Y
  Self.X = Self.X + Par1
  Self.Y = Self.Y + Par2
  OBJ_COLL_FIELD Self

Jetzt zählen wir, zu wie vielen Seiten das Monster gehen kann. Sind beispielsweise die Felder links und rechts frei, so haben wir zwei Wege zur Auswahl.

  Ways = 0
   If OBJ_LEFT(Self, 20) Then Ways = Ways + 1
   If OBJ_RIGHT(Self, 20) Then Ways = Ways + 1
   If OBJ_UP(Self, 20) Then Ways = Ways + 1
   If OBJ_DOWN(Self, 20) Then Ways = Ways + 1

Normal ist die Chance, dass sich das Monster in einem Gang (also zwei freie Felder anliegend) befindet, nur 2 zu 200. Haben wir jedoch mehr Wege zur Wahl (z.B. an einer Kreuzung oder auf offenem Feld), so ist die Chance 20 zu 200. Das  hört sich wenig an, jedoch müssen Sie bedenken, dass das MOVE-Event sehr oft aufgerufen wird, während das Monster eine Kreuzung passiert.

  Chance = 2
   If Ways > 2 Then Chance = 20

Sollte die Chance gekommen sein, sprich der Zufallswert zwischen 0 und 199 (RND*200) ist kleiner als die Chance, so wird wieder MONSTER_RANDOM_SPEED benutzt, um einen anderen oder evtl. auch den gleichen Weg einzuschlagen.

  If Rnd * 200 < Chance Then
     MONSTER_RANDOM_SPEED Self, Speed
  End If
End Sub

Der Vorteil dieser starken Unterteilung liegt auf der Hand: Wollen Sie mal ein neues Monster hinzufügen brauchen Sie nur ein, zwei Werte ändern und sind fertig.

 
  4.9.5 Hebeleien voriges Thema [ Top ] nächstes Thema

Nun sind wir endlich wieder bei den eigentlichen EVENT-Routinen. Diesmal besprechen wir USER_EVENT_SWITCH. Diese Prozedur steuert die Schalter und entsprechenden Blöcke auf dem Spielfeld.

Sub USER_EVENT_SWITCH (Self As ObjType, MESSAGE, Other As _
    ObjType, Par1, Par2)

  Select Case Self.T

Wir besprechen jetzt wieder exemplarisch einen Schalter.

      Case NR_SWITCH_RED

       Select Case MESSAGE

Bei der Kollision mit PACMAN wird der Schalter umgelegt und das nur, wenn der Schalter sich in Ausgangsstellung (R1=0) befindet. Es werden dann alle Objekte durchsucht. Wenn ein zu dem Schalter passender Block gefunden wurde wird dieser gelöscht und so der Weg freigegeben. Zum Abschluss kommt noch eine WAVE-Datei.

        Case ID_COLL
           If Other.T = NR_PACMAN And Self.R1 = 0 Then
              Self.R1 = 1
              For M = 0 To UBound(Obj)
                If Obj(M).T=NR_BLOCK_RED Then OBJ_CLEAR Obj(M)
              Next M
              WAV_PLAYBACK Pfad$ + "SCHALTER.WAV"
            End If

Beim DRAW-Ereignis wird der Grundframe genommen und R1 hinzu addiert. Dadurch hat der Schalter in der Ausgangsstellung eine andere Sprite-Nummer als wenn er umgelegt ist.

         Case ID_DRAW
            Self.FRAME = Spr(ID_SWITCH_RED + Self.R1)

       End Select

Genauso sieht's bei den anderen Schaltern aus.

     Case NR_SWITCH_GREEN
       Select Case MESSAGE
          Case ID_COLL
           If Other.T = NR_PACMAN And Self.R1 = 0 Then
              Self.R1 = 1
                For M = 0 To UBound(Obj)
                If Obj(M).T = NR_BLOCK_GREEN Then OBJ_CLEAR Obj(M)
                Next M
               WAV_PLAYBACK Pfad$ + "SCHALTER.WAV"
           End If
         Case
ID_DRAW
           Self.FRAME = Spr(ID_SWITCH_GREEN + Self.R1)
        End Select

     Case
NR_SWITCH_BLUE
        Select Case MESSAGE
          Case ID_COLL
            If Other.T = NR_PACMAN And Self.R1 = 0 Then
             Self.R1 = 1
              For M = 0 To UBound(Obj)
                If Obj(M).T = NR_BLOCK_BLUE Then OBJ_CLEAR Obj(M)
              Next M
             WAV_PLAYBACK Pfad$ + "SCHALTER.WAV"
            End If
         Case
ID_DRAW
           Self.FRAME = Spr(ID_SWITCH_BLUE + Self.R1)
        End Select

     Case
NR_SWITCH_YELLOW
        Select Case MESSAGE
          Case ID_COLL
            If Other.T = NR_PACMAN And Self.R1 = 0 Then
             Self.R1 = 1
             For M = 0 To UBound(Obj)
                If Obj(M).T = NR_BLOCK_YELLOW Then OBJ_CLEAR Obj(M)
             Next M
             WAV_PLAYBACK Pfad$ + "SCHALTER.WAV"
            End If
         Case
ID_DRAW
           Self.FRAME = Spr(ID_SWITCH_YELLOW + Self.R1)
        End Select

Die Schalter können wir zum Glück kurz und schmerzlos besprechen: Sie sind praktisch passiv und sorgen nur dafür, dass PACMAN zurückgesetzt wird, wenn er die Blöcke berührt. Dies wird, wer hätte es gedacht, mit Hilfe der Sicherungskoordinaten OX und OY gemacht. Außerdem wird PACMANs Geschwindigkeit auf 0 gesetzt.

  Case NR_BLOCK_RED, NR_BLOCK_GREEN, NR_BLOCK_BLUE, NR_BLOCK_YELLOW
     Select Case MESSAGE
         Case ID_COLL
            If Other.T = NR_PACMAN Then
              Other.X = Other.OX
              Other.Y = Other.OY
              Other.SX = 0
              Other.SY = 0
            End If
     End Select

Nun schließen wir die Prozedur ab.

  End Select

End Sub
 
  4.9.6 Teleportation voriges Thema [ Top ] nächstes Thema

Jetzt, wo schon die Teleportation in der Wissenschaft funktioniert hat (zumindest mit einem Lichtteilchen) liegt der folgende Teil gar nicht mal so sehr im Sience-Fiction-Bereich: Die Prozedur USER_EVENT_TELE verwaltet die Teleporter auf dem Schirm, mit denen der Spieler sich von einer Seite zur anderen "beamen" kann.

Sub USER_EVENT_TELE (Self As ObjType, MESSAGE, Other As ObjType,_
                     Par1, Par2)
  Select Case Self.T

Die Teleporter reagieren, sobald PACMAN mit Ihnen kollidiert. Dann sucht der betreffende Teleporter sein Pendant. Dies wird dadurch erreicht, daß wir alle Objekte durchlaufen und prüfen, ob es sich um einen Teleporter der gleichen Farbe handelt, der an einer anderen Position steht. Letzteres ist sehr wichtig, damit wir nicht den gleichen Teleporter erwischen, bei dem die Reise begann.

Case NR_TELE_RED
  Select Case MESSAGE
    Case ID_COLL
      If Other.T = NR_PACMAN And Self.R1 = 0 Then
        For
M = 0 To UBound(Obj)
          If Obj(M).T = NR_TELE_RED And (Obj(M).X <> Self.X Or _
                        Obj(M).Y <> Self.Y) Then

Wurde einer gefunden, wird PACMAN teleportiert, indem seine Koordinaten auf den anderen Teleporter gesetzt werden. Danach wird eine Wavedatei ausgegeben. Da er aber nicht darauf stehen bleiben kann wird geprüft, welche Felder es zum Ausweichen gibt. Wird eins gefunden so bewegt sich PACMAN dorthin, und die Routine wird beendet.

            Other.X = Obj(M).X
            Other.Y = Obj(M).Y
            WAV_PLAYBACK Pfad$ + "TELEPORT.WAV"
            If OBJ_LEFT(Obj(M),20)Then Other.X=Other.X-20:Exit Sub
            If
OBJ_RIGHT(Obj(M),20)Then Other.X=Other.X+20:Exit Sub
            If
OBJ_UP(Obj(M),20)Then Other.Y=Other.Y-20:Exit Sub
            If OBJ_DOWN(Obj(M),20)Then Other.Y=Other.Y+20:Exit Sub
          End If
        Next
M
      End If

Beim Zeichnen wird eigentlich nichts besonderes gemacht, alles wie gehabt.

    Case ID_DRAW
      OBJ_ANIMATE Self.R1, 0, Self.R2, 3
      Self.FRAME = Spr(ID_TELE_RED + Self.R2)

  End Select

Case NR_TELE_BLUE
  Select Case MESSAGE

    Case ID_COLL
      If Other.T = NR_PACMAN And Self.R1 = 0 Then
        For M = 0 To UBound(Obj)
          If Obj(M).T = NR_TELE_BLUE And(Obj(M).X <> Self.X Or _
                        Obj(M).Y <> Self.Y) Then
            Other.X = Obj(M).X
            Other.Y = Obj(M).Y
            WAV_PLAYBACK Pfad$ + "TELEPORT.WAV"
            If OBJ_LEFT(Obj(M),20)Then Other.X=Other.X-20:Exit Sub
            If OBJ_RIGHT(Obj(M),20)Then Other.X=Other.X+20:Exit Sub
            If OBJ_UP(Obj(M),20)Then Other.Y=Other.Y-20:Exit Sub
            If OBJ_DOWN(Obj(M),20)Then Other.Y=Other.Y+20:Exit Sub
          End If
        Next M
      End If

    Case ID_DRAW
      OBJ_ANIMATE Self.R1, 0, Self.R2, 3
      Self.FRAME = Spr(ID_TELE_BLUE + Self.R2)
  End Select

Case NR_TELE_GREEN

  Select MESSAGE
    Case ID_COLL
      If Other.T = NR_PACMAN And Self.R1 = 0 Then
        For
M = 0 To UBound(Obj)
          If Obj(M).T = NR_TELE_GREEN And (Obj(M).X <> Self.X Or _
                                         Obj(M).Y <> Self.Y) Then
            Other.X = Obj(M).X
            Other.Y = Obj(M).Y
            WAV_PLAYBACK Pfad$ + "TELEPORT.WAV"
            If OBJ_LEFT(Obj(M),20)Then Other.X=Other.X-20:Exit Sub
            If
OBJ_RIGHT(Obj(M),20)Then Other.X=Other.X+20:Exit Sub
            If
OBJ_UP(Obj(M),20)Then Other.Y=Other.Y-20:Exit Sub
            If
OBJ_DOWN(Obj(M),20)Then Other.Y=Other.Y+20:Exit Sub
          End If
        Next
M
      End If

    Case ID_DRAW
      OBJ_ANIMATE Self.R1, 0, Self.R2, 3
      Self.FRAME = Spr(ID_TELE_GREEN + Self.R2)

  End Select

Case
NR_TELE_YELLOW
  Select Case MESSAGE
 
    Case ID_COLL
      If Other.T = NR_PACMAN And Self.R1 = 0 Then
        For
M = 0 To UBound(Obj)
          If Obj(M).T = NR_TELE_YELLOW And (Obj(M).X <> Self.X Or_
                        Obj(M).Y <> Self.Y) Then
            Other.X = Obj(M).X
            Other.Y = Obj(M).Y
            WAV_PLAYBACK Pfad$ + "TELEPORT.WAV"
            If OBJ_LEFT(Obj(M),20)Then Other.X=Other.X-20:Exit Sub
            If
OBJ_RIGHT(Obj(M),20)Then Other.X=Other.X+20:Exit Sub
            If
OBJ_UP(Obj(M),20)Then Other.Y=Other.Y-20:Exit Sub
            If
OBJ_DOWN(Obj(M),20)Then Other.Y=Other.Y+20:Exit Sub
          End If
        Next
M
      End If

    Case
ID_DRAW
      OBJ_ANIMATE Self.R1, 0, Self.R2, 3
      Self.FRAME = Spr(ID_TELE_YELLOW + Self.R2)

  End Select
End Sub
 
  4.10 Ready 2 go voriges Thema [ Top ] nächstes Thema

Geschafft! Das Spiel ist soweit einsatzbereit. Sollte es bei Ihnen aber trotz Sorgfalt beim Abtippen (oder Kopieren *g*) nicht laufen, können Sie den Quellcode aus dem letzten Teil benutzen. Ich wollte hier aus Speichergründen nicht noch einmal die ganzen Dateien anhängen.

Ich konnte auch nicht noch einmal testen, ob der hier dokumentierte Code komplett ist. Wenn also irgendwo etwas fehlt, bitte ein E-Mail an   go12power@hotmail.com

Falls das Spiel richtig läuft, wünsche ich Ihnen erst einmal viel Spaß damit. Vielleicht haben Sie ja noch Lust es zu erweitern oder zu ändern - es steht Ihnen frei.

Kleiner Hinweis: Wenn ich in diesem Kurs von PACMAN rede meine ich natürlich VB-Kurs PACMAN ;-)

 
  4.11 Ende Teil 4 voriges Thema [ Top ]   

Damit wäre das PACMAN-Spiel komplett (endlich, endlich, endlich) abgeschlossen. Wie es weitergeht steht bis jetzt noch nicht fest, aber Sie können mir gerne Ihre Meinung sagen bzw. schreiben. Wie wäre es mit einem Kurs zu einem 3D-Spiel? (Diesmal aber nicht ganz so umfangreich, wie dieses Spiel hier). Oder vielleicht ein Kurs zur API oder ein weiterer Teil Grafik mit Dateiformaten?

Wenn Sie weitere Themenwünsche oder Ideen für den nächsten Aufbaukurs haben, schreiben Sie mir doch einfach ein E-Mail. Meine Adresse: 

 go12power@hotmail.com

Wie immer freue ich mich auch über jede Art von Feedback zu dem Kurs und über Fragen zu VB oder anderen Themen. Wenn Sie Zeit haben, können Sie ja die Programmierer-Konferenz besuchen, die immer am Donnerstag um 20:00 Uhr im Konferenzraum 2 von AOL stattfindet (STRG+K Konferenzen, Raum 2).

Ich hoffe, wir sehen uns dann wieder.

  Download
Spielekurs_Teil4.zip
 (245,6 kB)
Downloadzeit: <1 Min. - 28.8k / <1 Min. - ISDN Downloads bisher: [ 5282 ]

Startseite | VB/VBA-Tipps | Projekte | Tutorials | API-Referenz | Komponenten | Bücherecke | VB-/VBA-Forum | VB.Net-Forum | DirectX | DirectX-Forum | Foren-Archiv | VB.Net | Chat | Links | Suchen | Stichwortverzeichnis | Feedback | Impressum

Seite empfehlen Bug-Report

Letzte Aktualisierung: Freitag, 12. Juli 2002