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

Puzzle 010

So weit, so gut???

C64 Studio

Lasst uns nun das Puzzle vorläufig abschließen. Wir haben im letzten Beitrag ja schon alles vorbereitet, sodass wir jetzt nur noch die Bildschirme fürs Weiterkommen oder Game Over anzeigen müssen.

Game Over

Kümmern wir uns zunächst ums Game Over. Wir wollen im Falle eines scheiterns, einen abschließenden Bildschirm anzeigen. Dazu entwerfen wir im Char Screen Editor unseren Game Over Bildschirm mit dem Namen „GameOver.charscreen“.

Beispiel fürs Game Over.
Beispiel fürs Game Over.

Jetzt noch die Funktion gameOver anpassen:

;*******************************************************************************
;*** Der Spieler hat verloren
;*******************************************************************************
;*** Übergabe: -
;*******************************************************************************
;*** Rückgabe: -
;*******************************************************************************
;*** ändert  : A, SR
;*******************************************************************************
!zone gameOver
gameOver
 lda #$00                           ;Alle Sprites
 sta SPRITEACTIVE                   ;abschalten
 ;*** GameOver-BS anzeigen
 ldx #<screenGameOver               ;LSB ins X-Register
 ldy #>screenGameOver               ;MSB ins Y-Register
 jsr drawScreen                     ;Bild ausgeben
.loop
 jsr joystickInput                  ;warten bis
 and #JOY_FIRE                      ;der Feuerknopf gedrückt wurde
 bne .loop
.loop1
 jsr joystickInput                  ;und jetzt warten bis
 and #JOY_FIRE                      ;der Feuerknopf wieder losgelassen wurde
 beq .loop1
 jmp main                           ;Programm neu starten

Wir schalten zu Beginn alle Sprites ab, die benötigen wir hier ja nicht mehr. Dann rufen wir unsere bekannte Routine drawScreen zum Anzeigen der Game Over Meldung auf. Abschließend warten wir darauf, dass der Feuerknopf gedrückt und wieder losgelassen wird, bevor wir zum Titelbild zurückspringen.

Gewonnen!

Jetzt noch etwas Ähnliches für das Lösen des Puzzles.
Auch hier zeigen wir einen Bildschirm „Solved.charscreen“ an, der den Spieler darüber informiert, dass er es geschafft hat und was als nächstes geschieht.

Gewonnen, es kann weitergehen.
Gewonnen, es kann weitergehen.

Auch hier die dazugehörige Funktion puzzleSolved anpassen:

;*******************************************************************************
;*** Der Spieler hat gewonnen
;*******************************************************************************
;*** Übergabe: -
;*******************************************************************************
;*** Rückgabe: -
;*******************************************************************************
;*** ändert  : A, SR
;*******************************************************************************
!zone puzzleSolved
puzzleSolved
 lda #$00                           ;abschalten aller
 sta SPRITEACTIVE                   ;Sprites
 ;*** 'Geschaff'-BS anzeigen
 ldx #<screenSolved                 ;LSB ins X-Register
 ldy #>screenSolved                 ;MSB ins Y-Register
 jsr drawScreen                     ;Bild ausgeben
.loop
 jsr joystickInput                  ;prüfen, ob der
 and #JOY_FIRE                      ;Feuerknopf gedrückt wurde
 bne .loop
.loop1
 jsr joystickInput                  ;prüfen, ob der
 and #JOY_FIRE                      ;Feuerknopf wieder losgelassen wurde
 beq .loop1
 jmp main                           ;Noch gibt es keinen weiteren Level,
                                    ;also erstmal einfach neu starten.

Eigentlich ist alles so wie bei gameOver, nur das hier ein anderer Bildschirm angezeigt wird. Da noch kein weiterer Level vorhanden ist, springen wir auch hier erstmal zurück zum Startbild.

Damit unsere beiden Bildschirme gefunden werden, müssen wir die natürlich noch einbinden, wie wäre es hinter screenPuzzle:

;*** Verloren: Die Zeit ist abgelaufen
screenGameOver
 !media "GameOver.charscreen",charcolor

;*** Geschafft, das Puzzle wurde gelöst
screenSolved
 !media "Solved.charscreen",charcolor

Wenn ihr das Programm nun startet, sollten die entsprechenden Bildschirme angezeigt werden. Allerdings klappt der Neustart noch nicht so ganz.
Da durchs Spielen einige Werte geändert wurden, müssen wir diese für einen erneuten Versuch natürlich wieder auf die korrekten Startwerte zurückstellen.

Fügen wir hinter puzzleMainLoop diese neue Routine ein:

