1.14 |
Der Countdown läuft |
[ Top ]
|
Jetzt bekommt die Form des Spiels noch den letzen Schliff: |
|
Unter das Bildfeld Display setzen Sie zwei weitere Bildfelder. Diese zeigen später im Spiel die Energie und die
Schusskraft an. Nennen Sie das obere Bildfeld ENERGY und das untere
FKRAFT. Setzen Sie beim oberen die Hintergrundfarbe auf ein dunkles Rot und beim unteren auf ein dunkles
Cyan. Darunter kommt ein Bezeichnungsfeld mit dem Namen PUNKTEANZ. Setzen Sie
Caption auf "0", die Hintergrundfarbe auf dunkelgrün und die Vordergrundfarbe auf ein helles Gelb. Setzen Sie außerdem
BorderStyle auf 1. |
Das Design der Form ist nun abgeschlossen. Die Bildfelder mit den Sprites sind hier nicht zu sehen, da ich diese im unteren Teil der Form
platziert habe. Denken Sie daran, dass Sie alle Bildfelder mit Sprites, mit Masken und mit dem Hintergrund unsichtbar machen. Auch sollten Sie darauf achten,
dass das Display nicht (!) höher ist als der Hintergrund. In diesem Fall würde nämlich im späteren Spiel eine Lücke unter dem scrollenden Hintergrund entstehen. |
Der Rahmen ist fertig - jetzt kommt das Spiel. Also los! |
|
|
Private Sub
SpielStart()
Dim ObjectX(20) As Long
Dim ObjectY(20) As Long
Dim ObjectT(20) As Long
Dim ObjectE(20) As Long
Dim ObjectG(20) As Long
Dim ObjectA(20) As Long
Dim ObjectAC(20) As Long
Dim HGrundX As Long,
Punkte As Long
Dim FeuerKraft As Long,
Feuer As Long
Dim Pfad As String,
w As Long,
h As Long
Dim m As Long,
i As Long,
i2 As Long
Dim a As Long,
x As Long
Pfad = App.Path & "\"
MIDI_PLAY Pfad & "MUSIK.MID"
w = DISPLAY.ScaleWidth
h = DISPLAY.ScaleHeight
|
|
|
Als erstes werden die Datenfelder erstellt, die die Informationen über die Spielobjekte aufnehmen. Anschließend wird die Variable Pfad auf den Pfad gesetzt, indem sich die Dateien befinden. Hier müssen Sie den Pfad einsetzen, der zu den entsprechenden Sounddateien auf Ihrer Festplatte führt. Vergessen Sie nicht,
dass am Ende auf jeden Fall einen Backslash steht! Sonst kommt es zu Fehlern. Später, wenn Sie das Spiel fertig haben und kompilieren wollen, sollten Sie Pfad$ auf " " setzen, damit die Dateien im aktuellen Verzeichnis gesucht werden. Dann brauchen sich die Sounddateien nur im gleichen Verzeichnis mit dem Programm befinden, und sie werden abgespielt. Sollte dabei etwas nicht funktionieren, so ist das auch kein Beinbruch, da die Soundroutinen gegen alle Arten von
Fehler "immun" sind. Schlimmstenfalls kommt der Spieler nicht in den
Genuss der akustischen Untermalung ;-) |
Jetzt müssen wir das Spielobjekt des Spielers initialisieren. Dazu legen wir jetzt fest, welche Typennummer
(ObjectT(x)) für welches Objekt steht: |
ObjecT(x) |
Bedeutung |
1 |
Raumschiff |
2 |
Asteroid |
3 |
Laserschuss |
4 |
Explodierender Asteroid |
5 |
Explodierendes Raumschiff |
|
|
|
|
Start:
ObjectX(0) = w / 2
ObjectY(0) = h / 2
ObjectT(0) = 1
ObjectE(0) = 200
ObjectG(0) = 0
ObjectA(0) = 0
|
|
|
Bevor wir dies tun setzen wir allerdings noch die Marke Start in den Quellcode. Diese benötigen wir, wenn das Raumschiff des Spielers zerstört wird und er von Vorne anfangen
muss. Die Startposition des Spielers ist genau in der Mitte des Bildschirm. Der Typ ist 0, wie wir aus der Tabelle oben ersehen können. Die Startenergie des Spielers beträgt 200 Einheiten. Das Raumschiff benötigt keine Geschwindigkeitsangabe, da es direkt über die Tastatur gesteuert wird. Zum
Schluss wird noch die Animationsphase auf 0 gesetzt. |
|
|
For m = 1 To 20
ObjectT(m) = 0
Next m
|
|
|
Jetzt werden alle verbleibenden Objekte (beachten Sie, das wir diesmal nur 21 Objekte haben, einschließlich dem des Spielers) auf Typ 0 gesetzt, was soviel bedeut wie: Kein Objekt |
|
|
HGrundX = 0
Punkte = 0
FeuerKraft = 1000
|
|
|
Vor der Hauptschleife noch drei wichtige Werte: Die Variable HGrundX gibt an, wie weit der Hintergrund schon gescrollt ist. Die Bedeutung der Variable Punkte
muss ich wohl nicht erklären, oder? FeuerKraft gibt an, wie stark der Bordlaser des Spielers aufgeladen ist. 1000 ist hier der Maximalwert. Näheres dazu, wenn wir zur
Schuss-Steuerung kommen. |
|
|
Do
If MIDI_PLAYING() = 0 Then
MIDI_STOP
MIDI_CONTINUE
End If
|
|
|
Zuerst wird bei jedem Schleifendurchgang geprüft, ob die MIDI-Musik noch läuft. Wenn nein, wird die Datei wieder von
vorne abgespielt. |
|
|
HGrundX = HGrundX + 1
If HGrundX > (320 * 4) Then
HGrundX = HGrundX - (320 * 4)
End If
BitBlt DISPLAY.hDC, 0, 0, w, h, HGRUND.hDC, HGrundX, 0,
BIT_COPY
BitBlt DISPLAY.hDC, (320 * 4) - HGrundX, 0, w, h, _
HGRUND.hDC, 0, 0,
BIT_COPY)
|
|
|
Danach wird der Hintergrund gezeichnet. Dazu wird die Variable
HGrundX erst einmal um den Wert 1 erhöht, damit sich der Hintergrund auch weiterbewegt. Hat sich der Hintergrund so weit bewegt,
dass er komplett aus dem Bildschirm verschwunden ist, so wird die Variable
HGrundX wieder auf 0 gesetzt. |
|
Es wird als erstes ein Ausschnitt in der Größe des Bildfelds DISPLAY (W * H) aus dem Bildfeld
HGRUND kopiert. Das ist recht leicht zu bewerkstelligen
und zu verstehen: |
Je weiter der Spieler fliegt, desto weiter verschiebt sich der Bereich, der kopiert wird. Und wozu nun der zweite Kopiervorgang? Wenn der Spieler am Ende des Levels angekommen ist, haben wir die Situation,
dass der Bereich den wir kopieren sollen zum Teil außerhalb des Hintergrundpuffers liegt! Wenn wir keinen weiteren Kopiervorgang einbauen, erhalten wir dann an der Stelle "Müll". Deshalb wird der Anfang des Hintergrundpuffers an die Stelle kopiert, an der der erste aufhört. |
Um diese Koordinate zu bekommen, nehmen wir die Breite des Hintergrunds (in unserem Fall ist der Hintergrund 320*4 Pixel breit - das können Sie aber natürlich ändern) und ziehen die Position
HGrundX davon ab. Die meiste Zeit wird dieser Bereich außerhalb des Displays liegen, jedoch beeinträchtigt das die Leistung des Spiels kaum, da die
API-Funktionen "intelligent" genug sind, um nicht unnötig viel zu kopieren. Wenn Ihnen hierbei noch Zweifel kommen, so probieren Sie mal im fertigen Spiel, den zweiten Kopiervorgang herauszunehmen. Sie werden sehen,
dass es ohne diesen nicht geht.
|
|
|
If
FeuerKraft < 1000 Then
If FeuerKraft < 100 Then
FeuerKraft = FeuerKraft + 5
If FeuerKraft > 100 Then
FeuerKraft = 100
Else
FeuerKraft = FeuerKraft + 2
End If
End If
|
|
|
Da sich der Laser des Spielers automatisch regeneriert, wird an dieser Stelle die Feuerkraft des Spielers erhöht. Erst wird nachgesehen, ob der Spieler nicht schon volle Feuerkraft erreicht hat. Wenn nicht, dann wird geprüft, ob sich die Feuerkraft unter 100 Punkte befindet. Ist dies der Fall, so lädt der Laser schneller. Ab 100 Punkten wird dann nur noch mit 2 Einheiten pro Bildschirmaufbau geladen. Dadurch wird erreicht,
dass Der Spieler, selbst wenn die Feuerkraft auf 0 ist, in kurzer Zeit wenigstens einen
Schuss abgeben kann. Will der Spieler den Laser so stark aufladen lassen,
dass er einen Feuerstoß abgeben kann, muss er viel länger warten. Eine solche Aufteilung ist natürlich kein
Muss, macht aber das Spiel interessanter, da die Schüsse nicht immer gleichmäßig kommen. |
|
|
Display.SetFocus
DoEvents
|
|
|
Mit der Methode SetFocus wird der Focus bei jedem Schleifendurchgang auf das Display-Bildfeld gesetzt. Damit soll verhindert werden,
dass der Spieler aus Versehen zu einem anderen Objekt wechselt und die Kontrolle über sein Schiff verliert. Anschließend wird der Befehl
DoEvents aufgerufen. Dieser sorgt dafür,
dass auftretende Ereignisse verarbeitet werden. Ohne diesen Befehl hätte Windows keine Möglichkeit, das
KeyDown- oder KeyUp-Ereignis an unser Bildfeld zu senden und die Steuerung würde nicht funktionieren. |
Jetzt kommen wir zu der Schleife, die die Objekte durchgeht: |
|
|
For
i = 0 To 20
ObjectX(i) = ObjectX(i) + ObjectG(i)
Select Case ObjectT(i)
|
|
|
Die Schleife geht die Einträge 0 bis 20 durch und die Nummer wird in der Variable
"i" gespeichert. Anschließend wir das aktuelle Objekt bewegt. Hier spielt es überhaupt keine Rolle, ob das Objekt benutzt wird oder nicht. Ein unbenutztes Objekt ist ja so oder so nicht zu sehen und bei einem benutzen Objekt kann uns die automatische Bewegung ja nur recht sein. Jetzt kommt der wichtigste Programmteil: Der
Select-Case-Block. Aus dem Plan von eben wissen Sie ja,
dass jedes Objekt eine Typennummer hat, die es identifiziert. In dem
Select-Case-Block wird jeder Typ gesteuert. Zum Beispiel wird im Case 1-Abschnitt das Raumschiff des Spielers gesteuert.
|
|
|
Case
0
ObjectAC(i) = 0
ObjectA(i) = 0
If Feuer > 0 And
FeuerKraft > 0 Then
Feuer = 0
FeuerKraft = FeuerKraft - 50
ObjectX(i) = ObjectX(0) + 2
ObjectY(i) = ObjectY(0) + 8
ObjectT(i) = 3
ObjectG(i) = 4
ObjectE(i) = 3
WAV_PLAYBACK Pfad$ + "LASER.WAV"
Punkte = Punkte - 1
|
|
|
Es folgt der erste Case-Abschnitt. Dieser behandelt alle Spielobjekte mit dem Typ 0, also alle Objekte, die nicht benutzt werden. Zuerst wird hier geprüft, ob der Spieler auf die Feuer-Taste gedrückt hat. Dies wird mit der Variable Feuer geprüft. Hat diese Variable einen Wert größer als 0, so hat der Spieler die Feuertaste gedrückt. Wenn außerdem die Feuerkraft größer oder gleich 50 ist, so kann ein
Schuss abgegeben werden. |
In diesem Fall wird als erstes die Variable Feuer wieder auf Null gesetzt, damit erst im nächsten Schleifendurchgang wieder gefeuert werden kann. Die Feuerkraft wird um 50 Einheiten verringert. Dies ist die Kraft, die für einen
Schuss benötigt wird. Nun wird das aktuelle Objekt (das ja vorher unbenutzt war) als
Schuss benutzt. Dazu wird es auf die Koordinaten des Spielers gesetzt. Allerdings wird bei der X-Koordinate der Wert 2, und bei der Y-Koordinate der Wert 8 hinzuaddiert. Dies geschieht, damit es so aussieht, als würde der
Laserschuss wirklich aus den Kanonen des Raumschiffs kommen. Es ist also nicht mehr, als eine kleine Verschönerung. Das Spiel würde auch ohne diese Verzierung laufen. Nun wird der Typ des Objekts auf 3 gesetzt. |
Wie Sie aus der Tabelle der Objekttypen wissen, steht in diesem Spiel der Wert 3 für einen
Laserschuss. Die Geschwindigkeit eines Laserschusses beträgt vier Pixel pro Bildschirmaufbau und die Energie drei Einheiten. Nachdem diese Einstellungen getroffen sind, wird noch eine Sounddatei ausgegeben, damit der Spieler auch hört,
dass er gefeuert hat. Und zum Schluss wird ihm noch ein Punkt für einen
Schuss abgezogen. Diese Gemeinheit habe ich eingebaut, damit der Spieler nicht einfach immer die Feuertaste gedrückt hält, um sich seinen Weg zu bahnen. |
|
|
Else
a = Fix(Rnd * 4000)
If a < 2 Then
ObjectX(i) = w + 40
ObjectY(i) = Rnd * h
ObjectT(i) = 2
ObjectG(i) = -2 - Rnd * 3
ObjectE(i) = 15
End If
End If
|
|
|
Wenn kein Schuss abgegeben werden kann oder soll, wird der andere Teil des IF-Blocks ausgeführt. Hier wird per Zufall entschieden, ob ein neuer Asteroid erstellt werden soll. Dafür wird ein Wert zwischen 0 und 4000 ausgelost. Ist dieser Wert kleiner als 2, so wird ein Asteroid erstellt. Das mag Ihnen vielleicht gering vorkommen, allerdings müssen Sie überlegen,
dass dies pro unbenutzten Objekt und pro Bildschirmaufbau gilt. |
Sie werden schon sehen, wie viele Asteroiden herumfliegen werden. Wenn die Entscheidung zu Gunsten eines neuen Asteroiden erfolgt ist, wird dieser erstellt. Seine X-Position liegt rechts außerhalb vom Bildfeld. Seine Y-Position wird ebenfalls per Zufall festgelegt. Dazu wird ein Wert zwischen 0 und der Höhe des Spielfelds benutzt. Der Typ eines Asteroiden hat die Nummer zwei - also setzen wir sie auch. Die Geschwindigkeit beträgt mindestens -2 Pixel pro Sekunde und maximal -5 Pixel pro Sekunde. Auch hier wird der Zufallsgenerator benutzt, damit die Asteroiden nicht in Reihe und Glied fliegen. Ein Gesteinsbrocken hat 15 Energieeinheiten und
muss somit 5 mal von einem Laserschuss (Energie = 3) getroffen werden, bis seine Energie gleich 0 ist, und er explodiert. |
|
|
Case
1
ObjectAC(i) = ObjectAC(i) + 1
If ObjectAC(i) > 10 Then
ObjectA(i) = ObjectA(i) + 1
ObjectAC(i) = 0
End If
If ObjectA(i) > 1 Then
ObjectA(i) = 0
End If
|
|
|
Wenn der Jet des Spielers gezeichnet wird, so ist als erstes die Animation an der Reihe. Jedes Objekt hat ja zwei Animationszähler.
ObjectA(x) enthält die Animationsphase und
ObjectAC(x) ist ein Verzögerungszähler. Das Prinzip dieser Zähler sieht so aus: Bei jedem Durchgang wird
ObjectAC erhöht. Wird ein bestimmter Wert (in diesem Fall 10) überschritten, so wir wird
ObjectAC auf 0 zurückgesetzt und ObjectA um eins erhöht. Überschreitet
ObjectA den zulässigen Maximalwert, so wird auch diese Variable auf 0 (oder auf einen anderen beliebigen Startwert) zurückgesetzt. Dank diesem Verfahren wechselt die Animation nicht bei jeden Bildschirmaufbau, sondern nur bei jedem 10. |
|
|
ZeichneSprite ObjectA(i),
ObjectX(i), ObjectY(i))
If Gedrückt(37) And ObjectX(i)
> 0 Then
ObjectX(i) = ObjectX(i) - 3
End If
If Gedrückt(39) And ObjectX(i)
< w Then
ObjectX(i) = ObjectX(i) + 2
End If
If Gedrückt(38) And ObjectY(i)
> 0 Then
ObjectY(i) = ObjectY(i) - 2
End If
If Gedrückt(40) And ObjectY(i)
< h Then
ObjectY(i) = ObjectY(i) + 2
End If
If Gedrückt(32) Then Feuer =
1
If Gedrückt(27) Then End
If ObjectE(i) <= 0 Then
ObjectT(i) = 5
ObjectAC(i) = 0
ObjectA(i) = 0
WAV_PLAYBACK Pfad$ + "EXPLOSIO.WAV"
End If
|
|
|
Nachdem die Animation geändert wurde, wird das Raumschiff gezeichnet. Da der Animationszähler
ObjectA() zwischen den Wert 0 und 1 schwankt, können wir hiermit auch gleich die Nummer des Sprites ermitteln, der gezeichnet werden soll. Da das Raumschiff des Spielers in den Bildfelder
BITMAP(0) und BITMAP(1) liegt, können wir den Wert vom Animationszähler übernehmen. Würde das Raumschiff in den Feldern
BITMAP(5) und BITMAP(6) liegen, so würden wir an dieser Stelle einfach 5 addieren. Normalerweise sollte man zwar das Raumschiff erst zeichnen, nachdem es bewegt wurde, aber der Unterschied beträgt höchstens 1 bis 2 Pixel und so spielt es keine Rolle. |
Aber nun zur Steuerung an sich: Dazu werden die für die Steuerung wichtigen Tasten überprüft. Ist zum Beispiel die Pfeiltaste nach links gedrückt und ist der Spieler noch vom linken Rand entfernt, so wird die X-Position um 3 verringert. Ähnliches gilt auch für die anderen Tasten. Ist die Pfeiltaste nach rechts gedrückt und der Spieler befindet sich vor dem rechten Rand, so bewegt sich das Raumschiff um zwei Einheiten vorwärts. Die Bewegung nach links ist übrigens extra schneller als in die anderen Richtungen, damit der Spieler besser vor einem Brocken zurückweichen kann. Aber natürlich können Sie das auch ändern, wenn Sie wollen.
Ist die Steuerungs-Taste (STRG oder CTRL) gedrückt, so wird die Variable Feuer auf 1 gesetzt. Wie Sie wissen hat das zur Folge,
dass das nächste freie Objekt bei genügender Feuerkraft in einen
Laserschuss umgewandelt wird. |
Wird die Taste Escape (ESC) gedrückt, so wird das Spiel mit dem Befehl
END abgebrochen. Das Spiel lässt sich nämlich auf keinem anderen Weg beenden, wenn es kompiliert wurde. Der Benutzer kann die Form zwar schließen, allerdings erscheint Sie kurz darauf neu. Das liegt daran,
dass die Prozedur SpielStart aus der Form_Load-Ereignisprozedur aufgerufen wird. Aber zerbrechen Sie sich darüber nicht den Kopf. Denken Sie nur daran,
dass man Ihr Spiel per Tastendruck beenden kann. Und Zum Schluss
wird geprüft, ob die Energie des Spielers gleich 0 ist. In diesem Fall wird das Objekt in ein Objekt von Typ 5 (explodierendes Raumschiff) umgewandelt, die Animationszähler werden zurückgesetzt und eine Sounddatei wird ausgegeben, die dieses dramatische Ereignis untermalen soll. |
Jetzt kommen wir zu den Asteroiden. Auch hier wird mit Verzögerungen und Animationszählern gearbeitet. Der Animationszähler enthält hier allerdings Werte zwischen 0 und 3, da ein Asteroid vier Frames hat. |
|
|
Case
2
ObjectAC(i) = ObjectAC(i) + 1
If ObjectAC(i) > 10 Then
ObjectA(i) = ObjectA(i) + 1
ObjectAC(i) = 0
End If
If ObjectA(i) > 3 Then
ObjectA(i) = 0
End If
ZeichneSprite (ObjectA(i) + 8, ObjectX(i),
ObjectY(i)
If ObjectX(i) < -30 Then
ObjectT(i) = 0
If ObjectE(i) <= 0 Then
ObjectT(i) = 4
ObjectA(i) = 0
ObjectAC(i) = 0
WAV_PLAYBACK Pfad$ +
"EXPLOSIO.WAV"
Punkte = Punkte + 20
End If
|
|
|
Nun wird das Sprite gezeichnet. Hier muss zum Animationszähler der Wert 8 addiert werden, da der erste Frame der Animation sich in Bildfeld acht befindet. Wenn der Asteroid links aus dem Bild verschwindet, also seine X-Koordinate in den Minusbereich geht, wird er gelöscht, indem der Objekttyp auf 0 gesetzt wird. Wenn die Energie des Asteroiden gleich 0 ist, so wird er in Typ 4, einen explodierenden Asteroiden, umgewandelt. Dabei wird genauso vorgegangen wie beim Raumschiff des Spielers: Der Typ wird gesetzt, die Animationszähler werden auf 0 zurückgesetzt und eine WAVE-Datei wird abgespielt. Zusätzlich werden hier die Punkte des Spielers um 20 erhöht. Schließlich soll er ja auch belohnt werden, wenn er einen Asteroiden vernichtet hat ;-) |
Jetzt kommen wir zu einem sehr wichtigen Teil im Spiel: Der Kollisionsabfrage. Wenn der Spieler gegen einen Gesteinsbrocken "crasht", so
muss dem Spieler ja auch dafür Energie abgezogen werden. Um das zu erreichen, müssen wir bei jedem Asteroiden prüfen, ob er mit dem Spieler zusammengestoßen ist. Das Prinzip einer solchen Abfrage läuft so: Es werden jeweils alle anderen Objekte durchgegangen und es wird geprüft, ob das entsprechende Objekt mit dem aktuellen kollidieren kann. Zum Beispiel sollen die Asteroiden ja nicht miteinander kollidieren. Also prüfen wir den Typ des Objekts. Ist das getan prüfen wir noch, ob der Abstand (sowohl in X- als auch in Y-Richtung) einen bestimmten Wert unterschritten hat. In dem Fall ist eine Kollision eingetreten: |
|
|
For
i2 = 0 To 20
If ObjectT(i2) = 1 Then
If Abs(ObjectX(i2) - ObjectX(i)) < 50 Then
If Abs(ObjectY(i2) - ObjectY(i)) < 40 Then
ObjectE(i2) =
ObjectE(i2) - ObjectE(i)
ObjectE(i) = 0
WAV_PLAYBACK
Pfad$ + "EXPLOSIO.WAV"
End If
End If
End If
Next i2
|
|
|
Es werden also wieder alle Objekte in der Schleife durchgegangen. Die aktuelle Nummer wird in der Variable I2 gespeichert. Es wird
jedes Mal geprüft, ob das aktuelle Objekt den Typ 1 hat, also ob es der Jet des Spielers ist. Dann wird geprüft, ob die Entfernung der X-Koordinate vom Spielerraumschiff und der vom Asteroiden kleiner als 50 ist. In dem Fall wird auch noch geprüft, ob die Y-Entfernung kleiner als 40 ist. Wenn diese Bedingungen alle zutreffen, so hat eine Kollision stattgefunden. Dem tragen wir Rechnung, indem wir dem Spieler die Energie abziehen, die der Asteroid noch hat. Wenn der Asteroid noch keinmal von einem
Laserschuss getroffen wurde, so werden hier dem Spieler 15 Energieeinheiten abgezogen. Anschließend wird die Energie des Asteroiden auf -1 gesetzt. Dadurch wird er beim nächsten Bildschirmaufbau explodieren. Eine Explosion müssen wir hier nicht extra ausgeben, da das beim nächsten Schleifendruchlauf sowieso passiert. |
Sie werden sich vielleicht fragen, warum ich an dieser Stelle alle Objekte durchgehe. Schließlich wissen wir doch,
dass der Spieler Objekt Nr. 0 ist. Tja, man könnte die Schleife auch weglassen, aber das bringt fast keine Geschwindigkeitsvorteile. Und außerdem wissen wir auch nicht,
ob Objekt Nr. 0 immer noch ein Raumschiff ist. Wenn es mittlerweile explodiert ist, befindet sich an dieser Position eine Explosion oder vielleicht ein anderer Asteroid. Ein weiterer Grund ist,
dass eine solche Kollisionsabfrage, wie wir Sie gerade besprochen haben, sehr vielseitig eingesetzt werden kann. Sie sehen sie sogar gleich wieder, wenn wir bei den Laserschüssen sind. |
Noch ein Hinweis: Die minimale Entfernung der Objekte lässt sich einfach ermitteln. Angenommen der Jet des Spielers ist 20 Pixel breit und der Asteroid 60 (das stimmt zwar nicht ganz, aber es ist ja nur eine Annahme), dann beträgt der minimale Abstand zwischen dem Mittelpunkt des Jets und dem des Asteroiden 20/2 + 60 /2 Pixel. Also: 40 Pixel. Wird diese Grenze unterschritten, so überlagern Sie sich. Diese Abfrage
muss dann noch für die Höhe gemacht werden. Um die Differenz zwischen zwei Werten zu errechnen, subtrahiere ich die beiden Werte voneinander (die Reihenfolge ist hier egal). Anschließend wird der Wert mit der Funktion
ABS(x) in eine positive Zahl umgewandelt. Auch diese Formel ist allgemein gültig und findet oft Verwendung. |
|
|
Case
3
ZeichneSprite 7, ObjectX(i), ObjectY(i)
If ObjectX(i) > w + 5 Then
ObjectT(i) = 0
If ObjectE(i) <= 0 Then
ObjectT(i) = 0
For i2 = 0 To 20
If ObjectT(i2) = 2 Then
If Abs(ObjectX(i2) - ObjectX(i)) < 30 Then
If Abs(ObjectY(i2) - ObjectY(i)) < 30 Then
ObjectE(i2) = ObjectE(i2) - ObjectE(i)
ObjectE(i) = 0
End If
End If
End If
Next i2
|
|
|
Wir behandeln die weiteren Objekt nicht mehr ganz so ausführlich. Daher kommt hier der ganze Abschnitt für den
Laserschuss. Der Laserschuss hat keine Animation, folglich können wir uns Zähler und ähnliches sparen. Es wird nur Sprite Nr. 7 an die entsprechende Position gezeichnet. Wie Sie aus der Tabelle von oben wissen, ist Sprite Nr. 7 ja der Laser. Als nächstes wird überprüft, ob der
Schuss den Bildschirm verlassen hat. In diesem Fall wird er einfach gelöscht. Fällt die Energie eines Schusses auf 0, so löst er sich auf. Da wir hierfür keine Animation vorgesehen haben löschen wir den Sprite einfach. |
Jetzt kommt wieder die Kollisionsabfrage. Diesmal ist das Ziel nicht der Spieler (das wäre ja auch etwas gemein) sondern Asteroiden (Typ 2). Hier hat sich gegenüber der anderen Kollisionsabfrage nicht viel geändert. Nur die minimal Entfernungen haben neue Werte. Die Auswirkungen bleiben gleich.
Trifft ein Laserschuss einen Asteroiden, so verschwindet der
Laserschuss und dem Asteroiden wird Energie abgezogen. |
|
|
Case
4
ObjectAC(i) = ObjectAC(i) + 1
If ObjectAC(i) > 10 Then
ObjectA(i) = ObjectA(i) + 1
ObjectAC(i) = 0
End If
If ObjectA(i) > 3 Then
ObjectA(i) = 0
ObjectT(i) = 0
Else
ZeichneSprite(ObjectA(i) + 12,
ObjectX(i), ObjectY(i)
End If
If ObjectX(i) < -30 Then
ObjectT(i) = 0
|
|
|
Nun kommen wir langsam zum Schluss. Typ 4 ist der explodierende Asteroid. Hier brauchen wir wieder einen Animationszähler. Schließlich haben wir ja nicht umsonst vier Animationsphasen gezeichnet! Ist der letzte Frame gezeichnet (also wenn der Animationszähler größer ist als 3), so wird der Zähler zurückgesetzt, und der Asteroid gelöscht. Andernfalls wird das Objekt gezeichnet. Dabei wird zum Zähler 12 addiert, da die Animation des explodierenden Asteroiden bei Sprite Nr. 12 anfängt. Beachten Sie hier,
dass der Sprite NICHT gezeichnet werden darf, wenn die Animation abgelaufen ist und die Zähler zurückgesetzt wurden. Sonst würde der Spieler ein kurzes Aufblinken des ersten Frames sehen - und das wollen wir ja nicht. Verschwindet die Animation vom Bildschirm bevor die Animation abgelaufen ist, so wird
das Sprite direkt gelöscht. |
|
|
Case
5
ObjectAC(i) = ObjectAC(i) + 1
If ObjectAC(i) > 10 Then
ObjectA(i) = ObjectA(i) + 1
ObjectAC(i) = 0
End If
If ObjectA(i) > 4 Then
ObjectA(i) = 0
ObjectT(i) = 0
GoTo Start
Else
ZeichneSprite(ObjectA(i) + 2,
ObjectX(i), ObjectY(i)
End If
If ObjectX(i) < -30 Then
ObjectT(i) = 0
|
|
|
Das explodierende Raumschiff ist genauso aufgebaut, nur dass es 5 Frames hat und so die Animation bei 4 beendet ist. Außerdem beginnt der explodierende Jet bei Sprite Nr.
2. |
|
|
End Select
Next i
Display.Refresh
|
|
|
Nun haben wir alle Objekte fertig, die wir brauchen. Der Select-Case-Block wird
geschlossen, genauso wie die For-Schleife. Anschließend wird das Bildfeld noch "refresht", damit wir auch den Lohn unserer Arbeit sehen. Jetzt fehlen uns noch die Instrumente: |
|
|
FKRAFT.Cls
x = FKRAFT.ScaleWidth / 1000 * FeuerKraft
FKRAFT.Line (0, 0)-(x,
FKRAFT.ScaleHeight), QBColor(11), BF
|
|
|
Fangen wir mit der Anzeige der Feuerkraft an. Erst einmal wird das Bildfeld geleert (damit die vorherige Anzeiger gelöscht wird). Da wir eine Prozentanzeige haben wollen, errechnen wir, wie weit der Balken im Verhältnis zum Bildfeld stehen
muss. Dazu nehmen wir die Zeichenbreite des Bildfelds und teilen Sie durch 1000 (das ist der Maximalwert für die
Schusskraft). Nun multiplizieren wir das Ganze noch mit der aktuellen
Schusskraft. Das diese Formel funktioniert, kann man sich leicht überlegen: |
Die volle Feuerkraft hat den Wert 1000. Und wenn wir die Balkenlänge durch 1000 teilen und mit 1000 multiplizieren, haben wir wieder die Balkenlänge heraus, und der Balken ist voll. Man kann sich das ganze natürlich auch an einer Verhältnisgleichung veranschaulichen, aber ich will Ihnen ja keine Nachhilfe in Prozentrechnung geben :) |
OK, nun haben wir die Länge des Balkens und können ihn einzeichnen. Dazu verwenden wir die Line-Methode. Wir zeichnen eine Box von der linken oberen Ecke bis zu der Balkenposition und dem unteren Rand. Diese Box wird hellcyan gefüllt. |
|
|
ENERGY.Cls
x = ENERGY.ScaleWidth / 200 * ObjectE(0)
ENERGY.Line (0, 0)-(x,
ENERGY.ScaleHeight), QBColor(12), BF
|
|
|
Der Energiebalken des Spielers wird analog gezeichnet. Und wird daher nicht mehr erklärt. Er wird übrigens hellrot gefüllt. |
|
|
PUNKTEANZ.Caption = Punkte
|
|
|
Die letzte Anzeige ist die Punktetafel. Hier setzen wir einfach die Caption-Eigenschaft auf die aktuelle Punktzahl. Das war's |
|
|
Loop
End Sub
|
|
|
Jetzt wird noch die Hauptschleife geschlossen. Wir sind fertig. Sie können jetzt Ihr fertig Spiel testen. |
Das fertige Spiel befindet sich übrigens für Visual Basic 3.0 und 4.0 - 16 Bit im Verzeichnis "GAME16" und für 4.0 - 32 Bit oder 5.0 im Verzeichnis "GAME32".
|
|
Vielleicht haben Sie das Gefühl, dass der Aufwand für ein solches Spiel zu groß war. An dieser Stelle kann ich Ihnen versichern, je öfter Sie sich mit
Spiel-Programmierung beschäftigen, desto leichter fällt es Ihnen. Dieses Demospiel habe ich (zusammen mit den Grafiken) in ca. 1 ½ Stunden geschrieben.
Das Beispielprojekt gibt es hier zum Download:
|
|
|
Download
Spielekurs_Teil1.zip
(235,7 kB) |
|
Downloads
bisher: [ 2055
] |
|
|