Sprite-Animation / Farbwechsel

Es gibt 16 Antworten in diesem Thema, welches 2.627 mal aufgerufen wurde. Der letzte Beitrag (17. Dezember 2014 um 22:38) ist von Kiri.

  • Moin,

    ich brauche mal einen Denkanstoß für meine Routinen:

    Routine 1: Animation. Hier werden nur die Koordinaten aus einer Tabelle gelesen und für alle Sprites gesetzt.

    Routine 2: Farbwechsel. Die Sprites sollen blinken, wenn sie eine bestimmte Y-Koordinate (Label "spr_BorderBottom") erreicht haben. Bisher ist das für jedes Sprite einzeln realisiert:

    Nun meine Frage: Kann man das nicht irgendwie in einer Schleife erledigen? Das Problem ist, daß jedes Sprite seinen eigenen Pointer auf die Farbtabelle braucht, sonst blinken ja alle im gleichen Rhythmus. So wie es jetzt ist, funktioniert es zwar, ist aber nicht schön programmiert...

  • klar geht das, musst halt irgendwo sowas machen (PseudoCode)

    Code
    BEDINGUNG: Sprite_Y_Pos >#$FOO -> EVENT: JSR ColorRefresh


    Oder man macht sich eine etwas aufwändigere Tabellenlösung, bei der der Pointer jeweils aus der Y-Koordinate kommt.

  • Und wie genau fange ich das am besten an? Die Schleife zum Durchlaufen der Y-Koordinaten ist kein Problem, aber der Sprung zu "ColorRefresh" würde ja dann die Schleife unterbrechen. Also würde meinetwegen Sprite 0 blinken, die Abfrage für Sprites 1-7 würden dann aber gar nicht mehr greifen. Oder mache ich da einen Denkfehler?

  • Sämtliche Spritebekasperung egal ob Positionierung, Pointer, Farben sollten spätestens 1 Rasterzeile vor der niedrigsten Y-Pos des angezeigten Sprites erledigt sein, dann flackert auch nix. Beispiel wenn Top/Bottom-Border offen: warten bis letzte Rasterzeile im Screen durchgelaufen ist, DANN alle Sprite-Events handlen, et voila, im nächsten Frame sind die neuen Werte angepasst.

  • Möglicherweise hab ich das Problem nicht ganz verstanden, aber um eine Routine von einem einzigen Datensatz auf n Datensätze umzustricken, nimmt man üblicherweise Tabellen. Bei Sprites bietet es sich an, die Tabelleneinträge 16 Bit groß zu machen, dann kann man mit dem Offset für den Tabellenzugriff (0,2,4,6,8,10,12,14) auch gleich die Positionsregister im VIC beschreiben. Also wenn jedes Sprite "seinen eigenen Pointer auf die Farbtabelle" braucht, dann leg diese acht Pointer in eine Tabelle und ruf sämtliche Sprite-Routinen mit dem entsprechenden Offset in Y auf.
    EDIT: X zu Y geändert, denn das wird im ersten Beispiel ja schon benutzt, um die acht Sprites anzusprechen.

    Yes, I'm the guy responsible for the Bitte melde dich an, um diesen Link zu sehen. cross assembler. And some Bitte melde dich an, um diesen Link zu sehen..


  • Also wenn jedes Sprite "seinen eigenen Pointer auf die Farbtabelle" braucht, dann leg diese acht Pointer in eine Tabelle und ruf sämtliche Sprite-Routinen mit dem entsprechenden Offset in Y auf.

    Die Pointer für jedes Sprite sind aber nicht fest, die ändern sich ja mit jedem Frame:

    Das heißt, Sprite 0 ist meinetwegen gerade bei Farbe $0f, Sprite 1 bei $0b usw.

    Ein weiteres Problem ist (zumindest für mich ;) die Tatsache, daß die Y-Koordinaten in der Schleife in Zweierschritten gesetzt werden ($d001, $d003, ...), die Farbwerte aber in Einerschritten ($d027, $d028, ...). Brauche ich dann nicht zwei Schelifenzähler, um das abzufragen?

  • Okay, wenn Du regelmäßig die Farbregister beschreibst, arbeite lieber mit Offsets von 0 bis 7. Für den Zugriff auf die Positionsregister kannst Du den aktuellen Offset kurz verdoppeln:

    In diesem Beispiel enthält X immer nur die Spritenummer.
    Alternativ zum Verdoppeln über den Akku wäre auch noch eine "Multiplikationstabelle" für die verdoppelten Offsets möglich - wie es Dir besser passt.

    Yes, I'm the guy responsible for the Bitte melde dich an, um diesen Link zu sehen. cross assembler. And some Bitte melde dich an, um diesen Link zu sehen..

  • So, ich habe mal versucht, alles in einer Schleife zu lösen, Dank des Tipps mit der Offset-Tabelle klappt das schon ganz gut.

    Das einzige Problem ist, daß das Blinken, was an einer bestimmten Y-Koordinate ausgelöst werden soll, nur einmal ausgeführt wird, danach nicht mehr. Irgendwo wird eine Flag wohl nicht korrekt zurückgesetzt, aber ich kann nichts finden?

    Hier mal die Schleife:

  • Hab nur kurz raufgeschaut, aber überspringst du deine Flash-Routine nicht immer - ausser wenn die Position *genau* "Sprite_BorderBottom" entspricht?

    Code
    cmp spr_BorderBottom // Unterer Rand erreicht?
                        *bne* spr_NoFlashing // Nein? Dann Sprung


    Bei Zeile 14 vielleicht mal bmi statt bne nehmen..?

  • Ich glaub', da stimmt was nicht:

    Code
    lda spr_FlashActive,y // Blink-Flag laden
    bne spr_DoFlashing // 0? Nein? Dann Sprung
    
    
    lda #$01 // Sonst Blink-Flag
    sta spr_FlashActive,y // auf 1 setzen
    
    
    lda spr_FlashActive,y // Blink-Flag laden
    beq spr_NoFlashing // 0? Dann Sprung


    Da wird von Bitte melde dich an, um diesen Link zu sehen. auf Bitte melde dich an, um diesen Link zu sehen. gesetzt, dann wieder geladen und auf Bitte melde dich an, um diesen Link zu sehen. verglichen?

    Bitte melde dich an, um diesen Link zu sehen. Bitte melde dich an, um diesen Link zu sehen. Bitte melde dich an, um diesen Link zu sehen.

    Ex-TLI (The Level 99 Industries) & Ex-TNP (The New Patriots) & Ex-TEA (The East Agents) & ?

  • Erstmal vielen Dank für die Antworten.


    Hab nur kurz raufgeschaut, aber überspringst du deine Flash-Routine nicht immer - ausser wenn die Position *genau* "Sprite_BorderBottom" entspricht?

    Code
    cmp spr_BorderBottom // Unterer Rand erreicht?
    *bne* spr_NoFlashing // Nein? Dann Sprung


    Bei Zeile 14 vielleicht mal bmi statt bne nehmen..?

    Hmm, eigentlich sollte das nicht so sein, deshalb ja die Flags. Falls die Y-Position = spr_BorderBottom ist, dann soll erstmal geguckt werden, ob die Flash-Routine gerade aktiv ist:

    Code
    lda spr_FlashActive,y
    bne spr_DoFlashing

    Falls ja, direkt aufrufen. Falls nicht, Flash-Flag für das Sprite auf 1 setzen, der untere Rand wurde ja erreicht:

    Code
    lda #$01
    sta spr_FlashActive,y


    Ich glaub', da stimmt was nicht:

    Code
    lda spr_FlashActive,y // Blink-Flag laden
    bne spr_DoFlashing // 0? Nein? Dann Sprung
    
    
    lda #$01 // Sonst Blink-Flag
    sta spr_FlashActive,y // auf 1 setzen
    
    
    lda spr_FlashActive,y // Blink-Flag laden
    beq spr_NoFlashing // 0? Dann Sprung


    Da wird von Bitte melde dich an, um diesen Link zu sehen. auf Bitte melde dich an, um diesen Link zu sehen. gesetzt, dann wieder geladen und auf Bitte melde dich an, um diesen Link zu sehen. verglichen?

    Stimmt, das ist irgendwie doppelt gemoppelt, ich habe diese beiden Zeilen jetzt mal rausgenommen, der Vergleich ist an der Stelle ja unnötig. Aber am Verhalten hat sich leider auch nichts geändert.

    Code
    lda spr_FlashActive,y
    beq spr_NoFlashing

    Ich habe mir die Animation gerade mal länger angesehen und festgestellt, daß das Blinken nur bei jedem 12. Mal (ca. nach 13 Sekunden) ausgeführt wird. Also scheinen die Flags doch korrekt zurückgesetzt zu werden. Seltsam... ich hänge mal das D64-Image an.

  • Falls die Y-Position = spr_BorderBottom ist, dann soll erstmal geguckt werden, ob die Flash-Routine gerade aktiv ist: [...]
    Falls ja, direkt aufrufen. Falls nicht, Flash-Flag für das Sprite auf 1 setzen, der untere Rand wurde ja erreicht:


    Dann ist der Test unnötig. Wenn der untere Rand erreicht wurde => Flashen. Ob das vorher auch schon der Fall war, ist an der Stelle egal.

    Yes, I'm the guy responsible for the Bitte melde dich an, um diesen Link zu sehen. cross assembler. And some Bitte melde dich an, um diesen Link zu sehen..

  • Ich denke, Squidward und Mac Bacon haben schon recht, das Problem ist deine Y-Abfrage.

    Vertausche hier mal die Abfragen...

    Code
    //***************** Blink-Routine
                        cmp spr_BorderBottom // Unterer Rand erreicht?
                        bne spr_NoFlashing // Nein? Dann Sprung
    
    
                        lda spr_FlashActive,y // Blink-Flag laden
                        bne spr_DoFlashing // 0? Nein? Dann Sprung


    Sonst greift dein spr_FlashActive-Pointer nur in dem Moment, wenn Y = unterer Rand ist.
    Achte aber darauf, dass du den Akku für das cmp nicht veränderst!

    Code
    //***************** Blink-Routine
                        ldx spr_FlashActive,y // Blink-Flag laden
                        bne spr_DoFlashing // 0? Nein? Dann Sprung
    
    
                        cmp spr_BorderBottom // Unterer Rand erreicht?
                        bne spr_NoFlashing // Nein? Dann Sprung

  • Dann ist der Test unnötig. Wenn der untere Rand erreicht wurde => Flashen. Ob das vorher auch schon der Fall war, ist an der Stelle egal.

    Ganz so einfach ist es leider nicht. Wenn der untere Rand erreicht wurde, soll nur angefangen werden zu blinken. Aber während die Sprites blinken, bewegen sie sich ja auch wieder nach oben, dann würde der "Blinkvorgang" in dem Moment abbrechen, wenn die Koordinaten nicht mehr stimmen. Deshalb muß ich ja diesen Umweg über die Flags gehen.

    Ich denke, Squidward und Mac Bacon haben schon recht, das Problem ist deine Y-Abfrage.

    Vertausche hier mal die Abfragen...

    Code
    //***************** Blink-Routine
                        cmp spr_BorderBottom // Unterer Rand erreicht?
                        bne spr_NoFlashing // Nein? Dann Sprung
    
    
                        lda spr_FlashActive,y // Blink-Flag laden
                        bne spr_DoFlashing // 0? Nein? Dann Sprung


    Sonst greift dein spr_FlashActive-Pointer nur in dem Moment, wenn Y = unterer Rand ist.
    Achte aber darauf, dass du den Akku für das cmp nicht veränderst!

    Code
    //***************** Blink-Routine
                        ldx spr_FlashActive,y // Blink-Flag laden
                        bne spr_DoFlashing // 0? Nein? Dann Sprung
    
    
                        cmp spr_BorderBottom // Unterer Rand erreicht?
                        bne spr_NoFlashing // Nein? Dann Sprung

    Okay, nach dem Vertauschen der beiden Blöcke blinken die Sprites tatsächlich regelmäßig bei jedem Ereichen des unteren Rands, allerdings tritt jetzt das oben beschriebene Problem auf, daß das Blinken aufhört, sobald die Y-Koordinate nicht mehr dem unteren Rand entspricht, ist ja auch logisch.

    Hmm, damit es so funktioniert, wie es soll, muss jetzt die Anzahl der passenden Y-Koordinaten in der Tabelle mit der Anzahl der Blink-Farben übereinstimmen, dann geht's. Aber wie schon gesagt, eigentlich sollen die Sprites solange weiterblinken, bis alle Farben aus der Farbtabelle gelesen wurden und nicht nur dann, wenn die Koordinaten mit dem unteren Rand übereinstimmen.

  • Es läuft schon, nach der erwähnten Änderung, aber es läuft viel zu schnell ab!

    Bei $6200 scheint der IRQ für die Musik und das Blinken zu liegen.
    Dort wird anscheinend (nach der Musik) gleich 4x zur Blink-Routine gesprungen.

    Code
    .C:620d  20 03 10    JSR $1003
    .C:6210  20 00 82    JSR $8200
    .C:6213  20 00 82    JSR $8200
    .C:6216  20 00 82    JSR $8200
    .C:6219  20 00 82    JSR $8200


    Damit wird diese also bei PAL 200x in der Sekunde aufgerufen. Selbst wenn sie nur 1x aufgerufen wird, würde das Blinken in ca. 0,25 Sek durchlaufen.

    Setze mal unter VICE einen Breakpoint bei $8225 (müsste die BPL-Prüfung für die Farben sein), dann kannst zu sehen, dass alle Farben verwendet werden.
    Wenn du das Blinken anders 'timest', sollte es eher deinen Vorstellungen entsprechen.


    Außerdem gibt es noch ein zweites Problem:
    Hier setzt du spr_ColorPointers zwar auf 0...

    Code
    lda #$00 // Sonst
                        sta spr_ColorPointers,y // Color-Pointer und
                        sta spr_FlashActive,y // Blink-Flag auf 0 setzen

    ...läufst dann aber direkt weiter in den folgenden Abschnitt...

    Code
    spr_SetColor:
                        sta $d027,y // Farbe setzen
                        inx // Color-Pointer +1
                        txa // X -> A
                        sta spr_ColorPointers,y // Neuen Color-Pointer speichern


    ...hier wird spr_ColorPointers dann auf einen ungültigen Wert gesetzt! Da X immer noch aufs Tabellenende zeigt und nun auch noch um 1 erhöht wird.

  • Es läuft schon, nach der erwähnten Änderung, aber es läuft viel zu schnell ab!

    Bei $6200 scheint der IRQ für die Musik und das Blinken zu liegen.
    Dort wird anscheinend (nach der Musik) gleich 4x zur Blink-Routine gesprungen.

    Code
    .C:620d  20 03 10    JSR $1003
    .C:6210  20 00 82    JSR $8200
    .C:6213  20 00 82    JSR $8200
    .C:6216  20 00 82    JSR $8200
    .C:6219  20 00 82    JSR $8200


    Damit wird diese also bei PAL 200x in der Sekunde aufgerufen. Selbst wenn sie nur 1x aufgerufen wird, würde das Blinken in ca. 0,25 Sek durchlaufen.

    Verdammt, du hast recht, ich hatte vollkommen vergessen, daß ich die Routine pro IRQ viermal aufrufe, damit die Animation der Sprites schneller abläuft. Die Blink-Routine habe ich erst danach hinzugefügt und nicht mehr an den Aufruf gedacht. Danke, darauf wäre ich nie gekommen.


    Außerdem gibt es noch ein zweites Problem:
    Hier setzt du spr_ColorPointers zwar auf 0...

    Code
    lda #$00 // Sonst
                        sta spr_ColorPointers,y // Color-Pointer und
                        sta spr_FlashActive,y // Blink-Flag auf 0 setzen

    ...läufst dann aber direkt weiter in den folgenden Abschnitt...

    Code
    spr_SetColor:
                        sta $d027,y // Farbe setzen
                        inx // Color-Pointer +1
                        txa // X -> A
                        sta spr_ColorPointers,y // Neuen Color-Pointer speichern


    ...hier wird spr_ColorPointers dann auf einen ungültigen Wert gesetzt! Da X immer noch aufs Tabellenende zeigt und nun auch noch um 1 erhöht wird.

    Stimmt, der nachfolgende Code wird ja trotzdem ausgeführt... habe das jetzt mal wie folgt angepaßt.

    Jedenfalls funktioniert es jetzt erstmal, wie es soll. Das Timing zwischen Bewegung und Blinkfrequenz anzupassen, ist dann der nächste Schritt.

    Kiri, ich sehe gerade deine Signatur, bist du derjenige, der für die Seite retro-programming.de verantwortlich ist? Falls ja, möchte ich dir an dieser Stelle ein großes Lob aussprechen. Die Seite hat mir beim Einstieg in Assembler sehr geholfen.
    :thumbsup:

  • Kiri, ich sehe gerade deine Signatur, bist du derjenige, der für die Seite retro-programming.de verantwortlich ist? Falls ja, möchte ich dir an dieser Stelle ein großes Lob aussprechen. Die Seite hat mir beim Einstieg in Assembler sehr geholfen.
    :thumbsup:

    Jup, der bin ich. Schön, dass dir die Seite gefallen hat.