It is currently Mon Oct 23, 2017 6:44 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 9 posts ] 
Author Message
PostPosted: Sat Feb 11, 2017 8:22 am 
Offline

Joined: Tue Nov 20, 2012 1:59 pm
Posts: 65
Hi, again! lol I'm sorry to be making so many threads, but here I am again with another problem. I have a rule of I'll try to figure something out and if I can't after a couple of days I'll post a help thread. So here I am with about 4 days (about an hour per day) of trying to update a collision buffer correctly during screen scrolling. My idea is, once the screen scroll hits a multiple of 16 (which are the dimensions of the tiles) I need to update the collison buffer. Easy? Well, it doesn't seem to work the way I want it to. The collision seems to aways be a tile or two off. I've tried multiple instances, but this seems to be the correct way if I'm right about the idea. Something just isn't right though. By the way, it's just a simple two-screen setup.

Here's my code:

The camera move code:
Code:
CameraMovement:
   LDA buttons
   AND #%00000001
   BEQ @ck_right
   LDA Player_X         ;only scroll the screen if the player is at a fixed
   CMP #$90         ; camera anchor
   BCC @ck_right
   LDA CameraX         ;make sure that the add wont go over boundries
   CLC
   ADC #$01
   CMP #$FF
   BCS @r1
@move_camera
   LDA CameraX         ;scroll the camera
   CLC
   ADC #$01         ;note: change speed later
   STA CameraX
   JSR UpdateCollisionBuffer   ;update the collision buffer
   LDA Player_X         ;make the player not move faster than the scroll rate
   SEC
   SBC #$02
   STA Player_X
@r1
   RTS

@ck_right
   LDA buttons
   AND #%00000010
   BEQ @r
   LDA Player_X         ;only scroll the screen if the player is at fixed achor
   CMP #$60
   BCS @r
   LDA CameraX         ;make sure that the subtract wont go over boundries
   SEC
   SBC #$01
   BCC @r
   LDA CameraX         ;scroll the camera
   SEC
   SBC #$01
   STA CameraX
   JSR UpdateCollisionBuffer   ;update the collision buffer
   LDA Player_X         ;make the player not move faster than the scroll rate
   CLC
   ADC #$02
   STA Player_X
@r
   RTS


The parts that update the collision buffer:
Code:
UpdateCollisionBuffer:
   LDA CameraX
             ;xxxxxxxx
   AND #$0F         ;every time a new 16x16 tile is scrolled on screen
   BEQ @getNewData         ; we update the collision buffer
   RTS
@getNewData
   JSR RefreshCollisionTable
   RTS

RefreshCollisionTable:
   LDA CameraX         ;get absolute
   AND #$10         ;only take the multiple of 16
   LSR A            ; shift into low nybble
   LSR A            ; as index to coll. data
   LSR A
   LSR A
   STA Scratch         ;index into scratch
   STA Temp         ; monitor ram
   LDA LevelDataPtr
   PHA
   LDA LevelDataPtr+1      ;preserve the pointer to the level data
   PHA            ; because were fixin to mess it up

   LDA LevelDataPtr
   CLC
   ADC Scratch         ;add the collison index we resolved earlier
   STA LevelDataPtr
   LDA LevelDataPtr+1
   ADC #$00
   STA LevelDataPtr+1

   TXA
   PHA

   LDY #$00         ;copy the collision buffer from top to bottom
@coll_loop
   LDA (LevelDataPtr), y      ; using the index as a start point
   TAX
   LDA TileCollisionBytes,x
   STA CollisionBuffer,y
   INY
   CPY #$F0
   BNE @coll_loop

   PLA
   TAX
   PLA
   STA LevelDataPtr+1
   PLA
   STA LevelDataPtr
   RTS


Top
 Profile  
 
PostPosted: Tue Feb 14, 2017 10:45 am 
Offline

Joined: Tue Nov 20, 2012 1:59 pm
Posts: 65
Alright so after some time I can see why this doesn't work. Can someone rather than having to go through my code, give me an example of how this works? My idea is once the scroll reaches a multiple of tile size update this buffer. I'm having trouble wrapping my head around how to locate the right data and where to put it.


Top
 Profile  
 
PostPosted: Tue Feb 14, 2017 1:26 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1787
Location: DIGDUG
Are you updating the entire buffer, or just a little bit?

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Tue Feb 14, 2017 1:34 pm 
Online
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10067
Location: Rio de Janeiro - Brazil
It'd also help if you explained how you set up things, since there's no standard for this kind of thing. A lot of people don't even use collision maps, and instead get their collision information straight from the level map.

