It is currently Mon Jun 17, 2019 10:04 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Tue Jan 01, 2019 10:18 am 
Offline

Joined: Tue Dec 18, 2018 9:48 pm
Posts: 13
Hey everybody! A while back i came here asking for help to separate game logic from the NMI so i could try my hand on collision detection for a platformer, and now here i am asking for help to make said platformer.

The way i'm doing it is on my code there's a label which points to the name table used on my game like so:
Code:
nameTable:
   .incbin "mapa.nam"
   .byte $FF ; The nametable does not have byte $FF, just a way to know when
           ; it should stop reading data, think of it as a \0 on a C-string.

Then i start treating this as a 2 dimensional array, using the X and Y position of the player's sprite (which is on left-top most pixel, trying to replicate the SFML/SDL way of doing things here). Of course i can't just use X and Y for the indexing as that would go way outside of the boundaries from the name table, so i divide both by 8 (size of a single tile on screen).
Code:
LDA $0203
LSR A
LSR A
LSR A
TAX

LDA $0200
LSR A
LSR A
LSR A
TAY

Then to finally finish it all of i would do x + y * 32 (32 being the size of a whole line of tiles on one screen), since there's no multiplying instruction for the 6502, i had to improvise a little:

Code:

      LDA #<nameTable
      STA playerTileLow
      LDA #>nameTable
      STA playerTileHigh

LoopY:

      CLC
      LDA playerTileLow
      ADC #$20
      STA playerTileLow
      LDA playerTileHigh
      ADC #$00
      STA playerTileHigh

      DEY
      BNE LoopY
   
   LoopX:

      CLC
      LDA playerTileLow
      ADC #$01
      STA playerTileLow

      LDA playerTileHigh
      ADC #$00
      STA playerTileHigh

      DEX
      BNE LoopX

Then i would just proceed to see if there's a tile different to $#24(Nothing on spritesheet i am using) for every direction of each sprite.
Code:
;This is just for checking collision on the right side of the sprite, but it's basically the same for the remaining directions
         ;Also, X is 0 here
LDY #$02
   LDA (playerTileLow), y
   CMP #$24
   BEQ @didntColideRight
      STX canColideRight
      JMP @EndRight
   @didntColideRight:

   LDY #$22
   LDA (playerTileLow), y
   CMP #$24
   BEQ @didntColideRight2
      STX canColideRight
   @didntColideRight2:

For the most part it works, but then this some times happens when walking left or right towards a wall:
Image
I think it is because of the way i did the indexing (x+y*32), does somebody know a possible reason for this? Or maybe a better way to handle collision on the NES?

Edit
Movement code when moving right:
Code:
   LDA control
   AND #%00000001
   BEQ endRight
   
      LDA canColideRigt
      CMP #$01
      BEQ @dontNeedToCheckColision
         LDA $020b
         AND #%00000111 ; Stop only when right side of the sprite will hit something
         BEQ endRight
      @dontNeedToCheckColision:

      LDX #$00
      @LoopSprites:
         INC $0203, x

         INX
         INX
         INX
         INX
         CPX #$10
         BNE @LoopSprites
   endRight:


Last edited by Evaghetti on Tue Jan 01, 2019 9:50 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Tue Jan 01, 2019 8:28 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1287
Code:
LDA $0200
LSR A
LSR A
LSR A
TAY



Code:
LoopY:

      CLC
      LDA playerTileLow
      ADC #$20
      STA playerTileLow
      LDA playerTileHigh
      ADC #$00
      STA playerTileHigh

      DEY
      BNE LoopY

Suppose $0200 is a value from 0 to 7. What happens?

Code:
LDA $0203
LSR A
LSR A
LSR A
TAX

Code:
LoopX:

      CLC
      LDA playerTileLow
      ADC #$01
      STA playerTileLow

      LDA playerTileHigh
      ADC #$00
      STA playerTileHigh

      DEX
      BNE LoopX

Suppose $0203 is a value from 0 to 7. What happens?

