step 14 - add animation
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.
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
It works, but the players move so fast they look spastic.
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.
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
ROM
Source:
4 Comments
Recommended Comments