Anyway, what are the dimensions of your vision buffer? What information does it contain? How is it stored in RAM? How is it stored in ROM? How does your camera system work? Do you use 8 or 16 bit coordinates?


Top
 Profile  
 
PostPosted: Tue Feb 14, 2017 5:51 pm 
Offline

Joined: Tue Nov 20, 2012 1:59 pm
Posts: 65
I'd like to update the entire buffer. I don't have a map for collision. I have a table setup for each tile. 0 for empty, 1 for solid. That's where the TileCollisionBytes label in the earlier posted code are. I have the idea that I only need xF0 bytes as the length of the buffer, one for each tile. It's stored in RAM one tile right after the other. I explained earlier how it's read from ROM. The whole camera system code is posted above in the first post. Like I said, I'm just looking for a way to understand this and maybe just need some direction other than trying to debug and fit tie the code I've already written? There is no nametable swapping. At the beginning of the level both nametables are copied and left alone. The camera starts out at x=0, y=0 at the beginning and x is increased once the player reaches a certain point. I think that's all..

Thanks, zkip.


Top
 Profile  
 
PostPosted: Tue Feb 14, 2017 7:06 pm 
Online
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10067
Location: Rio de Janeiro - Brazil
So your collision buffer is 240 bytes, 1 byte per 16x16-pixel metatile, right? The rest of my post is based on this assumption.

I noticed a few things wrong with your code. The first thing I noticed is that you load new data when the camera is in a position that's a multiple of 16. One problem with this is that you can't scroll at speeds larger than 1 pixel per frame, or you'll miss updates. The second problem is that if the camera stops at a position that's a multiple of 16, the same data will be loaded every frame, wasting CPU time. To avoid these problems, the correct way to detect a metatile boundary crossing is to compare the previous and the current values of CameraX, looking for changes in bit 4:

Code:
   lda CameraX
   eor OldCameraX
   and #%00010000
   bne @getNewData

All you need for this to work is to copy CameraX to OldCameraX before modifying it. This way you can scroll up to 16 pixels per frame without problems, and the CPU will only spend time loading new data once.

Here's another possible problem:

Code:
   LDA CameraX         ;get absolute
   AND #$10         ;only take the multiple of 16

Shouldn't you be ANDing with $f0, to keep the 4 highest bits of the camera? ANDing with $10 only preserves bit 4, so the index you create from this will only ever be 0 or 1, which I believe is not what you want. To tell the truth, you don't even need an AND here at all, since the lower bits will be discarded anyway due to the 4 LSRs. Also note that since CameraX is only 8 bits, you can't fully scroll into the second screen. Since the highest possible value for CameraX is $FF (1 pixel short of entering the second screen), the farthest you can start from is the last column of the first screen, meaning you'll never reach the last column of the second screen. Fixing this would require extending CameraX to 16-bits, so the index is created like this:

Code:
   lda CameraX+1
   lsr
   lda CameraX+0
   ror
   lsr
   lsr
   lsr

This uses only the lowest bit of CameraX+1, supporting levels up to 512 pixels wide. For longer levels you'll need to take more bits from CameraX+1.

Now, the biggest problem is probably the actual data transfer. You can't just transfer 240 contiguous bytes, since the level is longer than 16 metatiles. The buffer is 16 metatiles wide, so when the index goes from 15 to 16, that's an automatic wrap to the next row, but since the map in the ROM is longer, this index will just keep reading from the same row, wrapping at the wrong position, and causing your buffer to be filled with misaligned garbage. What you need to do is adjust the pointer (LevelDataPtr) after each run of 16 bytes copied, so the data is correctly copied from the row below.

Since your maps are apparently 32 metatiles long, you can get away with adding 16 to LevelDataPtr after every 16 bytes copied, to force a wrap to the next row, while still using the same unmodified index for the destination. This is a quick hack for when the length of the level is constant, but if you ever switch to levels of arbitrary lengths you will need a more versatile solution. This is one way to implement the hack:

Code:
   iny
   tya
   and #$0F
   bne @test_end
   clc
   lda LevelDataPtr+0
   adc #$10
   sta LevelDataPtr+0
   lda LevelDataPtr+1
   adc #$00
   sta LevelDataPtr+1
