Last time I wrote code to put a hero sprite onscreen. In my first (pre-sprite) video demo, I had room changes triggered directly by player input. Today I’m going to recode the input handler to alter the hero instead, specifically the hero’s direction.

Joypad revisited

I posted my joypad reading code in a previous post, but here it is again for easy reference:

update_joypad_data: ;props to blargg for A-only joypad read.
    lda joypad1
    sta joypad1_old
    lda #%01111111
    sta joypad1
    sta $4016
    asl a
    sta $4016
@loop:
    lda $4016
    and #$03  ;props to Disch for Famicom support
    cmp #$01
    ror joypad1 ;right, left, down, up, start, select, B, A
    bcs @loop

    lda joypad1_old
    eor #$FF
    and joypad1
    sta joypad1_pressed ;this tracks off-to-on transitions.

    lda joypad1
    eor #$FF
    and joypad1_old
    sta joypad1_released ;this tracks on-to-off transitions
    rts

Before, I was checking joypad_pressed to determine whether or not to change a room. This was so that if the player held down a button, it would only change rooms once (instead of once per frame held). But now that I’m going to be using input to control the sprite, it makes more sense to check joypad1 and use every frame’s input. If the player holds down the d-pad, the sprite should keep moving.

But I’m not quite ready to move yet!

Changing Direction

I want to rewrite my input handler so that it takes d-pad input and uses it to set the hero’s direction. This sounds pretty straightforward, but there are some special cases I need to be wary of: what happens if the player presses left+right? or up+down? This isn’t likely to happen on a real controller, but most people play Nintendo games on emulators these days. If you don’t protect against these strange key combinations, you open yourself up to some pretty bizarre glitches, like the Zelda 2 acceleration glitch (commentator talks about it around the 4:20 mark):

I’m going to work around this problem by assigning a single direction to these weird combinations. But first we need to look at our input. In my joypad reading routine above, I store the button states in a variable called joypad1. The left 4 bits of joypad1 will hold the d-pad states and the right 4 bits will hold the states of the other four buttons. First I want to isolate the d-pad bits:

    lda joypad1
    and #$F0
    beq @end
    lsr
    lsr
    lsr
    lsr
    ;now the d-pad button states are in the right nibble.
    ;do change direction stuff here.
@end:
    rts

The right nibble of the A register now holds the d-pad button states. Next I will build a look-up table, assigning a direction to each possible combination of d-pad presses. Let’s look at the combinations. There are sixteen possibilities in all:

0000xxxx
    ||||
    |||+--up
    ||+---down
    |+----left
    +-----right

0000 = no arrows pressed (will never happen)
0001 = up
0010 = down
0011 = up+down
0100 = left
0101 = left+up
0110 = left+down
0111 = left+up+down
1000 = right
1001 = right+up
1010 = right+down
1011 = right+up+down
1100 = right+left
1101 = right+left+up
1110 = right+left+down
1111 = right+left+down+up

The highlighted ones are the problem combinations. I will need to assign a single, specific direction to them. For example in the case of right+left, I will just choose one: left. Let’s turn this into a lookup table. Recall that I have a set of constants assigning a value to each direction (for indexing into CHR lookup tables among other things):

;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

Now I will make a lookup table assigning these constant values to the various button combinations:

direction_change_table:
    .byte $FF               ;0000 dummy value
    .byte SPRITE_UP         ;0001, up
    .byte SPRITE_DOWN       ;0010, down
    .byte SPRITE_UP         ;0011, up+down
    .byte SPRITE_LEFT       ;0100, left
    .byte SPRITE_UP_LEFT    ;0101, left+up
    .byte SPRITE_DOWN_LEFT  ;0110, left+down
    .byte SPRITE_UP_LEFT    ;0111, left+up+down
    .byte SPRITE_RIGHT      ;1000, right
    .byte SPRITE_UP_RIGHT   ;1001, right+up
    .byte SPRITE_DOWN_RIGHT ;1010, right+down
    .byte SPRITE_DOWN_RIGHT ;1011, right+up+down
    .byte SPRITE_LEFT       ;1100, left+right
    .byte SPRITE_UP_RIGHT   ;1101, left+right+up
    .byte SPRITE_DOWN_RIGHT ;1110, left+right+down
    .byte SPRITE_DOWN_LEFT  ;1111, left+right+up+down

Now all button combinations have a single direction associated with each of them. All that I have left to do is read from this table and set the hero’s direction, like so:

handle_joypad:
    lda joypad1
    and #$F0
    beq @end
    lsr
    lsr
    lsr
    lsr
    tay ;right nibble has the d-pad button states. use as index
    lda direction_change_table, y
    sta hero_direction
@end:
    rts

Changing the sprite

The rest of the work is already done for me. I already have a routine update_hero_sprite (see last post) that takes hero_direction and uses it to index into some CHR lookup tables. I don’t have to change anything to get the new sprites to display. It works! (you can see the input in the bottom left of each picture):

