Scrolling collision anomalies

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Scrolling collision anomalies

Post by JoeGtake2 »

Hey all - got a nifty little simple LR scrolling routine 99% working with the existing structure. It's ugly, being that it has to work around some things that are already there, but it works well enough for the most part.

Using two nametables and a 32 value column tracker, I've got it updating tiles, attributes, and collisions just as expected, when expected.

One of the challenges has been in making the room for two collision tables. I can't exactly put them adjacent for right now, but I have collisionTable1 and collisionTable2 in RAM. The thing that keeps tripping is doing a test to determine which collision table to read from upon collision detection. I've had to do some crazy tapdancing to get it to work, and I'm left with the very occasional read from the wrong table.

Effectively, I have a oScroll value assigned for each object, which is set at first to the table, but sort of acts as a high byte for the position as well. My method has been something like this:

- Clear the carry
- Load x + speed, store it in temp.
- Load oScroll+0 (if there was a carry, this would increase, if not, would stay the same as oScroll). Store it in temp2.
- do collision detection. If all was clean, load temp2 into oScroll, load temp into x.

Same thing with left movement, but doing a 16 byte evaluation to check if the carry is still set.

Then in the collision detection, I'm reading, and based on what is in temp2, it determines if I should read from the *same page* (if my nt is the first nt, it reads from collisionTable, if it was looking at second, it reads from collisionTable2), or the *opposite page*.

All of this works perfectly, exactly as expected, EXCEPT every once in a while when moving back left, it reads from the wrong table. And I mean about 5% of the time, without any clear pattern as to why.



Knowing I can't easily put the col data consecutively and for now at least need to work off of this two separate table scenario, anyone have any better suggestions for handling this? I had originally thought it was a rather clever way around the issue. But keep having this anomaly, and every time I THINK it's fixed and move on, it pops up again.

Curious what you guys might advise!
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Scrolling collision anomalies

Post by tokumaru »

I'm having a hard time understanding your logic, maybe because I don't have enough information or I just plain suck at understanding solutions I didn't design, but I don't know why you'd need to explicitly test which table to check and things like that... Maintaining different states and flags in sync is always harder than keeping everything in the same consistent space for as long as possible, and only doing coordinate conversions when interacting with things living in other spaces.

For example, if you have 2 screens worth of collision data, just have everything live in a circular 512x240 area, and keep the update seam near the center of the off-screen area, so you have plenty of room for objects near the edges of the screen to interact with the map without having the collision data changing below them.

As for your specific issue, without fully understanding the architecture of your engine, I can only guess that maybe you're changing an entire collision map at once when the camera crosses a screen boundary, and that causes problems with objects near the edges of the screen. If that's not how you're losing collision data though, there's probably a sync issue involving the separate mechanisms you use for scrolling, maybe triggered by switching directions or something.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Scrolling collision anomalies

Post by tepples »

Super Mario Bros. uses a 512x208-pixel circular buffer, organized internally as two 256x208-pixel sub-buffers. Perhaps the logic problem relates to selecting which of the two sub-buffers to read.
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: Scrolling collision anomalies

Post by JoeGtake2 »

Sorry, I'll clarify (and report some things I found)

Tepples, yes this is similar to what I'm doing. I have two *collision table* buffers in ram...16x15 each, with defined collision types. I have a routine that does the math on the position to determine which collision byte is at the potential position. This all works no problem single screen. Super easy. Flawless.

The seam for my collision happens off screen. Basically, each screen is 16 columns...the column is metatile, attribute quadrant, and collision byte. So, the scroll window "camera" seam is always updating the 24th out of 32 columns. Also no problem. I can move either direction, and the NT, ATT and Col columns update as expected.

The challenge comes when the player is on the boundary of the two screens (and it seems that moving *left* is causing it, after a lot of testing). Basically, I have a byte for each object called oScroll. This acts as a high bit for sort of macro positioning on a screen (there are 256 screens per map). So lets say I start on screen 0, then oScroll starts as 0. When I cross the threshold, it's easy:

Code: Select all


   LDA playerx
   clc
   adc playerspeed
   LDA oScroll
   ADC #$00
   STA oScroll

If there is a carry (player has moved to the right of the screen), oScroll increase, which now also designates what screen the player is on. Conceptually, super easy.

But now to do a four corner collision detection script, it gets tougher, and this seems to be where it gets messy, but only when moving left. Right seems to always work as I would expect.

First, I know which collisionTable the object's left point is, because I can simply check oScroll AND #%00000001...if that's a zero, it's an even screen, thus it should check collisionTable1. If it's a one, it's an odd screen and should check collisionTable2.

But when I check, say, the right point, it needs to evaluate whether or not the right point puts it across the screen threshold. Like this:

Code: Select all


    LDA playerx
    CLC 
    ADC playerWidth
    BCC nevermindThreshold
;;;; Ok, here the threshold is reached.
;;;; so the right most point will be
;;;; in the opposite collision table.

    LDA oScroll
    AND #%00000001 ;; if 1, this means we are in an odd
                                 ;; screen, so we should check
                                 ;; even
    BNE checkEvenScreen
    JMP checkOddScreen
    
nevermindThreshold:
     ;;; threshold was not reached, so this point
     ;;; follows normal rules
     LDA oScroll
    AND #%00000001
    BNE checkOddScreen
    JMP checkEvenScreen

;;; then those two routines...
checkEvenScreen:
     LDA collisionTable1,y
     JMP gotPoint
checkOddScreen:
     LDA collsionTable2,y
   
gotPoint:
    ;;;now, in A, we have the collision value
    ;; to evaluate.

It's an ugly code, but moving right, this works perfectly. No issues. But left, when I hit the threshold for the screen is when things don't behave as expected, it starts pulling form the wrong collision table.

How much am I overcomplicating? What might be a more sane way to do this relatively basic thing? (Be able to evaluate collision points across two RAM tables) Or is this a reasonable approach, and I should just keep mining for whatever gremlin keeps popping up?
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: Scrolling collision anomalies

Post by JoeGtake2 »

**SOLVED, I think**

Method works fine. On subtract, I was getting a false positive of it working do to the construct of the screens. It was a REALLY stupid mistake - where adding, the carry would implicitly add to the oScroll, such was not the case with subtraction to subtract from it. Instead, I just built in a check to see if the carry was clear, and if it was, manually subtracted one from the oScroll in that case, otherwise left it alone.

This seems to have fixed the issue...played around on it for 10 minutes, trying to see it break, toying around on the seams, and so far, no site of the gremlin.

Thanks for the responses guys. That was driving me crazy!
Post Reply