Strange Issue With Metatile Collision

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
rlawlor
Posts: 6
Joined: Tue Sep 01, 2020 2:46 am

Strange Issue With Metatile Collision

Post by rlawlor » Tue Sep 01, 2020 2:55 am

I just managed to decode metatile level data to display on screen and shortly after tried to write code for collision... but it seems like the collision for a metatile is governed by the top left corner for some reason. The following is my code for checking collision:

Code: Select all

CheckCollide:
    TXA
;    ADC scroll
    STA collideX
    LSR
    LSR
    LSR
    LSR                     ; divide x by 16 to get into metatile space
    STA tempX
    TYA
    STA collideY
    LSR
    LSR
    LSR
    LSR                     ; divide y by 16 to get into metatile space
    ASL
    ASL
    ASL
    ASL                     ; multiply y by 16 to change rows of level table
    STA tempY
    LDA tempX
    ADC tempY               ; X/16 + (Y/16)*16
    TAX
    LDA (Level1), x         ; load what current metatile you are in
    STA currentMetatile

CheckCurrentTile:
    LDA collideX
    AND #%00001111
    LSR
    LSR
    LSR                     ; divide by 8
    ADC #$01
    STA metatileX           ; find out if player in right or left of metatile

    LDA collideY
    AND #$00001111
    LSR
    LSR
    LSR                     ; divide by 8
    ADC #$01
    ASL
    ASL                     ; multiply by 4
    STA metatileY           ; find out if player in top or bottom of metatile

; 0 0 0 0 0 0 0 0
;         | | | |_ X
;         | | |___ X
;         | |_____ Y
;         |_______ Y

    CLC
    LDA metatileX
    ADC metatileY
    STA metatileCorner      ; calculate what corner the player is in

    LDA metatileCorner
    AND #%00000101          ; x = 01, y = 01
    BEQ +
    JMP TopLeft
+
    LDA metatileCorner
    AND #%00000110          ; x = 10, y = 01
    BEQ +
    JMP TopRight
+
    LDA metatileCorner
    AND #%00001001          ; x = 01, y = 10
    BEQ +
    JMP BottomLeft
+
    JMP BottomRight         ; x = 10, y = 10

TopRight:
    LDX currentMetatile
    LDA (CastleTR), x       ; find out what block type player is on
    TAX
    LDA (TileProperties), x
    AND #%00000001          ; only check tile collision
    
    RTS

TopLeft:
    LDX currentMetatile
    LDA (CastleTL), x       ; find out what block type player is on
    TAX
    LDA (TileProperties), x
    AND #%00000001          ; only check tile collision

    RTS

BottomRight:
    LDX currentMetatile
    LDA (CastleBR), x       ; find out what block type player is on
    TAX
    LDA (TileProperties), x
    AND #%00000001          ; only check tile collision

    RTS

BottomLeft:
    LDX currentMetatile
    LDA (CastleBL), x       ; find out what block type player is on
    TAX
    LDA (TileProperties), x
    AND %00000001          ; only check tile collision
    
    RTS
and this is the TileProperties and metatile tables (only collision is being used as a property currently, the others are purely examples I may use in future):

Code: Select all

;===================================Meta Tiles===============================================

CastleTL: .db $00, $00, $00, $00, $07, $07, $07, $07
CastleTR: .db $00, $07, $07, $00, $07, $07, $07, $07
CastleBL: .db $00, $00, $00, $07, $00, $07, $07, $07
CastleBR: .db $00, $07, $00, $07, $00, $07, $07, $07

;==================================Tile Properties===========================================

;0 0 0 0 0 0 0 0
;| | | | | | | |_ Collidable
;| | | | | | |___ One Way Platform
;| | | | | |_____ Ladder
;| | | | |_______ Kills
;| | | |_________ Slippery
;| | |___________ ?
;| |_____________ ?
;|_______________ ?

