Ok, I got the hero to change directions based on user input. Now it’s time to move her. The first step for this is to have a hero_moving flag that the input handler will set if there is d-pad input:

hero_moving: .res 1
handle_joypad:
    lda joypad1
    and #$F0
    beq @end
    lsr
    lsr
    lsr
    lsr
    tay
    lda direction_change_table, y
    sta hero_direction
    lda #$01
    sta hero_moving
    rts
@end:
    lda #$00
    sta hero_moving
    rts

With this updated input handler, the hero_moving flag will be 1 when the d-pad is pressed, and 0 when it is not. And as before, our hero’s direction will be stored in hero_direction.

Next, I need to check the hero moving flag in my update_hero_sprite routine, which is called every frame:

update_hero_sprite:
    lda hero_moving
    beq @hero_moving_done

    jsr move_hero
@hero_moving_done:
    ;...
    ;write sprite data to RAM as before

Now that I have the foundation laid, I need to write the move_hero routine.

move_hero

There are 8 possible directions the hero could be facing. The current direction is stored in a variable called hero_direction. The possible values for this variable are 0-7, as assigned by these constants:

;direction indexes for tables
SPRITE_UP =         $00
SPRITE_DOWN =       $01
SPRITE_LEFT =       $02
SPRITE_RIGHT =      $03
SPRITE_UP_LEFT =    $04
SPRITE_UP_RIGHT =   $05
SPRITE_DOWN_LEFT =  $06
SPRITE_DOWN_RIGHT = $07

I need to alter the hero’s coordinates based on the direction the player is moving. If they are moving left, I need to subtract from hero_x. If they are moving right, I need to add to hero_x. If they are moving up, I need to subtract from hero_y. If they are moving down, I need to add to hero_y. If they are moving diagonally, I need to update both hero_x and hero_y. There are many possibilities, and rather than have a million branch instructions, I’ll use table lookups again. I will have one table for horizontal movement and one table for vertical movement. I will index into these tables using hero_direction. The tables look like this:

;up, down, left, right, up_left, up_right, down_left, down_rt
hero_movement_x:
    .byte $00, $00, $FF, $01, $FF, $01, $FF, $01

hero_movement_y:
    .byte $FF, $01, $00, $00, $FF, $FF, $01, $01

My move_hero routine will read from these tables and add to the hero coordinates. Note that adding $FF to a number (on an 8-bit system) is the same as subtracting 1, since it will wrap around from $FF to $00. Here is move_hero:

move_hero:
    ldy hero_direction
    lda hero_movement_x, y
    clc
    adc hero_x
    sta hero_x

    ldy hero_direction
    lda hero_movement_y, y
    clc
    adc hero_y
    sta hero_y

    rts

And now she’s moving.

miau pointed out in a comment that diagonal movement should really alter x and y by SQRT(2) rather than 1 (see Pythagorean Theorum). This is something I didn’t consider before, so my hero covers ground a little more quickly when moving diagonally. I’m not sure yet if this is undesirable behavior or not. If I keep it a simple maze-solving game, it probably won’t be an issue. If I get more ambitious and change it to a more action-oriented game with enemies (something I’ve been seriously thinking about), it may be an issue. We’ll see :).

Animation

I want to talk very briefly about animating the sprite. To do animation, I need a few things:

  1. Graphics for the various frames of animation
  2. A counter to tell me when to change animation frames
  3. A variable telling me the current animation frame

I already have the graphics for my test sprite. The other two I need to make myself.

frame_counter: .res 1
hero_anim_frame: .res 1

The animation is a walking animation, so I don’t want the hero to animate unless she is moving. It makes sense then to do the animation frame-changing logic in the move_hero routine. The Guardian Legend hero sprite has a 4-frame walking animation, so here’s how I do it:

move_hero:
    ldy hero_direction
    lda hero_movement_x, y
    clc
    adc hero_x
    sta hero_x

    ldy hero_direction
    lda hero_movement_y, y
    clc
    adc hero_y
    sta hero_y

    inc frame_counter
    lda frame_counter
    cmp #$0B ;change anim frame every 11 "moving frames"
             ;found this number by trial and error
    bcc @end

    lda #$00
    sta frame_counter ;reset counter
    inc hero_anim_frame ;go to next anim frame
    lda hero_anim_frame ;make sure we stay between 0 and 3
    and #$03
    sta hero_anim_frame
@end:
    rts

Then I will modify my CHR tile lookup tables to handle graphics for all four frames of animation for all 8 directions. Then I will update my update_hero_sprite routine to read from these tables based on the values of hero_anim_frame and hero_direction. I’m not going to post the code, because it’s a little long and there are a lot of tables. If anybody wants me to elaborate more, let me know in the comments :)

BTW, before I forget! When adding new variables, it’s a good idea to initialize them. Here is my updated initialize_hero_sprite routine:

initialize_hero_sprite:
    lda #$50
    sta hero_x
    sta hero_y

    lda #$00
    sta hero_moving
    sta hero_anim_frame
    sta frame_counter

    lda #SPRITE_DOWN
    sta hero_direction

    jsr update_hero_sprite

    rts

Conclusion

Whew. I covered a lot of ground today. I probably should have made this two posts instead of one, but I want to hurry up and get to collision detection so I can put up the next demo video. :)

You may have noticed by now that I use hero_direction a lot to index into lookup tables. This is because everything changes based on the hero’s direction, and lookup tables are a great alternative to long sections of compare/branch code. Often when I find myself writing a lot of branching code I’ll see if I can come up with some scheme to turn the test value into a table index.

See you next time!

4 Responses to “Explorer: 9 – Sprite Movement”

  1. Rob says:

    This is definitely the kind of thing I need to implement. I’ve been including all parts of movement into the actual control routines instead of a using a flag to set when the character movement routine should happen. That’s good enough for smaller projects, but your way makes alot more sense for bigger endeavors, I think. Great post!

  2. Thomas says:

    @Rob: Thanks! I like to separate the different pieces/components of my program as much as possible. Each piece does its own thing with its own data and that’s it. Then the different pieces communicate with each other with flags. Makes it a lot easier to read the code. And spot bugs too.

    Actually, I think I should use separation more than I do. ca65 has a lot of cool scoping features that I don’t use at all because I’ve been too lazy to read up on them. Reading through the source code for Neotoxin made me realize what I’m missing though. Neotoxin was written for a different assembler, but I think they have a lot of the same features.

  3. Rob says:

    Oh man, tell me about it! I was looking through that code before and was just blown away! There are so many files, and you have to do alot of bouncing around back-and-forth to see what is going on. It works really well, though, so it’s definitely put together thoughtfully, it seems.

  4. Thomas says:

    @Rob: When I first looked through it it was a real head-scratcher. I keep going back to it though just to get a look at other approaches and the more I read it the more sense it makes.

Leave a Reply