Code:
LDY #$02
   LDA (playerTileLow), y
   CMP #$24
   BEQ @didntColideRight
      STX canColideRight
      JMP @EndRight
   @didntColideRight:

   LDY #$22
   LDA (playerTileLow), y
   CMP #$24
   BEQ @didntColideRight2
      STX canColideRight
   @didntColideRight2:

Is canColideRight ever changed anywhere else? Suppose it's non zero. This will make it zero (since you say X is zero when this runs.) If nothing makes it non zero again, it will just stay that way.

Edit: A tiny bit more help:

Code:
LoopX:

      TXA
      CLC
      ADC playerTileLow
      STA playerTileLow

      LDA playerTileHigh
      ADC #$00
      STA playerTileHigh

That will fix part of what I described (and is better because it doesn't loop), but still think about why yours had an error. (And this doesn't fix Y, so you still need to think about it anyway.)

You can totally multiply/divide by 2 in 6502, and 32 is a power of 2, so you can make your loop a little faster in most cases. (You multiply the value in Y by 32, then add the result to playerTileLow.) But let's get it working before making it faster.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Tue Jan 01, 2019 9:06 pm 
Offline

Joined: Tue Dec 18, 2018 9:48 pm
Posts: 13
Quote:
Is canColideRight ever changed anywhere else?

Only place that changes canColideRight is this subroutine.
Quote:
Suppose it's non zero. This will make it zero (since you say X is zero when this runs.) If nothing makes it non zero again, it will just stay that way.[

Before i do the checks for collision i set it to 1(true).
Code:
LDA #$01
STA canColideDown
STA canColideRight
STA canColideLeft

So it does reset back to a non-zero value(1) if there's no collision.
Quote:
Suppose $0200 is a value from 0 to 7. What happens?

Quote:
Suppose $0203 is a value from 0 to 7. What happens?

The result will be 0, then when it is 8 it will be 1 and when another number comes that can be divided by 8 it will be 2 and so forth, i noticed that, and as such is the reason i think the problem is the way i'm doing indexing, as it would only update the canColide variables when $0200 enters this criteria(since if the sprite goes a little down the collision starts works properly), but i don't know a way to do it better unfortunately :?
Quote:
Code:
LoopX:

      TXA
      CLC
      ADC playerTileLow
      STA playerTileLow

      LDA playerTileHigh
      ADC #$00
      STA playerTileHigh

That will fix part of what I described (and is better because it doesn't loop), but still think about why yours had an error. (And this doesn't fix Y, so you still need to think about it anyway.)

Oh that's neat, when i thought about the loop to add 32 Y times somehow i also thought that i needed to do the same for the X bit :lol:, thanks for the suggestion

Edit:
Oh yeah forgot to answer the part of the LoopY, the idea behind it was to add 32 (size of a line filled with tiles) Y times to the name table addres, since theres no MUL instruction or anything similar to do a "MUL $#32, y" or something among these lines i improvised that loop. It works as it should (or at least i think it is)


Last edited by Evaghetti on Tue Jan 01, 2019 9:13 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Tue Jan 01, 2019 9:12 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1287
No, the result will not be zero, that's the problem. Go through it step by step.

Edit: Bigger hint: Y is 0. dey. What happens?
Edit2: Stated a different way. Y is 0, dey. Does BNE branch? How long until that changes?
Edit3: I somehow think the problem is more how you're dealing with the collision/movement than this indexing, even if there are problems with the indexing. All the issues I can see would only cause issues when you're near the edges of a screen. (If your game scrolls, you can't just ldy #2 to get a tile two tiles to the right, since that will end up getting a tile from the beginning of the next row rather than the same row in a different nametable if you're near a screen boundary.)

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Tue Jan 01, 2019 9:31 pm 
Offline

