Erstellt: 3. November 2013 (zuletzt geändert: 1. November 2021)

Landung 007

The Eagle has landed

C64 Studio

Nun ist es an der Zeit, dass wir auf eine Kollision reagieren und natürlich ebenfalls prüfen, ob das Landungsschiff sicher aufgesetzt hat. Basis ist (wie jedesmal) der Download aus dem letzten Beitrag Landung 006.

Eine Kollision erkennen

Um festzustellen, ob es zu einer Kollision mit dem Hintergrund gekommen ist, prüfen wir einfach das VIC-II Register 31 $d01f / VIC_SPRITEBACKGROUNDCOLL. Auf einen extra Interrupt (der ja durchaus möglich ist) verzichten wir. Schreiben wir also erstmal eine kleine Routine, die das Register prüft. Fügt diese am besten direkt hinter dem jmp gameloop ein.

!zone checkCollision
;*******************************************************************************
;*** Sprites-Hintergrundkollision (inkl. perfekter Landung) prüfen
;*******************************************************************************
;*** Übergabe: -
;*******************************************************************************
;*** Rückgabe: Bei Kollision Alive auf 0, bei perfekter Landung auf 1 setzen
;*******************************************************************************
;*** ändert  : A, SR
;*******************************************************************************
checkCollision
 lda VIC_SPRITEBACKGROUNDCOLL       ;Kollision mit dem Hintergrund?
 and #%00010001                     ;nur das Schiff kontrollieren
 beq .exit                          ;falls nicht, weiter -> .exit
 lda #$00                           ;crash mit #$00 
 sta Alive                          ;in Alive anzeigen
.exit
 rts                                ;zurück zum Aufrufer

Wie ihr an der Einleitung erkennen könnt, soll die Routine Kollisionen und die korrekte Landung (folgt weiter unten) erkennen. Die neue Variable Alive wird nun verwendet, um den Status des Landungsschiffes anzuzeigen. Alive erhält den Wert -1 / $ff, solange das Raumschiff unbeschädigt ist. Bei einer Kollision wechselt Alive auf 0, um die Zerstörung anzuzeigen. Ein perfekte Landung wird mit einer 1 gekennzeichnet.
Die Funktion holt sich einfach den Inhalt des VIC-Registers 31 in den Akku. Da die Düsen nicht geprüft werden müssen, maskieren wir einfach alles aus, sodass die beiden Hauptsprites (0 & 4) des Landungsschiffes übrig bleiben. Anschließend prüfen wir, ob eine Kollision stattgefunden hat (dann ist der Akku ungleich 0). Gab es keine Kollision, wird die Routine einfach verlassen, anderenfalls erhält Alive, wie eben erwähnt, den Wert 0, bevor die Funktion endet.

Jetzt brauchen wir natürlich noch die neue Variable. Fügt diese am besten hinter der Variablen InputState ein.

Alive                               ;Zustand des Landungsschiffes
 !byte $00                          ;-1 = OK, 0 = zerstört, 1 = gelandet

Außerdem sollten wir nicht vergessen den Wert korrekt zu initialisieren. Sucht dazu die Routine initLander und fügt direkt an den Anfang diese zwei Zeilen hinzu:

 lda #$ff                           ;Alive auf -1 / $ff setzen
 sta Alive

Wir müssen natürlich noch auf eine Kollision reagieren, dazu erweitern wir einfach unsere Hauptschleife gameloop. Ersetzt nun die Anweisung jmp gameloop mit diesem Block.

gameloop
 jsr checkCollision                 ;zur Kollisionserkennung
 lda Alive                          ;Landungsschiff noch 'am Leben'? (-1)
 bmi gameloop                       ;falls ja, Hauptschleife
 sei                                ;IRQs sperren
 beq gameover                       ;falls 0 -> crash
                                    ;sonst -> gelandet
 lda #COLOR_LIGHTGREEN              ;mit grün anzeigen, dass alles gut ist
 jmp waitForFire                    ;auf Feuer warten
gameover
 lda #$00
 sta VIC_SPRITEACTIVE               ;Sprites abschalten
 lda #COLOR_RED                     ;crash = rot
waitForFire
 sta VIC_BACKGROUNDCOLOR
 sta VIC_BORDERCOLOR
 jsr joystickInput                  ;ggf. warten
 and #JOY_FIRE
 beq *-5                            ;bis der Feuerknopf losgelassen wurde
 jsr joystickInput                  ;warten
 and #JOY_FIRE  
 bne *-5                            ;bis Feuer gedrückt wird
 jsr joystickInput                  ;und nochmal warten
 and #JOY_FIRE
 beq *-5                            ;bis der Feuerknopf losgelassen wird
 jmp main                           ;dann -> Neustart

