This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Collision Detection errors

Collision Detection errors
by on (#90533)
Hi folks! I'm in the process of writing my own homebrew game and implementing collision detection has been a troublesome task, as I am stuck at an error that seems to have no explanation and figured this would be the right forum to ask.

The data I am working with is an entire uncompressed screen (well, 2 of them but at this moment I am only concerned about working with one.)

This is the code I am working with.

checkCollisionRight:
LDA #$20 ;; Hi byte since nametable starts at $2000
STA collisionHi

LDA HeroY ;; Takes Hero Y coordinate, divides by 8
LSR A
LSR A
LSR A
STA collisionLo

LDX #$00
shiftLoop:
LDA collisionLo
CLC
ASL A
STA collisionLo
LDA collisionHi
ADC #$00
STA collisionHi
INX
CPX #$05 ;; This run five times since it must be multiplied by 32
BNE shiftLoop

endCollision:
RTS

All this is trying to do at the moment is returning the current line that the hero is walking on, not the exact tile. It does fine for the first half of the screen, from $2000-21FF, but for some reason, when the hero is beyond tiles $2200, the high byte does not increment but rather loops from $2100 all over again. Only once you reach the bottom quarter of the screen does it increase to $2200, where it should actually be at $2300.

by on (#90537)
Maybe I'm misunderstanding something here, but I don't think you can do what you want to do that way.

The first nametable starts at $2000, but that's in the PPU's memory. The CPU can't access that data directly.

If I'm understanding correctly, your goal with this subroutine is to get the PPU address for the tile (well, first tile in the row) that the hero is currently standing on? There's really not much reason to do that for collision.

In any case, maybe this breakdown of what part of your routine is doing will help with your actual glitch:

Code:
LDX #$00
shiftLoop:
LDA collisionLo
CLC;You clear the carry flag
ASL A;But this line shifts the highest bit of A into the carry
;Effectively obliterating what CLC just did.
STA collisionLo
LDA collisionHi;Because of the ASL above, the carry is unknown here.
ADC #$00;So this could add 0 or 1, but not in a controlled way

;Edit: Actually I just realized that because of your divides (LSR instructions) before this loop begins, this will add 0 at least 3 times in a row, because the highest bits of collision low are guaranteed to be zero.

STA collisionHi;
INX
CPX #$05 ;; This run five times since it must be multiplied by 32
BNE shiftLoop

endCollision:
RTS


If you're looking for how to multiply a 16bit number by 32, you do it like this:

Code:
ldx #$05
shiftloop:
lda lowbyte
asl a
sta lowbyte

lda highbyte
rol a
sta highbyte

dex
bne shiftloop


And you can actually skip some steps. Here is what you can do instead of dividing by 8, then multiplying by 32.


Code:
lda HeroY
and #%11111000;Gets rid of the lowest bits, which is what
;the first three divides did


From there, you can get away with multiplying the 16 bit number by only 4, since you don't have to undo some divides while you multiply.

The next step can then add the hero's y position to $2000 with a 16bit add.

Then divide his X position by 8 and add that with a 16bit add. Which I think is a step further than what you're doing now.

The above method also fixes an issue with your current process. You divide only the low byte by 8, but then you (I assume) are trying to multiply the high AND low bytes by 32. This means your low byte is no longer matched up with the high byte during the multiplies.


If that's not what you want, I'm actually stumped with your goal and your process. Even if that is what you want, it's not exactly a great way to do this unless you're doing something like drawing a tile where the hero is currently standing.

Could you be even more detailed for with your end goal and current goal so I can provide better help?

by on (#90540)
Like Kasumi said, your multiplication by 32 is wrong. Even if the multiplication itself was correct, there would be no reason to multiply the high byte of the name table address, it would actually have to be added at the end.

If the coordinates of the here are expressed in pixels, this is the formula to find the tile it falls in: (HeroY / 8) * 32 + (HeroX / 8) + $2000.

Like Kasumi said, the division by 8 can be combined with the multiplication by 32, so that you only have to multiply by 4 instead (after clearing the lower 8 bits). Then you can divide HeroX by 8 and add to the 16-bit number, and finally add $20 to the high byte. That will get you the correct address for the tile, assuming that the player coordinates are 8-bits and that he can only move inside the first name table. If there's scrolling involved and the player can access more than 1 name table, things are obviously more complex.

Again like Kasumi said, checking tiles in VRAM is not the optimal way to check for collisions. Your game logic typically runs as the frame is rendering, meaning that VRAM can not be read at that moment, so you'd have to wait to check tiles only during VBlank, which would result in a lot of wasted time. Ideally, games have the level maps stored in ROM or RAM, where they can be "consulted" at any time.

by on (#90541)
Thank you for your responses, Kasumi and tokumaru. I was able to get the correct number using this routine with your help:

LDX #$05
shiftLoop:
LDA collisionLo
ASL A
STA collisionLo
LDA collisionHi
ROL A
STA collisionHi
DEX
BNE shiftLoop

LDA collisionHi
CLC
ADC #$20
STA collisionHi

Adding the "ROL A" bit helped to get the correct number, it's just that I had to add 20 at the end since it was only returning 00,01,02, or 03. Right now my game is very uncompressed - The whole thing only consists of 2 screens, since this is my first attempt I figured something overly ambitious would be discouraging so I decided to start small. The screens are located at $E000 as well so I get the data directly from this location as well, it's just that I wanted to pull this from the PPU since I can poke around with it in the HEX editor for fceuxd and see if these calculations were correct. Thanks again for the help.