;*******************************************************************************
;*** Ein neues Puzzle beginnen
;*******************************************************************************
;*** Übergabe: -
;*******************************************************************************
;*** Rückgabe: Alle veränderbaren Werte wurden zurückgestellt
;*******************************************************************************
;*** ändert  : A, X, Y, SR
;*******************************************************************************
!zone puzzleStart
puzzleStart
 lda #COLOR_BLACK                   ;Schwarz in den Akku und
 sta VICBACKGROUNDCOLOR             ;Hintergrund- sowie
 sta VICBORDERCOLOR                 ;Rahmenfarbe setzen
 ;*** Puzzle-BS anzeigen
 ldx #<screenPuzzle                 ;LSB ins X-Register
 ldy #>screenPuzzle                 ;MSB ins Y-Register
 jsr drawScreen                     ;Bild ausgeben
 lda #$ff                           ;leeres Feld wieder
 sta tileOrder+8                    ;rechts unten
 lda #$08                           ;auch die Hilfsvariable
 sta freetilePos                    ;zurückstellen
 ldx #$07                           ;Ein gelöstest Puzzle erstellen
.loop
 txa
 sta tileOrder,X
 dex
 bpl .loop
 jsr loadSprites                    ;Sprites laden
 jsr shuffleTiles                   ;Puzzle mischen
 lda #$14                           ;Timer zurückstellen
 sta timer
 lda #$00                           ;Uhr auf NULL stellen
 sta CIA1_CLOCK_SECONDS             ;Sekunden und
 sta CIA1_CLOCK_TENTH               ;zehntel reichen uns
 rts                                ;zurück

Das Meiste stammt aus puzzleMain und wird dort von uns gleich entfernt. Wir setzen die Rahmen- und Hintergrundfarbe auf schwarz und zeigen den Puzzle-Bildschirm an. Dann sorgen wir dafür, dass wir ein gelöstes Puzzle haben, bevor wir die Sprites laden und das Puzzle mischen. Dann wieder unseren Zähler für den timer auf 20 zurückstellen und die Uhrzeit im CIA1 auf Null setzen.

Wie erwähnt können wir jetzt puzzleMain bereinigen. Zwischen den beiden Sprungmarken benötigen wir nur noch den Aufruf unserer neuen Funktion (siehe Markierte Zeile).

;*******************************************************************************
;*** Das Puzzlespiel starten
;*******************************************************************************
!zone puzzleMain
puzzleMain
 jsr puzzleStart                    ;Alles auf Anfang stellen
 
;*******************************************************************************
;*** Eingabeschleife für das Puzzle
;*******************************************************************************
puzzleMainLoop                      ;Start der Endlosschleife
 jsr checkInput                     ;Joysticks prüfen & Eingaben verarbeiten
 jsr drawTimer                      ;ggf. den Zeitbalken aktualisieren
 jmp puzzleMainLoop                 ;Endlosschleife

Mäuschen mach mal Piep 😉

Eigentlich sind wir nun am fertig mit unserem ersten Spiel, aber ich möchte doch noch eine Kleinigkeit hinzufügen. So ganz ohne akustische Rückmeldung will ich das Puzzle jetzt nicht abschließen. Also lasst uns einen kleinen Ton wiedergeben, wenn ein Puzzleteil bewegt wird. Das ist jetzt wirklich nichts umwerfendes, daher verzichte ich hier auch auf einen eigenen Beitrag zum Thema Sound (der kommt später).

Wie so häufig benutze ich wieder einige Konstanten, fügt diese daher in den Source ein.

;*******************************************************************************
;*** Die SID Register  -  ANFANG                                             ***
;*******************************************************************************
SID_BASE            = $d400         ;(Nr) Basisadresse des SID_BASE
SID_VOICE1_FREQ_MSB = $d401         ;(01) 1. Stimme: High-Byte der Frequenz
SID_VOICE1_WAVEFORM = $d404         ;(04) 1. Stimme: Wellenform
SID_SUSTAIN_RELEASE = $d406         ;(06) BIT 7-4: Anschlag (sustain)
                                    ;         3-0: Abschwellen (release)
SID_VOLUME          = $d418         ;(24) BIT   7: 3. Stimme Filtermodus AUS
                                    ;           6:   -''-    Hochpass
                                    ;           5:   -''-    Bandpass
                                    ;           4:   -''-    Tiefpass
                                    ;         3-0: Lautstärke

SID_WAVE_TRIANGLE   = $11           ;Wellenform: Dreieck
SID_WAVE_SAWTOOTH   = $21           ;Wellenform: Sägezahn
SID_WAVE_PULSE      = $41           ;Wellenform: Rechteck
SID_WAVE_WHITENOISE = $81           ;Wellenform: Rauschen
;*******************************************************************************
;*** Die SID Register  -  ENDE                                               ***
;*******************************************************************************

Dies sind erstmal nur die gleich von uns benötigten Konstanten, später lernen wir alle SID-Adressen kennen.

Jetzt brauchen wir noch eine kleine Routine, die unseren Sound wiedergibt. Fügt hinter checkInput die nächsten Zeilen ein:

