reaktor24 wrote:
Wow, I never thought 'simple' NES games could get so complicated! lol.
In simple projects you can hardcode a lot of stuff and skip the complications of dealing with dynamic data, but that doesn't scale well as projects grow.
Quote:
I simply replaced run_right_frame_1 with player_animation with the first value as the first frame. Well it didn't work, I'm clearly missing something. It loads a garbled random few tiles but it's not the correct frame I thought it would load. I must be missing something that's probably very obvious? If it was C++ I would have gave the value as player_animation[0]. Still too new to asm.
You're using indexed addressing to read the contents of the run_right_frame_1 array, and that addressing mode works by adding a variable index (held in either X or Y) to a constant base address, which can't be changed, so you won't be reading data from anywhere else than run_right_frame_1 while using that addressing mode. To be able to change the base address, you need to use indirect indexed addressing, an addressing mode where the base address isn't constant, but held in a pointer in ZP, so you're free to change that base address to point to the base address of other animation frames.
So, here's how the data is set up:
Code:
animation_frames:
.dw run_right_frame_1, run_right_frame_2, run_right_frame_3
run_right_frame_1:
.db $80, $32, $00, $80
.db $80, $32, $00, $80
;(...)
.db $ff
run_right_frame_2:
.db $80, $32, $00, $80
.db $80, $32, $00, $80
;(...)
.db $ff
run_right_frame_3:
.db $80, $32, $00, $80
.db $80, $32, $00, $80
;(...)
.db $ff
I put an end marker after each frame's sprites, so that you can easily tell when to stop copying sprites.
And here's the code to select one of the pointers:
Code:
lda frame_index ;get the frame index
asl ;multiply it by 2 since each address is 2 bytes
tax ;use it as an index
lda animation_frames+0, x ;copy the low byte
sta frame_pointer+0 ;to a position in ZP
lda animation_frames+1, x ;copy the high byte
sta frame_pointer+1 ;to the next position
Just run this whenever frame_index changes (which in your test code should be when A is pressed, right?), in order to update the pointer to the current frame of animation.
Then you just need to update the copy loop to use this pointer and the end-of-sprite marker:
Code:
ldy #$00 ;indirect indexed only works with Y
LoadFrame:
lda (frame_pointer), y ;get Y coordinate
cmp #$ff ;check for the end of the sprite
beq Done
sta $200, y ;copy the Y coordinate
.rept 3 ;copy the remaining 3 bytes
iny
lda (frame_pointer), y
sta $200, y
.endr
jmp LoadFrame
Done:
This should solve your simple case of just displaying different sprites when A is pressed, but an actual game will need a much more complex routine than that. First, you'll have to use separate indices for the source and destination (indirect indexed addressing only works with Y, so you can use X to index the destination), otherwise you won't be able to draw more than one character at a time. Second, you'll need to add the current position of the game object to the sprite coordinates in order to make the sprite movable (this may or may not affect the use of $ff as a end-of-sprite marker, but there will always be a value you can use). Third, you'll have to check for overflows and underflows in the resulting coordinates, in order to handle clipping when sprites are partially off-screen (unless your game mechanics do not allow sprites to be partially off-screen, ever). Fourth, you'll need to handle flipping, so your sprites can face left and right (and possibly up and down). Some people just use different metasprite definitions for left and right-facing sprites, but dynamic flipping isn't particularly hard to do.