Hinter gameloop springen wir zu unserer neuen Routione checkCollision und holen danach Alive in den Akku. Ist Alive negativ, dann springen wir einfach wieder zu gameloop. Dies geschieht solange, bis sich der Wert von Alive ändert. Sobald dieser nicht mehr negativ ist, werden erstmal die Interrupts gesperrt, anderenfalls könnte sich das Schiff noch bewegen. Es wird dann geprüft, ob der Wert 0 ist (also eine Kollision stattgefunden hat). Bei einem Zusammenstoß geht es beim gameover weiter. Dort deaktivieren wir alle Sprites, laden den Akku mit der Farbe rot und warten darauf, dass der Feuerknopf gedrückt wird.
Bei einer Landung wird nur grün in den Akku geholt und dann zu waitForFire gesprungen.
Bei waitForFire setzen wir Rahmen- und Hintergrundfarbe auf den Wert aus dem Akku. Danach benutzen wir drei Prüfungen für den Feuerknopf. Die erste wartet, bis der evtl. noch gedrückte Knopf losgelassen wird. Wir könnten ja mit aktivem Schub irgendwo reingedonnert sein. Ist der Knopf nicht mehr gedrückt, dann warten wir, bis er gedrückt wird. Würden wir jetzt sofort unser Spiel neustarten käme es gleich wieder zum Unglück, das Landungsschiff würde sofort in den Laser fliegen. Daher warten wir (bei der dritten Abfrage) mit dem Neustart, bis der Feuerknopf wieder losgelassen wird. Dann geht es weiter zu Label main.

Startet ihr das Programm, dann kann es sein, dass sofort eine Kollision angezeigt wird. Spätestens bei einem Neustart, wird es euch passieren, dass ihr zweimal Feuer drücken müsst, um neuzustarten.

Es kam zur Kollision!
Es kam zur Kollision!

Wir haben hier vergessen, das Kollisionsregister zu löschen. Durch das Initialisieren der Sprites wurde versehentlich eine Kollision ausgelöst. Also noch mal die Funktion initLander aufsuchen und am Ende (vor dem rts) das Register einmal lesen, damit es gelöscht wird:

lda VIC_SPRITEBACKGROUNDCOLL  ;Kollisionsregister zur Sicherheit löschen

Jetzt sollte das Programm korrekt starten bzw. neustarten.

Vom Laser verbrutzelt

Wer direk mal Vollgas gibt, wird feststellen, dass der Laser uns immer noch nicht aufhält. Der Grund sollte jedem klar sein, er ist kein Teil des Hintergrundes und wird somit von unserer Prüfung bisher nicht erkannt. Lasst uns in der Routine updateSprites prüfen, ob das Schiff den Laser berührt. Dazu begeben wir uns ans Ende und fügen vor die Zeile sta VIC_SPRITE0Y ;für alle fünf Sprites die folgende kleine Prüfung ein.

 cmp #RASTERIRQ_LASER+4             ;prüfen, ob der Laser berührt wird
 bcs .skip                          ;falls nicht weiter -> .skip
 ldx #$00                           ;sonst Alive auf 0 
 stx Alive                          ;setzen
.skip

Durch die vorherigen Anweisungen befindet sich im Akku die aktuelle Y-Position der Sprites. Diese vergleichen wir mit der letzen Laser-Zeile. Findet keine Berührung statt, springt das Programm einfach zu .skip, sonst wird Alive wieder auf 0 gesetzt, um die Zerstörung zu kennzeichnen, bevor es bei automatisch bei .skip weitergeht.

Schon werdet ihr feststellen, dass das Programm jetzt auch endet, falls ihr den Laserstrahl berührt.

Aufgesetzt

Ich hoffe ihr habt nicht versucht das Schiff wirklich zu landen. Denn bisher ist das unmöglich. Egal wie sanft ihr in der grünen Landezone aufsetzt, das Schiff wird zerstört. Auch hierfür fehlt natürlich noch unsere Prüfung!

Um die Landung zu erkennen müssen wir nur die neue Funktion checkCollision noch etwas erweitern. Auch bei einer Landung lassen wir zunächst eine Kollision auftreten, allerdings prüfen wir dann über absolute Werte, ob der Landebereich getroffen wurde. Dazu müssen wir ermitteln welche X- & Y-Werte für den Landebereich zulässig sind. Befindet sich das Schiff innerhalb dieses Fensters, dann ist eine perfekte Landung wahrscheinlich, sonst haben wir das Ziel verfehlt und das Landungsschiff wird wiedermal zerstört. Dieses Vorgehen ist natürlich nicht sehr flexibel, aber da wir nur einen festen Bereich haben, finde ich das hier durchaus OK.

