Now that I have the ability to make some rooms, and connect them together into floors, it would be nice if I had some mechanism to go from one room to another. To do this, I will need some subroutines that will change the room coordinates in RAM and then load the new room. This is really easy to code:
move_east:
ldx floor_roomx
inx
ldy floor_roomy
jsr floor_load_room ;this subroutine takes coords in x and
;y and loads the room at those
;coordinates (within the same floor).
rts
move_west:
ldx floor_roomx
dex
ldy floor_roomy
jsr floor_load_room
rts
Subroutines for moving north and south look similar. But there’s room for improvement here. Notice that I load the new room coordinates into x and y in both subroutines. I can save some bytes if I shorten it up a little bit:
move_west:
dec floor_roomx ;current room x coord for the floor
jmp :+
move_east:
inc floor_roomx
:
ldx floor_roomx
ldy floor_roomy
jsr floor_load_room
rts
If I really wanted to I could probably replace that JMP instruction with a BPL (branch if positive) and save an extra byte. I’ll leave it how it is for now for readability. I’ll save optimizations for the very end if I need them.
Changing Floors
Changing floors isn’t much harder. First I’ll have to load the new floor, and then I’ll have to load a room within that floor. Good thing I have the load_floor and load_room subroutines already written. Having a good foundation to build upon makes things easy:
floor_down:
dec map_floor ;this var holds the current floor.
jmp :+
floor_up:
inc map_floor
:
lda map_floor
jsr load_floor ;loads the floor, setting up room ptrs, etc
ldy floor_roomy
ldx floor_roomx
jsr floor_load_room
rts
Floor Boundaries
The thing I need to watch out for now is the floor boundaries. I don’t want to call move_west when I’m currently at x=0 or my game will try to load a room at x=FF, which probably isn’t there. When the game tries to move west, I will want to check if x==0 or not. If it is, I will skip the call. If the game tries to move east, I will want to check if x is equal to floor’s x-dimension – 1. If I’m in the eastern-most room, skip the call. Likewise for moving downstairs or upstairs. I will want to check the current floor number against 0 or the top floor number, respectively.
Triggering a Room Change
I have everything set up to change rooms. Now I need something to trigger the room changes. Most games trigger room changes based on sprite position/collision. If the user moves their character sprite to the edge of the screen, change the room. I haven’t coded sprite-support in yet, so for testing purposes I’m going to trigger room changes directly to the input. Pressing right will move east. Pressing down will move south. Pressing left will move west. Pressing up will move north. I’ll use B and A for going downstairs and upstairs.
Reading Input
First I need a joypad reading routine. The one I’m using is based off a really cool one that blargg posted on a nesdev thread. It preserves both x and y.
update_joypad_data: ;thanks to blargg for this A-only joypad
;read routine.
lda joypad1
sta joypad1_old ;save previous joypad data
lda #%01111111
sta joypad1
sta $4016
asl a
sta $4016
@loop:
lda $4016
and #$03 ;props to Disch for Famicon 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
For changing rooms I will want to check joypad1_pressed. This variable tracks off-to-on transitions for the buttons. Using joypad1_pressed instead of joypad1 ensures that I will only move once per button press.
The joypad reading routine is called once per frame. There are many frames in a second. If the player presses up for a second, the variable joypad1 will report an up press for each frame the button was held. That’s a lot of ups. joypad1_pressed will only report one up press – the first one. I only want to move once per button-press, so I’ll check my input using joypad1_pressed.
So I read the joypad, then I perform actions based on the input. If left was pressed I’ll check to make sure that our room x-coord isn’t 0, and then I’ll call move_west. If up was pressed I’ll check to make sure our room y-coord isn’t 0, then call move_north if it isn’t. ETC..
Take a look
I made a demo video showing room navigation. I hope to make several videos like this as I code the game. I think it will be cool to document the making of an NES game like this. Anyway, take a look:
Conclusion
Room navigation works great. The next step is to put a movable sprite into the game and have room changes triggered by that sprite’s coordinates. Then it will start to look like a real game!