@test_end
   cpy #$F0
   bne @coll_loop


EDIT: I edited the post a few times. Please be sure you didn't miss anything.


Top
 Profile  
 
PostPosted: Wed Feb 15, 2017 9:53 am 
Offline

Joined: Tue Nov 20, 2012 1:59 pm
Posts: 65
Ah! I see, so it's essentially done just like how I was loading the nametables. Thanks for explaining that. However, I still have one problem. It seems to only work when the screen is perfectly aligned to a 16x16 tile. Does the player X or the camera anchor (how far the player goes before scrolling starts) need to be sorted into this index? It doesn't seem like it should. Also, is it proper how I decrease the players x in the code above to stop from going farther than the speed of the scroll?

Thanks.


Top
 Profile  
 
PostPosted: Wed Feb 15, 2017 10:37 am 
Online
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10067
Location: Rio de Janeiro - Brazil
Just in case there are still any doubts about the alignment issue, I made a couple of pictures to illustrate the problem. Here's what happen when you copy contiguous bytes from a 32-metatile wide map to a 16-metatile wide buffer:

Attachment:
alignment-00.png
alignment-00.png [ 11.21 KiB | Viewed 743 times ]

The layouts don't match, so you end up with useless data.

To properly advance to the next row of the level map at the same time the index advances to the next row of the buffer, you have to skip the MapWidth - BufferWidth (in this case, 32 - 16 = 16) blocks:

Attachment:
alignment-01.png
alignment-01.png [ 11.05 KiB | Viewed 743 times ]

Notice how the data we're reading from the map is now properly aligned with the buffer. Like I said in the previous post, a quick way to do this is add the amount of metatiles to skip to the pointer you're using to read the map data.

zkip wrote:
Ah! I see, so it's essentially done just like how I was loading the nametables.

I don't know how you're loading name tables, but I imagine that if you're drawing a 16-metatile wide screen from a 32-metatile wide map you also need to skip 16 extra metatiles to advance to the correct point of the next row.

Quote:
It seems to only work when the screen is perfectly aligned to a 16x16 tile.

What exactly doesn't work? Is the collision buggy? Have you checked if that's due to the buffer having the wrong contents or due to the collision code not reading from the collision buffer properly? You can use an emulator that lets you inspect memory in real time (FCEUX, Mesen) to see if the collision buffer is being updated properly. If you can get the window to display 16 bytes per line you should easily eyeball the 16x15 map to see if it looks correct as you scroll.

Quote:
Does the player X or the camera anchor (how far the player goes before scrolling starts) need to be sorted into this index? It doesn't seem like it should. Also, is it proper how I decrease the players x in the code above to stop from going farther than the speed of the scroll?

The player position shouldn't affect how the collision data is loaded, since the loading is based on the position of the camera. The position of the player will affect how you test collisions against the buffer though. Since you're loading an entire screen worth of collision data, I assume you're keeping player coordinates as 8-bit coordinates relative to the screen (something I personally would never recommend, but let's keep going), but it seems you're not accounting for the fine scroll when using the player coordinate to read from the collision map.

Think about this: if the player is at screen position 9, but the screen has scrolled 10 pixels to the right, that means that 10 pixels of the first column of metatiles are off the left of the screen, and only the rightmost 6 pixels remain visible. If you simply take the player's position and divide by 16 to calculate the collision column, you'll get index 0, but position 9 is already past the 6 visible pixels of the first column, so position 9 is actually overlapping the SECOND column. To fix that, you have to take the fine scroll (CameraX AND $0F) into consideration when reading from the map. If the fine scroll is 10 pixels, you have to add that to player's coordinates before dividing by 16, so 9 + 10 = 19, which divided by 16 will give you the correct index of 1, so you can read from the second column.

Do you think this could be your problem?


Top
 Profile  
 
PostPosted: Wed Feb 15, 2017 1:06 pm 
Offline

Joined: Tue Nov 20, 2012 1:59 pm
Posts: 65
Yeah, the collision map is right in RAM, that's why I went to the players coordinates as being the culprit.

You're exactly right, I wasn't considering the scroll in the collision checking code. Setting that up (camerax and #$0f) made it better, but it still seems a pixel or two off. At this point, I'm not sure where I went wrong or what to even ask here. Ever had a problem you can't think of a question for?

edit: The problem is fixed. The problem was I forgot to add the fine scroll to the code that ejects the player from a tile. Thanks. :D


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

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