TileProperties:
    .db %00000001, %00000000, %00000100, %00000000      ; $00 to $1F
    .db %00000000, %00000000, %00000000, %00000000
    .db %00000000, %00000000, %00000000, %00000000
    .db %00000000, %00000000, %00000000, %00000000
    .db %00000000, %00000000, %00000000, %00000000
    .db %00000000, %00000000, %00000000, %00000000
    .db %00000000, %00000000, %00000000, %00000000
    .db %00000000, %00000000, %00000000, %00000000
    etc. etc.

rlawlor
Posts: 6
Joined: Tue Sep 01, 2020 2:46 am

Re: Strange Issue With Metatile Collision

Post by rlawlor » Tue Sep 01, 2020 2:57 am

The following is an example of a way in which CheckCollide would be called just for some sort of context:

Code: Select all

MovePlayer1Right:
    LDA #%00000000
    STA $206
    LDA p1XVelocityL            ; load lower byte of x velocity
    CLC                         ; clear carry flag
    ADC #xAccelerationL         ; add x acceleration
    STA tempL                   ; store in a temporary variable
    LDA p1XVelocityH            ; load high byte of x velocity
    ADC #xAccelerationH         ; add x acceleration
    STA tempH                   ; store in a temporary variable

    LDA tempL                   ; load temporary variable
    CMP #maxXVelocityL          ; compare with max allowed velocity
    LDA tempH                   ; load temporary variable
    SBC #maxXVelocityH          ; subtract max allowed velocity

    BVC +                       ; branch on overflow clear
    EOR #$80                    ; exclusive or with %1000000
+
    BMI NotMaxVRight            ; branch on result minus
    LDA #maxXVelocityL          ; load max x velocity (low) into accumulator
    LDX #maxXVelocityH          ; load max x velocity (high) into x register
    JMP MaxVRight

NotMaxVRight:
    LDA tempL                   ; set temp variable to max allowed velocity
    LDX tempH

MaxVRight:
    STA p1XVelocityL            ; store new velocity
    STX p1XVelocityH

    LDA player1XF               ; load player 1 fractional x coordinates
    CLC                         ; clear carry
    ADC p1XVelocityL            ; add velocity
    STA player1XF               ; update player coordinates

    LDA player1XI
    ADC p1XVelocityH
    STA player1XI

    LDA player1YPos             ; load player 1 y location
    CLC
    ADC #$01
    TAY
    LDA player1XI               ; load player 1 x location
    CLC
    ADC #$07
    TAX
    JSR CheckCollide
    BEQ +
    CLC                         ; clear carry
    LDA player1XF               ; load player 1 fractional coordinates
    SBC p1XVelocityL            ; subtract velocity
    STA player1XF               ; update coordinates
    LDA player1XI               ; load player 1 integer coordinates
    SBC p1XVelocityH            ; subtract velocity
    STA player1XI               ; update coordinates
    LDA #$00
    STA p1XVelocityL
    LDA #$00
    STA p1XVelocityH
+
    LDA player1YPos
    CLC
    ADC #$08
    TAY
    LDA player1XI
    CLC
    ADC #$07
    TAX
    JSR CheckCollide
    BEQ +
    CLC                         ; clear carry
    LDA player1XF               ; load player 1 fractional coordinates
    SBC p1XVelocityL            ; subtract velocity
    STA player1XF               ; update coordinates
    LDA player1XI               ; load player 1 integer coordinates
    SBC p1XVelocityH            ; subtract velocity
    STA player1XI               ; update coordinates
    LDA #$00
    STA p1XVelocityL
    LDA #$00
    STA p1XVelocityH
+

    RTS

User avatar
dougeff
Posts: 2735
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Strange Issue With Metatile Collision

Post by dougeff » Tue Sep 01, 2020 3:30 am

lsr x4
asl x4

Don't do that. Just use
and #$f0

Code: Select all

LDA (Level1), x
There is no such opcode. Indirect like this is always with the Y register.

LDA (Level1), y

The assembler is probably silently converting your code to this LDA Level1, x
nesdoug.com -- blog/tutorial on programming for the NES