If I press up, the sprite faces up.

If I press up, the sprite faces up.

Pressing down+right makes her face southeast

Pressing down+right makes her face southeast

Left+Right gets mapped to left.

Left+Right gets mapped to left.

up+down+left+right

up+down+left+right

Conclusion

My hero sprite is now capable of looking in 8 directions. I use a lookup table to assign a specific direction to each possible d-pad button combination. The next step is to actually move her in the direction she is facing. And when she is moving, I will want to animate her. Stay tuned!

6 Responses to “Explorer: 8 – Sprite Direction”

  1. Rob says:

    Oh wow! I encoded that video, which went bonkers when the commentator (my buddy Phil from Canada) uploaded it and YouTube gave it a really, REALLY crappy frame rate. ugh. I’ll be downing some beers with him in Tennessee in mid-August, as a matter-of fact : )

    It’s funny how you did the input, because just today I was messing with how I would know which directions are available for exiting from one screen to the next on that zombie game, and came up with this:

    ; *********************************************************
    ; Screen exit types: 0=all_directions, 1=top, 2=bottom,
    ; 3=left, 4=right, 5=top/left, 6=top/bottom,
    ; 7=top/right, 8=top/left/bottom, 9=top/bottom/right,
    ; a=top/left/right, b=bottom/left, c=bottom/right,
    ; d=bottom/left/right, e=left/right, f=none
    ; *********************************************************

    hehe That’s pretty cool!

    That is an interesting way to handle the controls, actually. I’ll have to read that some more, as I’ve always done something pretty different, but is only suited well for 4-direction movement.

    test up, if pressed, move sprite up and jump over the other directional buttons routines –
    if jump is NOT pressed -
    test down, if pressed, move sprite down and jump over other directional buttons routines -
    if not -
    etc. etc.

    Can’t wait to see how you do background collision, because that has been stumping me for a long time, even with people giving me a helping hand haha

  2. miau says:

    Yeah, my way of handling controls was different, too, and pretty naive (on my very first project, anyway). Actually, I didn’t check for bad button combinations and just added/subtracted from the speed vectors depending on which of the four d-pad buttons were pressed.

    It does make sense to handle each combination seperately, though, especially when having 8 directions to move into.
    Diagonal movement was possible with my simple approach, but it was way faster than horizontal or vertical movement.
    E.g.
    press down -> input handling function sets y-speed to 1
    press right -> function sets x-speed to 1
    press up+right -> function sets y-speed and x-speed to 1

    When in the last case both should actually be 1*SQRT(2) to be the same speed (as the diagonal of a square is one of its sides multiplied by the square root of two).

    Now I’ve probably stolen that bit from your next entry. If not, this might help beginners. It’s clear to me know, but wasn’t back when I started.
    (Oh well, I’ve never been that math-savvy anyway) :)

    Keep those updates coming!

  3. Thomas says:

    @Rob: oh, you know that guy? I couldn’t believe it the first time I heard that someone won zelda 2 in 6 minutes :) I used to play that game all the time with my friends when I was younger. Brought back a lot of good memories. It’s funny how you can still know all the songs after all the years pass by :)

    I did branch testing for directions the way you describe on my first NES project (the 4-way Dragon Warrior-style scroller). It worked just fine for me there. But you’re right, it gets unwieldy the more conditions you have to check for. Anytime I find myself with a lot of compares and branches in my code I try to see if I can find a way to use a lookup table instead. It’s a lot easier to read that way, and easier to tweak values and catch bugs too. I use tables for the movement too (as you’ll see in the next post). I use the hero_direction again as an index into tables that hold values of $00, $01 or $FF (ie, -1). I read from the table and add it to the hero’s coordinates. No branching needed.

    Another reason I do it this way is to separate the input handling from the rest of the engine as much as possible. The way it works now, I check the input and set the direction and I’m done. Then the update_hero routine will take care of all the logic stuff for me. Keeps the input routine nice and compact.

    Next post will be movement and animations and then the next one will be background collision detection (with video I hope!). Hope you’ll like it.

  4. Thomas says:

    @miau: I didn’t see your comment until after I replied to Rob. Sorry about that. :)

    I actually don’t calculate square roots. I do what you first said. Pressing up moves you up a pixel, pressing right moves you right a pixel and pressing up+right moves you up a pixel and right a pixel. Mathematically, diagonal movement might be faster than straight vertical or horizontal, but it isn’t really noticeable in the game.

    That said, I haven’t written any acceleration code yet that will take the hero to a speed above 1 pixel per frame (for each axis). If I do, and the speed difference is really obvious, I might have to fix it. But for now I’m going to keep it simple.

  5. [...] Tummai Games development blog for NES games « Explorer: 8 – Sprite Direction [...]

  6. [...] overruled a lot, doesn't he?). If you want to know more about this technique, I have written about protecting against odd dpad combinations using tables [...]

Leave a Reply