Page 2 of 2

Re: Need some help and advice with my collision detection

Posted: Sat Sep 13, 2014 6:43 am
by tepples
Sik wrote:
Kasumi wrote:Does $2005/$2000 really have to be written every frame?
Yes, because the address completely changes after it rendered the entire screen (the address keeps changing as it fetches the pixels for the background).
How so? If you change t (top of screen) through $2005, and you don't modify the background through $2005-$2007, then the PPU will keep the scroll in the same position. (In "skinny" terms, this is the v := t, or set video memory pointer to top of screen, that occurs in the vsync after the pre-render line.) But if you're planning to scroll, you should plan on setting $2005 every frame for two reasons: 1. you'll be loading new map data at the edge of the screen, and 2. the camera will be moving.

Re: Need some help and advice with my collision detection

Posted: Sat Sep 13, 2014 7:40 am
by SNgamer
Kasumi wrote:Okay, figured it out. Here's a ROM (Let me know if you want it removed, it's here to prove I'm not nuts), as well as the changes. If the changes I listed don't give you this result, tell me and I will find out what I forgot.

Code: Select all

main:;checks for collision
bit $2002
bpl main
lda $0204;vert pos of metasprite
clc
adc #$09
lsr a
lsr a
lsr a
sta ypos
lda $0207;hor pos of metasprite
lsr a
lsr a
lsr a
sta xpos
I made your main loop actually wait for the vblank to start (bit $2002, bpl). There are better ways to do this which I hope I'll remember to list in another post, but I have something to do like right now.

I changed your CLC, RORs to LSR A. LSR ignores the carry, so no need to CLC. LSR is identical to CLC, then ROR basically. (Both of these may not have had much to do with the problem, but when I didn't list everything and tried it, it didn't work.)
I also remembered some opcode that would act like clc+ror combo (so carry is not shifted into the accumulator), but i could not remember what that was, so i sticked with clc+ror
thanks for this one
I added this to the end of your NMI:

Code: Select all

lda #$80
sta $2000