rlawlor
Posts: 6
Joined: Tue Sep 01, 2020 2:46 am

Re: Strange Issue With Metatile Collision

Post by rlawlor » Tue Sep 01, 2020 5:17 am

lsr x4
asl x4

Don't do that. Just use
and #$f0
Thanks for the tip, certainly saves a lot of code! As for the "LDA (something), x" not being an opcode I had no idea. I changed it there and I'm still getting a similar problem so I'm not sure why it was working in the same way with x. Thanks for the help :D .

Drag
Posts: 1322
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Re: Strange Issue With Metatile Collision

Post by Drag » Tue Sep 01, 2020 5:33 pm

You're using lda (nn), Y (Indirect Y addressing) but I think you actually want lda nnnn, Y (Absolute Y addressing). :P

lda ($08), Y means, "I've written a 16-bit address in RAM at $08 and $09, take the address I wrote there, add Y to it, that's the byte I want to fetch."

lda $9040, Y means, "Fetch the byte at ($9040 + Y)", and you can use either Y or X.


The next thing to note is with this chunk of code:

Code: Select all

    LDA collideX
    AND #%00001111
    LSR
    LSR
    LSR                     ; divide by 8
    ADC #$01
    STA metatileX           ; find out if player in right or left of metatile
That final LSR will shift bit 2 of collideX into carry, which is going to affect the ADC. From the looks of the following code, this looks unintentional. :P You can drop a CLC there if you want, but a shortcut you can use is to change your AND mask from #%00001111 to #%00001000. (Can you guess why?)



----------
For posterity, Indirect X addressing is: lda ($08, X) = "Starting at RAM addresses $08-$09, I've written several 16-bit addresses, one at $08-$09, one at $0A-$0B, etc. Look at memory address ($08 + X), the 16-bit address I've written there is the address I want to fetch from"

So, for example, if the memory starting at $08 looks like "40 90 B8 90 D0 90", and you do lda ($08,X) with X set to 4, you will fetch a byte at $90D0.

It's funny that, when you do indirect addressing, the behavior is different depending on whether you're using X or Y as your index, but that's how it goes.

rlawlor
Posts: 6
Joined: Tue Sep 01, 2020 2:46 am

Re: Strange Issue With Metatile Collision

Post by rlawlor » Wed Sep 02, 2020 8:45 am

You're using lda (nn), Y (Indirect Y addressing) but I think you actually want lda nnnn, Y (Absolute Y addressing).
Spot on :D . Didn't realise there were multiple types of addressing.
change your AND mask from #%00001111 to #%00001000
That's something I never would've thought to do, thanks.

I've made all the changes suggested but it seems I'm still getting the same problem. I know my code for finding what metatile the player is in works so it could be that the rest of my code just needs rewritten in some form or another. Originally I was using RLE compression and a collision bitmask but very quickly realised how much storage space that would take up with a scrolling game hence the change to metatiles. Its nice being able to specify properties for each tile in a table and call it whenever you need to check something (or at least it will be when I get it working properly :D ).

rlawlor
Posts: 6
Joined: Tue Sep 01, 2020 2:46 am

Re: Strange Issue With Metatile Collision

Post by rlawlor » Wed Sep 02, 2020 8:57 am

I know my code for finding what metatile the player is in works
Famous last words... I disabled collision and did a quick check and it seems it thinks each metatile is one normal tile to the left of where it actually is (i.e. 8 pixels to the left of where it is on screen). So time to take a look through that code :roll: .

User avatar
dougeff
Posts: 2735
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Strange Issue With Metatile Collision

Post by dougeff » Wed Sep 02, 2020 10:46 am

You could just add 8 before doing the calculations.
nesdoug.com -- blog/tutorial on programming for the NES

rlawlor
Posts: 6
Joined: Tue Sep 01, 2020 2:46 am

Re: Strange Issue With Metatile Collision

Post by rlawlor » Wed Sep 02, 2020 11:06 am

Turns out I was using LSR before ADC and so I had a carry in my calculations :oops: .

Post Reply