Jump to content
  • entries
    657
  • comments
    2,702
  • views
    902,711

step 14 - add animation


SpiceWare

3,345 views

Static images that just slide around the screen work OK, but we can do better - so for this update we'll add a couple new images so we can animate the players as they run around the arena.

blogentry-3056-0-13920700-1405376934_thumb.png

 

While you can have as many frames of animation as you'd like, the code is most efficient if the number of frames is a power of 2 (2, 4, 8, 16, etc). The code that cycles through 4 frames is this:

Example:
        inc frame
        lda frame
        and #3       ; limits values from 0-3, if A was 4 it becomes 0 after the and
        sta frame
 

 

Just change the #3 to #7 (8 frames), #15 (16 frames) and so on. If you wanted to cycle thru a non-power of 2 count, say for example 5 frames, the code would look like this:

Example:
        ldx frame
        inx
        cpx #5
        bne save
        ldx #0
save:   stx frame
 

 

For Collect we're going to use 4 frames. You might be wondering why there's only 3 humanoid images - it's because we're going to use 1 of the images twice:

  • HumanGfx
  • HumanRunning0
  • HumanRunning1
  • HumanRunning0

 

In order to animate the players we'll need to keep track of which frame they're showing, so let's add 2 new RAM variables:

    ; indexes for player animation sequences
Animation0:     ds 1    ; stored in $B3
Animation1:     ds 1    ; stored in $B4
 

Then modify PositionObjects so it will animate the images when it preps the variables for the 2LK, but only when the player is in motion:

PositionObjects:
...
    ; select image to show for Player0
        lda ObjectX         ; get current X location for player 0
        cmp SavedX          ;   compare with prior X location
        bne Animate0        ;   if different, animate player 0
        lda ObjectY         ; otherwise check current Y location
        cmp SavedY          ;   against prior Y location
        bne Animate0        ;   and animate player 0 if they're different
        lda #0              ; if X and Y didn't change then select 0, the
        beq SaveFrame0      ;   stationary image, and save it
Animate0:
        inc Animation0      ; increment to select the next frame
        lda Animation0      ; load it
        and #3              ; limit to 0-3 (if it was 4, it's now 0)
SaveFrame0:
        sta Animation0      ; save it
        tax                 ; Transfer A to X
    ; Player0Ptr = HumanGfx + HUMAN_HEIGHT - 1 - Y position
        lda ShapePtrLow,x   ; select image as specified in X
        sec
        sbc Temp
        sta Player0Ptr
        lda ShapePtrHi,x    ; select image as specified in X
        sbc #0
        sta Player0Ptr+1
...
ShapePtrLow:
        .byte <(HumanGfx + HUMAN_HEIGHT - 1)
        .byte <(HumanRunning0 + HUMAN_HEIGHT - 1)
        .byte <(HumanRunning1 + HUMAN_HEIGHT - 1)
        .byte <(HumanRunning0 + HUMAN_HEIGHT - 1)
        .byte <(BoxGfx + HUMAN_HEIGHT - 1)
        
ShapePtrHi:
        .byte >(HumanGfx + HUMAN_HEIGHT - 1)
        .byte >(HumanRunning0 + HUMAN_HEIGHT - 1)
        .byte >(HumanRunning1 + HUMAN_HEIGHT - 1)
        .byte >(HumanRunning0 + HUMAN_HEIGHT - 1)
        .byte >(BoxGfx + HUMAN_HEIGHT - 1)
 

The code for player 1 is almost the same, though it adds a test so the box image will be displayed for one player game variations:

    ; select image to show for Player1        
        bit Players         
        bpl UseBoxImage     ; if 1 player game then draw the box
        lda ObjectX+1       ; get current X location for player 1
        cmp SavedX+1        ;   compare with prior X location
        bne Animate1        ;   if different, animate player 1
        lda ObjectY+1       ; otherwise check current Y location
        cmp SavedY+1        ;   against prior Y location
        bne Animate1        ;   and animate player 1 if they're different
        lda #0              ; if X and Y didn't change then select 0, the
        beq SaveFrame1      ;   stationary image, and save it