;*******************************************************************************
;*** Ton fürs Bewegen eines Puzzleteils
;*******************************************************************************
;*** Übergabe: X = Lautstärke
;*******************************************************************************
;*** Rückgabe: -
;*******************************************************************************
;*** ändert  : A, SR
;*******************************************************************************
!zone playTileSound
playTileSound
 lda #$00                           ;Anschlag & Abschwellen auf 0
 sta SID_SUSTAIN_RELEASE
 lda #SID_WAVE_WHITENOISE           ;Rausen
 sta SID_VOICE1_WAVEFORM            ;für die 1. Stimme
 lda #$05                           ;Niedrige Frequenz                          
 sta SID_VOICE1_FREQ_MSB            ;für die erste Stimme
 stx SID_VOLUME                     ;Lautstärke laut X-Register
 rts                                ;zurück

Da unser Sound nur sehr kurz wiedergegeben wird, haben einige Register keine bzw. kaum eine Auswirkung. Wir initialisieren diese aber trotzdem.
Zu Beginn setzen wir Anschlag und Abklingen auf 0, dann die Wellenform für die erste Stimme auf Rauschen und schließlich die Frequenz. Wir beschränken uns bei der Frequenz auf das MSB und wählen eine relativ niedrige aus. Ganz am Schluß setzen wir die Lautstärke auf den Wert, den wir im X-Register an die Routine übergeben haben und schon sind wir fertig.

Natürlich muss unser Sound auch noch aufgerufen werden. Sucht in der Funktion performInput nach dem Label .refresh und fügt dort die markierten Zeilen ein:

.refresh
 ldx #$0f                           ;max. Lautstärke
 jsr playTileSound                  ;Ton wiedergeben
 jsr spriteSetPositions             ;Sprites positionieren
 jsr drawTiles                      ;Hintergründe zeichnen
 lda #$00                           ;Lautstärke auf
 sta SID_VOLUME                     ;0 setzen!
 rts                                ;zurück

Wir starten vor dem erneuten Zeichnen unseren Ton mit der maximalen Lautstärke. Dann wird alles neu gemalt, danach setzen wir die Lautstärke einfach auf 0. Damit wird unser Ton also während des Bewegen der Puzzleteile wiedergegeben. Da das Mischen auch diese Funktion verwendet, erhalten wir auch zum Start einen Sound.

Wir gehen hier natürlich mit der Brechstange vor. Einfach die komplette Lautstärke auf 0 zusetzen ist natürlich nicht die feine Art. Da wir nur diesen einen Ton verwenden, hat das zur Zeit aber keine negativen Auswirkungen.

Damit wären wir am Ende des Puzzlespiels.

Das Programm hätte man (evtl. sogar einfacher und schneller) auch unter BASIC entwickeln können. Hier ging es aber in erster Linie darum ein erstes, kleines, lauffähiges Programm in Assembler zu erstellen.
Natürlich läßt sich hier noch vieles verbessern z. B. wären richtige Sounds und eine Hintergrundmusik sehr schön. Auch hübschere Grafiken würden das Programm aufwerten. Man kann auch überlegen, statt mit Sprites das Puzzle nur mit dem Zeichensatz zu erzeugen. Unsere bereits erwähnte Speicherplatzvergeudung (seht euch nur die vier Bildschirme an) sollten wir durchaus noch mal überdenken.

Aber das soll uns zunächst nicht interessieren, die Planung ist ja so angelegt, dass wir nach und nach die benötigten Fertigkeiten erlernen.

Nun geht es, wie geplant, mit Level 2 – Die Landung weiter…


Schrott!!Naja...Geht so...Ganz gut...SUPER! (7 Bewertungen | Ø 4,86 von 5 | 97,14%)

Loading...


ZurückWeiter

Ein Gedanke zu „Puzzle 010“

  1. Du schreibst “kleines Programm”, doch ich bin stolz darauf, alles korrekt übernommen zu haben. Nur einmal musste ich im Quellcode nachschauen, weil er nach dem Laden der Daten einfach so abbrach. Schlussendlich funktionierte es nach dem Umstellen der Unterprogramme und dem Verschieben der Daten ganz nach hinten.
    Ich denke, dies wäre ein guter Hinweis am Anfang, dass alle Daten hinter die Unterprogramme “gehören”. Ich hatte diese halt zwischen drin verteilt, je nach weiter hinzugefügtem Abschnitt.
    Ich gratuliere Dir, dass Du mich dazu gebracht hast, bis hierher dran zu bleiben, auch wenn ich manche Codeausschnitte erst durch Deine Erklärungen nachvollziehen konnte und manche Schritte immer noch “geheimnissvoll” bleiben wie etwa das Überschreiben der Zeitleiste mittels Timer.

Schreibe einen Kommentar

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

Protected by WP Anti Spam