You have to go through the object list in order regardless just to get the object data. (For the array, it's dey to get to the next object. For ... whatever this post describes, it's lda OBJnextline,x tay). What the list(s?) stores is an object index you can use to allocate an object OR access its data through.
Knowing you may need to relink, it's easy to store the previous index that was run. Before the object loop runs, set the previous index RAM to $FF. During the loop, store the current index before retrieving the index of the next object. (This is needed to easily relink for destruction, but not for collision, so it's done once per object per frame regardless of how many times you walk the list in other ways.)
OBJnext is the next free slot.
OBJnextline is the next allocated object. The main loop looks like this:
Code:
lda #$FF
sta <lastrun
ldx <firstobject;If no objects have been created
bmi nothing;We shouldn't do anything
spritestart:
;Run object
spriteend:
stx <lastrun;Store previous index for easy relinking in case of destruction
lda OBJnextline,x
tax
bpl spritestart
nothing:
To initialize the object list:
Code:
initializeobjectlist:;{
lda #$FF
sta <firstobject
sta <lastobject
ldx #objectnum-1
stx <OBJfreeslot
ldy #objectnum-2
iol.loop:
tya
sta OBJnext,x;the next available slot is one index below the current to start
jsr zeroobject
lda #$FF;Unless otherwise stated, there are no objects following us
sta OBJnextline,x
dey
dex
bpl iol.loop
rts;}
To create and destroy objects, you have to be aware of the first and last object created (they are special cases), but otherwise don't need to walk the list.
Code:
createobject:;{
;JSR here with the high byte of the object's address in <reserved0 and the low byte of the object's address in <reserved1
;If the object creation was successful, it the negative bit will be clear
ldx <OBJfreeslot;If there's not a free object
bmi co.fail;We can't do anything
lda <firstobject;If there are no objects in the list
bpl nochangefirst
stx <firstobject;We're the first
nochangefirst:
lda OBJnext,x;Next free slot
sta <OBJfreeslot
ldy <lastobject;If there are no objects existing
bmi nofixlast;We can't link a previously created object to ourselves
txa;Otherwise, link our index
sta OBJnextline,y
nofixlast:
stx <lastobject;And claim our spot as the object more things need to link to
lda #$FF;Nothing follows us
sta OBJnextline,x
lda <reserved1;Address to run our code
sta OBJaddrlow,x
lda <reserved0
and #%01111111
sta OBJaddrhigh,x
co.fail:
rts;}
And to destroy an object:
Code:
destroyobject:;{
;Destroy the object in X
lda <OBJfreeslot;Freeing our slot
sta OBJnext,x
stx <OBJfreeslot
ldy <lastrun;If this is positive, we're not first in the list
bpl do.notfirst;
;If here, we're the first object
;But we're getting destroyed, so now the next object is the new first object
ldy OBJnextline,x
sty <firstobject
cpx <lastobject
bne do.return
;If we're also the last object
lda #$FF;And we're getting destroyed, so let's reset it
sta <lastobject
do.return:
rts
do.notfirst:
lda OBJnextline,x;So we only need to relink the object ahead of use to the object behind us
sta OBJnextline,y
bpl do.nofix;If there's an object after us
sty <lastobject;If we are, the object behind us becomes the last object
do.nofix:;We're not the object
jsr zeroobject
ldx <lastrun
rts
The collision loop looks like this:
Code:
object.collisionloop:
ldy OBJnextline,x
bmi object.skipcollision
object.collision.start:
;You'd do the and type here if you wanted.
jsr generalspritecollision
bcc object.nextcollision
jsr destroyobject
jmp spriteend
object.nextcollision:
lda OBJnextline,y
tay
bpl object.collision.start
object.skipcollision:
One of the tricks of this is you can (probably) use OBJnext as a RAM for the object after its created. (Because the data one needs from it gets stored into OBJfreeslot on creation, and restored on destruction.) (I say probably because it was true, but I stopped it from being re-used when I hacked all this list walking stuff in.)
Edit 2: So as far as RAM usage, this (probably) just needs objectnum bytes of RAM for OBJnextline. Which is 1 byte per value rather than three. (If I understand how you're measuring.) The head trick you used for the array also works here, but only one byte copy is needed to do the equivalent of "copy down". (Granted, there's extra logic for the special cases, but a lot of things can probably be optimized. I threw this together.)
The above code IS tested, but not thoroughly tested, so I don't guarantee yet that there aren't fragmentation issues here! I wrote this because I was pretty sure the concept I was suggesting would work, but hadn't actually done it!
Another note: I definitely don't guarantee destroying the first object works properly, the thing I hacked this into makes it somewhat hard to test that without also removing the ability to test the rest of the collisions.
Final verdict: It is ever so slightly faster when you're not maxing out your available objects and have room for more objects than you will probably need. Whether that's worth objectnum bytes of RAM, I don't know. Edit: Maybe I'm not going to bed. If you use the linked list concept in the tokumaru post I linked for object creation and destruction, but then go through the array the normal way you don't need an extra objectnum bytes of RAM. Which... is what I was doing before I tried this. It's neat in that you can create and destroy things without needing to seek and it only costs a byte of RAM. (Because the RAM for the rest of the list can be used by an object once instantiated. That said, in the context of this topic, it's not really any faster unless LOTS of objects are being created in a single frame or something.)
If the concepts here aren't clear, let me know. Also let me know if it you find a logic error! I still recommend that if anyone reading wants to do something like that, they steal from this linked post:
https://forums.nesdev.com/viewtopic.php ... st#p152230Rather than this post. Edit3: As far as object creation without a seek, I truly believe the tokumaru approach is better (It's very similar to the head code for the array, and costs one byte of RAM when you're not trying to maintain a thing to walk through.) As far as if walking a linked list is better than the array approach, I'd have to think more about it, I dunno. This code gave me a decent amount of trouble. I'll probably continue doing what I did before I wrote this post: Linked list for object creation/destruction, walk the array for everything else until some project requires more.
I'm now going to bed.
I hope I don't wake up and find 1,000 errors in this post >_>