lda #$00 
sta $2005
sta $2005
This sets the scroll every frame. It's good practice to do it every frame, even if it doesn't change. A bit $2002 before those $2005 writes would be the most safe, but you cannot do this because of it messes up the frame wait stuff I mentioned above. (Which is part of why there are other ways to do it that I can't cover right this minute)
is it really necessary to enable NMI at the end of every frame? or is this in order to reset scroll position? i am a bit confused on this one, maybe you can find some time to explain later.
Finally, I did this:

Code: Select all

lda $0204;vert pos of metasprite
clc
adc #$09
lsr a
lsr a
lsr a
sta ypos
The PPU draws sprites one pixel lower than the position you give it. (A y position of 0 will be drawn on scanline 1, etc.) So I added 1 here to compensate.
this is something i did not know, this is very useful info

Re: Need some help and advice with my collision detection

Posted: Sat Sep 13, 2014 8:10 am
by Kasumi
Kasumi wrote:There are better ways to do this which I hope I'll remember to list in another post, but I have something to do like right now.
That errand is now complete, so back to this. An easy to wait for vblank is this:

Code: Select all

main:;checks for collision
lda vblankoccurred;A new variable you must define
mainvblankwait:
cmp vblankoccurred
beq mainvblankwait
It loads the value of vblankoccurred in A before the loop, then compares that value to vblankoccurred. It counts on the NMI to change vblankoccurred during the loop. So in your NMI, you just need this somewhere to break that loop:

Code: Select all

inc vblankoccurred;whatever it was, it's now different so the loop will break
It's also good to do this because using bit $2002, bpl can occasionally miss frames.

With your main loop now running once per frame instead of constantly, you can move the code that animates your character to the main loop instead of the NMI. It doesn't matter much in this case, but it's good practice to have nothing in the NMI that doesn't absolutely need to be there. (Sprite DMA, tile updates that need to be done during gameplay need to be there. Music engine doesn't need to be but is cool to put there after the required stuff is done, just so music won't slow down if your main loop is running too many things.)

Already covered lsr/clc, ror. One other tiny note is that you occasionally compare with 0 when it isn't required.

Code: Select all

lda yadd
cmp #$00
beq rts_le
In that case you can remove the cmp and the program will work exactly the same, because lda will set the zero flag if what was loaded is equal to zero.

The last thing to mention is (indexed),y addressing which may help you avoid large cmp, branch trees when your game gets larger. It allows you to load from an address you can change, rather than one specified at compile time the way a label's is.

Code: Select all

lda #<level0;Low byte of this label's address
sta addr;Addr pointing to the first of two bytes of zero page RAM

lda #>level0;high byte of this label's address
sta addr+1;Addr+1 is the second of two bytes of zero page RAM

;With the label's address now in contiguous zero page RAM, the following statements will get the same value in A
lda level0,y
lda (addr),y
This is useful. Right now, you have your one screen broken up into 4 labels to access more than 256 bytes. Instead, you could use (indexed),y and change the location of the pointer to go further than 256 bytes under the label. (But you have to use y instead of x like you're doing now since indexed,x does something different.)

Code: Select all

lda ypos
lsr
lsr
lsr
tax
beq skipaddressfix
addressfixloop:
inc addr+1;(this adds 256 bytes to the pointer, so each time the ypos divided goes up one, the pointer will access the next 256 bytes of data)
dex
bne addressfixloop
skipaddressfix:
Doing this and using (indexed),y allows you avoid all the quadrupled up code underneath le1, le2, and le3. With le0 using indexed, it's all you'd need. And again, it doesn't matter much in this case, but once your game has more data having to evenly space labels would get tedious, so that's a good thing to learn about.
SNgamer wrote: is it really necessary to enable NMI at the end of every frame? or is this in order to reset scroll position?
Solely to set the scrolling nametable again, heh. It's a bit sloppy. A nicer way to do it is to keep the last value in $2000 in a mirror in RAM. Then you can perform bitwise logic to it.

Code: Select all

lda mirror2000
and #%11111100;The first nametable is ensured, nothing else is changed
sta $2000
sta mirror2000

Re: Need some help and advice with my collision detection

Posted: Sat Sep 13, 2014 9:20 am
by SNgamer
thank you for your tips, i very appreciate them. these will improve my programs heavily (as i now see that i have them coded in very untidy and inefficent way :oops: )

Re: Need some help and advice with my collision detection

Posted: Sun Sep 21, 2014 10:37 pm
by Prime
Search tokumaru threads he goes in depth about the subject within five seconds of his explanation i was aware how to implement it.
He's got the clearest writing on the subject, as a matter of fact he explains all subjects the clearest i've ever seen.Kasumi is a notable second they both go into deep detail as so the op can understand.

Re: Need some help and advice with my collision detection

Posted: Wed Sep 24, 2014 6:09 am
by SNgamer
Prime wrote:Search tokumaru threads he goes in depth about the subject within five seconds of his explanation i was aware how to implement it.
He's got the clearest writing on the subject, as a matter of fact he explains all subjects the clearest i've ever seen.Kasumi is a notable second they both go into deep detail as so the op can understand.
Do you mind supplying me with a link. This sound pretty helpful

Re: Need some help and advice with my collision detection

Posted: Sun Sep 28, 2014 4:23 am
by Prime
Absolutely SNgamer a couple links to the subject

viewtopic.php?f=2&t=352

viewtopic.php?f=10&t=9953

Re: Need some help and advice with my collision detection

Posted: Mon Sep 29, 2014 2:32 pm
by Celius
Wow, that first thread is embarrassing! You may be able to extract some helpful information out of that one, but man... it's painful to read. I can't believe that was me. I clearly had absolutely no sense of forum etiquette/grace/actually listening.

I know there have been some discussions of this topic recently, with fairly in-depth coverage. Here are some other examples:

viewtopic.php?f=2&t=9702
viewtopic.php?f=10&t=11423
viewtopic.php?f=10&t=11022

Hopefully these will help too.

Re: Need some help and advice with my collision detection

Posted: Mon Oct 13, 2014 3:22 am
by Tsutarja
I don't know how much help this will be, but in theory, this is the way I would program hit detection with solid objects:
-As an example, falling speed is 3 pixels per frame
-The game checks if the space 3 pixels under the player is free
-If yes, move there
-If not, check again 2 pixels away
-If that is free, move 2 pixels down
-If not check 1 pixel away
-If that is free, move there
-If not, vertical speed is 0
-You need to have the same hit detection with slight changes four times to cover all four directions

I don't know how complicated this would be in 6502, but I have used this kind of hit detection in my games that use different programming language.

Re: Need some help and advice with my collision detection

Posted: Mon Oct 13, 2014 3:02 pm
by tokumaru
Tsutarja wrote:I don't know how complicated this would be in 6502
Not complicated, just slow in case there are collisions. The NES is pretty slow, and any task that's performed in loops consumes clock cycles pretty fast, so you should avoid those as much as possible, specially if you can predict the end result sooner.

The faster approach is to test the desired movement, and if that isn't allowed, instead of going back pixel by pixel and testing again you can simply compute what the last allowed position is. The collision data should allow you to do that. If the blocks that build your world are 16x16 pixels large, and you just verified that a coordinate 4 pixels within this block hit a solid pixel, its a waste to test again all the way up if you know that the whole block is solid. Just place the object immediately above the block and you're good. This also works with slopes, because using the slope's collision data you can place the object at the topmost pixel.