Joined: Tue Dec 18, 2018 9:48 pm
Posts: 13
Did what you asked, the following is a screenshot from my emulator's debugger (didn't remove the LoopX yet, wasn't expecting for you to answer so fast :lol:)
Image
As you can see the on the bottom left side of the image $0200 and $0203 are $#02 (between 0 and 7), done the LSR they the result becomes 0 (Look at the top-right side of the screen, where it's possible to see the values from all the registers, y isn't 0 as i did not step after the TAY instruction, but the result is on A), if i change the value of both to 8, then the following happens
Image
As you can see now that the number is divisible by 8 the result becomes 1.
($0200 is $#09 as i stepped too much with the debug, but still it is giving the expected behavior that i described)

Quick edit:
My god man i love how quick you reply this thread but my skills with english don't let me respond accordingly, sorry if i said something you already pointed out :(


Top
 Profile  
 
PostPosted: Tue Jan 01, 2019 9:37 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1287
I posted two pieces of related code for X and Y. Only one was the division.

X being zero should mean zero is added to playerTileLow/playerTileHigh. Y being zero should mean zero is added to playerTileLow/playerTileHigh. Therefore if $0203 and $0200 are both zero, playerTileLow/playerTileHigh should be equal to <nameTable/>nametable. That is not true with your current code. But I'll just give the answer after one more attempt, I guess!

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Tue Jan 01, 2019 9:42 pm 
Offline

Joined: Tue Dec 18, 2018 9:48 pm
Posts: 13
Quote:
Edit: Bigger hint: Y is 0. dey. What happens?

Ok, got it a underflow happens y goes to 255 and then everything goes downhill from there. Though it sure is a bug i don't think it is related with the problem of going through blocks :?

Quote:
Edit3: I somehow think the problem is more how you're dealing with the collision/movement than this indexing, even if there are problems with the indexing. All the issues I can see would only cause issues when you're near the edges of a screen. (If your game scrolls, you can't just ldy #2 to get a tile two tiles to the right, since that will end up getting a tile from the beginning of the next row rather than the same row in a different nametable if you're near a screen boundary.)

No scrolling yet, was going to try my hand at that when i made the collisions work and i made and edit on the post on how i handle the collision, but basically it's just a jump if canColideRight or any other direction is set to 0, otherwise just move accordingly.


Top
 Profile  
 
PostPosted: Tue Jan 01, 2019 9:53 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1287
Right, overflow is part of the problem. One restructuring is...

Code:
LDA $0200
LSR A
LSR A
LSR A
BEQ AfterY;If zero, add nothing
TAY
LoopY:
      CLC
      LDA playerTileLow
      ADC #$20
      STA playerTileLow
      LDA playerTileHigh
      ADC #$00
      STA playerTileHigh

      DEY
      BNE LoopY
AfterY:


And yes, it doesn't seem related to going through blocks, so it seems like you have a problem beyond the indexing. So. What's that code look like? What routines work with canCollideRight/etc after this routine runs?

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Tue Jan 01, 2019 9:59 pm 
Offline

Joined: Tue Dec 18, 2018 9:48 pm
Posts: 13
Kasumi wrote:
Right, overflow is part of the problem. One restructuring is...

Code:
LDA $0200
LSR A
LSR A
LSR A
BEQ AfterY;If zero, add nothing
TAY
LoopY:
      CLC
      LDA playerTileLow
      ADC #$20
      STA playerTileLow
      LDA playerTileHigh
      ADC #$00
      STA playerTileHigh

      DEY
      BNE LoopY
AfterY:


And yes, it doesn't seem related to going through blocks, so it seems like you have a problem beyond the indexing. So. What's that code look like? What routines work with canCollideRight/etc after this routine runs?

Made an edit on the original post on this thread how i'm doing to walk right, it's basically the same for the other directions too, though i think with all the headache my way of handling collision is giving it's more worth it starting another method from scratch, either way since you are so eager to help me i'll keep insisting on this one :D

Quote:
Edit
Movement code when moving right:
Code:
   LDA control
   AND #%00000001
   BEQ endRight
   
      LDA canColideRigt
      CMP #$01
      BEQ @dontNeedToCheckColision
         LDA $020b
         AND #%00000111 ; Stop only when right side of the sprite will hit something
         BEQ endRight
      @dontNeedToCheckColision:

      LDX #$00
      @LoopSprites:
         INC $0203, x

         INX
         INX
         INX
         INX
         CPX #$10
         BNE @LoopSprites
   endRight:


Top
 Profile  
 
PostPosted: Tue Jan 01, 2019 10:27 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1287
If it only happens when you approach the right side of the screen, it is because of the thing I mentioned earlier. (ldy #2 will give you tiles from the start of the next row rather than two to the right of the current row once you get close to the edge.)

Checking your previous topic, make sure you do that stack stuff in your NMI dougeff mentioned: viewtopic.php?p=230741#p230741 Otherwise all kinds of random things can happen if the NMI occurs during your game loop.

If it's not either of those (and it probably isn't either of those), I can't see anything that would cause that issue from what's posted here. Willing to post a ROM? As far as other ejection methods, I can think of some things, but they're require largish changes to how you're doing things so I have to think about how to structure that information.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Tue Jan 01, 2019 10:49 pm 
Offline

Joined: Tue Dec 18, 2018 9:48 pm
Posts: 13
Kasumi wrote:
If it only happens when you approach the right side of the screen, it is because of the thing I mentioned earlier. (ldy #2 will give you tiles from the start of the next row rather than two to the right of the current row once you get close to the edge.)

This problem of getting unwanted tiles is something that i am now aware, but for now i would prefer to focus on fixing the problem with collision showed on the screenshot where half the sprite is inside the block, my theory was because the division by 8 would only take the correct tile when the head of Mario was in a y position dividable by 8, now though i don't really know,

Kasumi wrote:
Checking your previous topic, make sure you do that stack stuff in your NMI dougeff mentioned: viewtopic.php?p=230741#p230741 Otherwise all kinds of random things can happen if the NMI occurs during your game loop.

Already doing that, really glad to him for remembering me that the stack exists :D

Kasumi wrote:
If it's not either of those (and it probably isn't either of those), I can't see anything that would cause that issue from what's posted here. Willing to post a ROM? As far as other ejection methods, I can think of some things, but they're require largish changes to how you're doing things so I have to think about how to structure that information.

Sure! No problem there, i could even upload the full source if wanted, though up to this point i have been translating my code on this thread to make it easier to understand since i speak Portuguese, but i can put some comments if you wanted. Also i'm all ears to how do a better collision detection, as normally i would do a AABB to check collision with the map, but since that's not really an option with the NES i tried doing using the nametable data.


Top
 Profile  
 
PostPosted: Tue Jan 01, 2019 11:04 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1287
For NES, I think it's easier to move something into a wall, then move it out if necessary. Rather than check if the destination is in a wall before moving. But the large changes I mean is that you're currently working directly with the page for RAM used for sprites, rather than separate variables. (So you're incing the position across multiple sprites, when two would have the same position, and stuff like that.) To state the basics of it, having a separate variable for the x and y position, then copying it to the sprites makes a lot of things easier later. (Sprite flickering at the very least, and scrolling.)

But I get that you want to focus on the current issue. I need more info, because I see no cause of the problem in the screenshot from what has been posted. I might be missing something and then someone else can post about it to find it, but I think it's something not posted. You don't really need to worry about comments, I was just gonna debug the ROM. (Which would have no comments.)

Edit: I promise I did initially think the other things I mentioned might cause this problem, since you could end up sliding up near the right of the screen due to the overflow. But if all that's fixed, yeah, there's more to do.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Tue Jan 01, 2019 11:23 pm 
Offline

Joined: Tue Dec 18, 2018 9:48 pm
Posts: 13
Kasumi wrote:
For NES, I think it's easier to move something into a wall, then move it out if necessary. Rather than check if the destination is in a wall before moving


To be honest when i started going down this rabbit hole i thought about doing just that, but also thought it would give some kind of shaking effect on the sprite and so scraped the idea, guess i should have tried it out before moving on to another method.

Kasumi wrote:
But the large changes I mean is that you're currently working directly with the page for RAM used for sprites, rather than separate variables. (So you're incing the position across multiple sprites, when two would have the same position, and stuff like that.) To state the basics of it, having a separate variable for the x and y position, then copying it to the sprites makes a lot of things easier later. (Sprite flickering at the very least, and scrolling.)

Yeah, thought about this stuff of putting positions separated from the RAM when i began thinking how to implement a pause menu, but since this ROM is just to mess around with the Nerdy Nights tutorials and some ideas of mine i didn't really bother to do the way it should be done. Also i have some doubts about sprite flickering but this is a question for another thread i am afraid.

Kasumi wrote:
But I get that you want to focus on the current issue. I need more info, because I see no cause of the problem in the screenshot from what has been posted. I might be missing something and then someone else can post about it to find it, but I think it's something not posted. You don't really need to worry about comments, I was just gonna debug the ROM. (Which would have no comments.)


Ok then, here is the link for the ROM and de source code, in case you prefer looking at it than the disassembly
https://drive.google.com/open?id=1kpcGMYCsf4_ImsA_NvoUdPgIn3CWPDfQ

Edit:
Kasumi wrote:
Edit: I promise I did initially think the other things I mentioned might cause this problem, since you could end up sliding up near the right of the screen due to the overflow. But if all that's fixed, yeah, there's more to do.

Oh no problem, i actually learned a lot with what you pointed out, as i already did see the bug when the sprites were as 0,0 but never really did think about a underflow on the LoopY, so thanks for the help either way!


Top
 Profile  
 
PostPosted: Wed Jan 02, 2019 1:00 am 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1287
Well. Okay. Technically it was possible to make a solid guess at the bug with what was posted before the source. But woah. I'm not embarrassed I didn't find it.

Here is a picture of your game with an 8x8 grid:

Image
Which may make the problem slightly more obvious? Mario's head is in row 15. That's not a collision tile. Cool! Well, one row below that is also not a collision tile. Cool! We can walk.

Row 15 is empty (except the far away mushroom stem on the right). Row 16 is also empty (except for the far away mushroom stem on the right). So until Mario's head dips into Row 16, he can freely walk halfway through those blocks.

Your code checks one row, and the row below it. But Mario can occupy THREE rows of tiles. Fixing it is not quite as easy as just checking a third column below the second because that will make him land too soon. You have to make sure you're checking the tile his feet are actually in. (Which is not always the tile below his head)

Edit: (One thing to note. If you only always check two tiles at his head and feet, it will be possible for a single 8x8 tile to pass through Mario's middle.)

I'm kinda too spent to recommend a good code fix for you at the moment. Edit: But the simplest way is just do the playerTileLow/playerTileHigh math (the multiply by 32 and everything) for every point you want to check, rather than doing it once and trying to offset with y from this. It is possible to do it purely with offsets, but it's a bit tougher.

Hopefully the why helps a little for now.

edit3: Note that this also applies for Y since Mario can also occupy three columns.

edit2: Responding to other stuff:

Evaghetti wrote:
To be honest when i started going down this rabbit hole i thought about doing just that, but also thought it would give some kind of shaking effect on the sprite and so scraped the idea, guess i should have tried it out before moving on to another method.

There won't be shaking if it's done correctly. Move player. If needed, move player out of wall. Draw player. So the player will never see the character in the wall. They'll just look pressed up against it.

Quote:
Also i have some doubts about sprite flickering but this is a question for another thread i am afraid.

It's reasonably simple. Like ten lines of code to make the flicker happen on a basic level. But it requires changes to what you've got too, since like... $0203 won't always be Mario's Y position anymore. If you follow a process, though, by first making the program work with variables copied to the hardcoded sprite RAM (instead of using the hardcoded sprite RAM as variables). Then it's easy to make the sprite RAM not hardcoded which gives you flicker in short order.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Wed Jan 02, 2019 1:44 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11369
Location: Rio de Janeiro - Brazil
Kasumi wrote:
But the simplest way is just do the playerTileLow/playerTileHigh math (the multiply by 32 and everything) for every point you want to check, rather than doing it once and trying to offset with y from this.

Yeah, that's how I normally do it. Doing collisions all in tile space is troublesome because of the different ways in which the sprites may align to the tile grid. Defining each exact point you need to test for solidity in pixel space before converting them to tile space will also make it easier to implement finer collision detection in the future, so you'll be able to implement things like slopes more easily, if you decide to.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group