A single page of NES Work RAM is dedicated to this data structure. It allows a maximum of 16 "objects" to exist on screen at once. These include the player cursor (which is always object zero), as well as the fish in the aquarium, fish food, air bubbles, etc. If an object is "created," the game will check to see if there are any empty object slots, and if not, no object is created. Otherwise the object's data will be stored in the lowest empty slot. If an object is "destroyed" the slot that object was in will be emptied.
This system is similar to the one NESMaker uses (and I have some hesitation using it since I'm concerned it's a little too similar, despite owning a license for the software I'm not using it to make this game, but I digress).
I'm trying to test out this system by "creating" three objects, with just enough data needed for the structure to function. Their object ID, X position, and Y position are all loaded, and the OBJECT_SLOTS variable in that column are set to 1 indicating an active object is contained within. Then I created this loop which was intended to copy the info needed to actually draw those objects on screen into shadow OAM. However, it failed producing a big mess of sprites all over the place. I'm not sure what went wrong, and it's difficult to debug this since I can't actually see any sprites appear in the PPU Viewer until after DMA occurs (and by then it's too late to see what is actually happening in the loop that's causing the failure.)
Here's the code. Again, please forgive the wonky formatting; that's something that keeps happening when I copypaste from Notepad++ to these forums.
Hopefully you all could be so kind as to help me with the following questions:
1. Is this a good or bad way to go about creating a data structure?
2. What went wrong with my loop?
3. Did I explain this data structure well enough?
4. Is this too similar to NESMaker's code for me to publish this game independently?
Code: Select all
TOTAL_MAX_OBJECTS = 16
.enum $0500 ;OBJECT RAM
OBJECT_XPOS .dsb TOTAL_MAX_OBJECTS
OBJECT_YPOS .dsb TOTAL_MAX_OBJECTS
OBJECT_ID .dsb TOTAL_MAX_OBJECTS ;$0430
OBJECT_SIZE .dsb TOTAL_MAX_OBJECTS ;$0450 Total number of sprites needed to display the object.
OBJECT_SLOTS .dsb TOTAL_MAX_OBJECTS ;$0490 0 if empty, otherwise that slot is occupied.
.ende
INIT:
;WE WILL TEST THIS INIT CODE BY STARTING WITH THREE OBJECTS: THE CURSOR, A BUBBLE, AND A BETTA FISH.
LDX #$00
LDA #$01
STA OBJECT_SLOTS,x
STA OBJECT_SLOTS+1,x
STA OBJECT_SLOTS+2,x
LDA #$00
STA OBJECT_ID,x
LDA #$01
STA OBJECT_ID+1,x
LDA #$02
STA OBJECT_ID+2,x
LDA #$80
STA OBJECT_XPOS,x
STA OBJECT_YPOS,x
LDA #$40
STA OBJECT_XPOS+1,x
STA OBJECT_YPOS+1,x
LDA #$20
STA OBJECT_XPOS+2,x
STA OBJECT_YPOS+2,x
;x should still be zero.
;LDX #$00
outer_loop_init: ;X = the object slot being copied to OAM. Y = which sprite's data is being copied.
LDA OBJECT_SLOTS,x
BNE continueToDrawObject ;if the slot is empty don't draw anything in it.
JMP skip
continueToDrawObject:
LDA OBJECT_ID,x
TAY ;use as index into object_id lut.
LDA OBJECT_ID_LO,y
STA pointerLo
LDA OBJECT_ID_HI,y
STA pointerHi
LDY #$00
STY looptempY ;we can't use pointers without Y, and Y needs to be zero for this to work. So we'll have to do something messy.
inner_loop_init:
LDA (pointerLo),y ;y pos
CLC
ADC OBJECT_YPOS,x
STA temp0 ;store y congruent to 0 mod 4 into temp0
INY
LDA (pointerLo),y ;tile
STA temp1 ;store y congruent to 1 mod 4 into temp1
INY
LDA (pointerLo),y ;attribute data
STA temp2 ;store y congruent to 2 mod 4 into temp2
INY
LDA (pointerLo),y ;x pos
CLC
ADC OBJECT_XPOS,x
STA temp3 ;store y congruent to 3 mod 4 into temp3
INY ;now Y is congruent to 0 mod 4 again.
;now we need y to equal the number of times we've done the above.
TYA
PHA
LDY looptempY
LDA temp0
STA $0200,y
LDA temp1
STA $0201,y
LDA temp2
STA $0202,y
LDA temp3
STA $0203,y
INC looptempY
PLA
TAY
;now we need to see if we've drawn all the sprites we need to draw.
LDA looptempY
CMP OBJECT_SIZE,x ;x did not change throughout the entire inner loop.
BNE inner_loop_init
;we've finished drawing all the sprites for this game object, so increment x so that we can draw the next one.
skip:
INX
CPX TOTAL_MAX_OBJECTS-1
BNE outer_loop_init
Code: Select all
OBJECT_ID_LO: ;tells us WHERE to collect OAM data from.
.byte #<cursor, #<bubble, #<fish_betta
OBJECT_ID_HI: ;tells us WHERE to collect OAM data from.
.byte #>cursor, #<bubble, #<fish_betta
DEFAULT_OBJECT_SIZE: ;tells us how many times to loop during OAM setup.
.byte #$04,#$01,#$08
fish_betta: ;format: y,tile,attrib,x
.byte 0,$23,2,0
.byte 0,$22,2,-8
.byte 0,$21,2,-16
.byte 0,$20,2,-24
.byte -8,$13,2,0
.byte -8,$12,2,-8
.byte -16,$11,2,-16
.byte -24,$10,2,-24
cursor:
.byte 0,$08,0,0
.byte 0,$09,0,8
.byte 8,$18,0,0
.byte 8,$19,0,8
bubble:
.byte 0,$01,0,0