Animate1:
        inc Animation1      ; increment to select the next frame
        lda Animation1      ; load it
        and #3              ; limit to 0-3 (if it was 4, it's now 0)
SaveFrame1:
        sta Animation1      ; save it
        tax                 ; Transfer A to X
        .byte $2C           ; $2C = BIT with absolute addressing, trick that
                            ; causes the ldx #4 to be skipped over
UseBoxImage:
        ldx #4              ; select the Box Image
    ; Player1Ptr = BoxGfx + HUMAN_HEIGHT - 1 - Y position
        lda ShapePtrLow,x   ; select image as specified in X
        sec
        sbc Temp
        sta Player1Ptr
        lda ShapePtrHi,x    ; select image as specified in X
        sbc #0
        sta Player1Ptr+1
 

 

blogentry-3056-0-64428200-1405376912_thumb.png

 

 

It works, but the players move so fast they look spastic.

 

collect_20140714_toofast.bin

 

To fix that, well revise it to use an image over multiple frames. For testing, we'll make the left player use each image for 2 frames while the right uses each image for 4:

Animate0:
        inc Animation0      ; increment to select the next frame
        lda Animation0      ; load it
        and #7              ; limit to 0-7 (if it was 8, it's now 0)
SaveFrame0:
        sta Animation0      ; save it
        lsr                 ; divide by 2 for 0-3 - this means we show the same
                            ; image twice in succession
        tax                 ; Transfer A to X
...
Animate1:
        inc Animation1      ; increment to select the next frame
        lda Animation1      ; load it
        and #15             ; limit to 0-15 (if it was 16, it's now 0)
SaveFrame1:
        sta Animation1      ; save it
        lsr                 ; divide by 4 for 0-3 - this means we show the same
        lsr                 ; image four times in succession
        tax                 ; Transfer A to X
 

 

collect_20140714_speedtest.bin

 

Both look OK, though I think the left player looks a little better so the final version will use each image twice. One minor thing happens when the game is over - if the players were in motion, the animation keeps on going even though the players are no longer in motion.

blogentry-3056-0-12236600-1405376921_thumb.png

 

To fix that, well add a Game Over check (same logic was added for Player1) that will select the stationary image:

    ; select image to show for Player0
        bit GameState
        bpl StopAnimation0  ; if game is inactive, stop animation
        lda ObjectX         ; get current X location for player 0
        cmp SavedX          ;   compare with prior X location
        bne Animate0        ;   if different, animate player 0
        lda ObjectY         ; otherwise check current Y location
        cmp SavedY          ;   against prior Y location
        bne Animate0        ;   and animate player 0 if they're different
StopAnimation0:        
        lda #0              ; if X and Y didn't change then select 0, the
        beq SaveFrame0      ;   stationary image, and save it
 

 

blogentry-3056-0-05036900-1405385739_thumb.png

 

 

ROM

 

collect_20140714.bin

 

Source:

 

Collect_20140714.zip

 

COLLECT TUTORIAL NAVIGATION
<PREVIOUS> <INDEX> <NEXT>
 

4 Comments


Recommended Comments

Is there a simple way to have the players smoothly slide against the playfield/obstacles instead of sticking and slowing down?

Link to comment

I don't know if there's a simple way or not.

 

MiniDig has a disassembly for Adventure, I suggest taking a look at it to see how it processes register CXBLPF, which is a collision between the ball and playfield.

Link to comment

Thanks for the heads up. I was actually able to find a very simple solution, though. If you leave two 0s of padding on the left and right side of your sprite and limit yourself to only turning on the four bits in the middle, the sprite will no longer stick to the PF. It's not necessarily an elegant or impressive way to solve the problem, but if it works it works, right?

  • Like 1
Link to comment
On 2/26/2024 at 9:06 PM, SUPERDIVORCE said:

Thanks for the heads up. I was actually able to find a very simple solution, though. If you leave two 0s of padding on the left and right side of your sprite and limit yourself to only turning on the four bits in the middle, the sprite will no longer stick to the PF. It's not necessarily an elegant or impressive way to solve the problem, but if it works it works, right?


Hi, thanks for finding a solution. I was also looking for one, but I didn't quite understand how you achieved it. Could you explain it in more detail or share the code if possible?

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...