Der gelbe Bereich inkl. der roten Linie ist ‚sicher‘

Um dies mal zu verdeutlichen, werft einen Blick auf die obige Grafik. Dort seht ihr zur Veranschaulichung ausnahmsweise zwei Sprites. Diese zeigen die äußerst linke und rechte Landeposition an. Die weißen Punkte markieren die obere / linke Ecke des Sprites, dies ist unsere X- & Y-Position. Bilden wir nun ein Rechteck nach unten mit diesen Eckpunkten, dann erhalten wir die sichere Landezone. Außerdem bemerkt ihr bestimmt, dass das Sprite sich bereits im grünen Landebereich befindet. Wir erhalten also eine Kollision. Nur dann müssen wir prüfen, ob sich das Sprite (Y-Position) unter der roten Linie befindet. Ist dies der Fall, dann prüfen wir ob sich die X-Position innerhalb des gedachten Rechteckes befindet. Dabei muss natürlich beachtet werden, dass links an der Landezone eine weitere Kollisionsmöglichkeit besteht. Treffen alle Prüfungen zu, dann wurde die Landezone getroffen, sonst zerschellt das Schiff.

Übernehmt die nächsten Zeilen in checkCollision, hinter der Anweisung beq .exit ;falls nicht, weiter -> .exit.

;*** perfekte Landung?
 lda VIC_SPRITE0Y
 cmp #$dd                           ;Y-Position größer 220?
 bcc .hit
 lda VIC_SPRITE0X
 cmp #$bc                           ;X-Position größer/gleich 188? 
 bcc .hit
 cmp #$dd                           ;X-Position kleiner 220?
 bcs .hit
 lda Spaceship_SpeedY+1             ;passt die Sinkgeschwindigkeit
 bne .hit
 lda Spaceship_SpeedX+1             ;passt die links/rechts Geschwindigkeit
 bne .hit 
 lda #$01                           ;Landung hat geklappt, $01 
 sta Alive                          ;nach Alive schreiben
 jmp .exit                          ;und raus
.hit

Wir vergleichen hier die X- & Y-Position des Schiffes, ob diese innerhalb der Landezone liegen. Dabei beachten wir natürlich, dass die Position immer die linke obere Ecke des Sprites angibt. Die Werte wurden also entsprechend der Spritegröße angepasst. Sind wir in der Landezone, dann wird noch geschaut, ob die Sink- und links/rechts-Geschwindigkeit niedrig genug war. Wir wollen schließlich, dass das Schiff sanft gelandet wird. Um dies zu prüfen, schauen wir uns die aktuelle X- & Y-Geschwindigkeit an. Ist der „Vorkommateil“ der beiden Werten 0, dann gehen wir von einer korrekten Landung aus.
Stimmen schließlich alle Vorgaben, wird eine 1 nach Alive geschrieben, um die perfekte Landung zu kennzeichnen. Weicht auch nur ein Wert ab, wird das Schiff, wie bei jeder Kollision, zerstört.

Jetzt könnt ihr euch an einen Testlauf wagen und versuchen, dass Schiff im Canyon sicher ans Ziel zubringen.

Geschafft! Das Schiff ist sicher gelandet.
Geschafft! Das Schiff ist sicher gelandet.

Wir können uns noch um zwei Kleinigkeiten kümmern. Einmal steht das Schiff bei der Landung etwas zu niedrig (wir haben ja erst bei einer Kollision reagiert). Außerdem könnte es passieren, dass trotz Landung eine der Düsen sichtbar bleibt. Korrigieren wir also die Spriteposition und schalten zur Sicherheit die Düsen ab. Das machen wir im gameloop direkt hinter dem Kommentar ;sonst -> gelandet mit diesen Anweisungen:

 lda #$dd                           ;das Landungsschiff passend platzieren
 sta VIC_SPRITE0Y
 sta VIC_SPRITE4Y
 lda #%00010001                     ;Düsen abschalten
 sta VIC_SPRITEACTIVE

Langsam nähert sich der Level seinem Ende. Wir wollen aber noch dafür sorgen, dass der Spieler sich nicht unnötig lange mit der Landung aufhält und werden als nächstes noch den Spritverbrauch einbauen.


Schrott!!Naja...Geht so...Ganz gut...SUPER! (2 Bewertungen | Ø 5,00 von 5 | 100,00%)

Loading...


ZurückWeiter

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Protected by WP Anti Spam