It is currently Thu Jul 18, 2019 7:27 pm

All times are UTC - 7 hours



Forum rules





Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Diagonal scrolling help
PostPosted: Sun Apr 21, 2019 5:41 pm 
Offline
User avatar

Joined: Fri Feb 27, 2009 2:35 pm
Posts: 360
Location: Fort Wayne, Indiana
I've been developing for SNES and I want to have levels that are two screens tall, except to save video RAM for tiles instead, I'm using 64x32 tilemaps, but I'm running into problems with some occasional wrong tiles on the top half of the level, corresponding to the right tiles for the bottom half in the same position. I think aside from that it works.

Image

Since this is my first time doing diagonal scrolling it's hard to tell if my implementation is wrong, or if the logic I'm trying to implement in the first place is wrong (so any implementation of that logic would be flawed) so I figured getting more eyes on my code would help.

I have a ColumnUpdateBuffer that gets DMA'd to ColumnUpdateAddress vertically, a full 32 tiles tall. I also have a RowUpdateBuffer that similarly gets DMA'd to RowUpdateAddress horizontally and it's always 64 tiles wide for simplicity, and I only write to the portion of it that will be visible. My level buffer is in LevelBuf and it is 256 wide * 32 tall * 2 bytes per metatile, arranged in a series of columns from left to right.

When the scrolling has changed: (simplified)

If the X scroll value passes a tile boundary:
  • If scrolling left, select a column 2 tiles left from the new scroll position
  • If scrolling right, select a column 34 tiles right from the new scroll position
  • (Selected column number will be called SelectedColumn)
  • Set ColumnUpdateAddress to tilemap base address + SelectedColumn AND 31, use second screen if needed
  • Prepare a pointer to start reading from level data (Level column is SelectedColumn / 2)
  • -----
  • Y = Y scroll position counted in metatiles
  • X = Y, restricted to fit ColumnUpdateBuffer
  • ColumnUpdateBuffer[X++] = Tile numbers[Level pointer[Y++]]
  • Repeat the previous step 15 more times, wrapping X around in the buffer.

If the Y scroll value passes a tile boundary:
  • If scrolling up, select a row 1 tiles up from the new scroll position
  • If scrolling down, select a row 29 tiles down from the new scroll position
  • (Selected row number will be called SelectedRow)
  • Set RowUpdateAddress to tilemap base address + SelectedRow * 32
  • Prepare a pointer to start reading from level data (Level column is X scroll position in metatiles, minus 1)
  • -----
  • Y = SelectedRow / 2
  • X = X scroll position in metatiles * 2, minus 1, constrained to the buffer size
  • RowUpdateBuffer[X++] = Tile numbers[Level pointer[Y]]
  • Move Level pointer ahead one column
  • Repeat previous two steps 20 times (probably more than needed. I was trying to make it slightly wider than the screen, hence starting a bit to the left of it)

Or probably a lot more clearly, the code itself:
scrolling.s
renderlevel.s


Attachments:
nova-the-squirrel-2.zip [14.11 KiB]
Downloaded 87 times
Top
 Profile  
 
PostPosted: Sun Apr 21, 2019 5:52 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21507
Location: NE Indiana, USA (NTSC)
"Y = Y scroll position counted in metatiles"
This needs to be based on the previous frame's Y scroll position, in case you cross a tile boundary in both directions at once.

_________________
Pin Eight | Twitter | GitHub | Patreon


Top
 Profile  
 
PostPosted: Sun Apr 21, 2019 6:11 pm 
Offline
User avatar

Joined: Fri Feb 27, 2009 2:35 pm
Posts: 360
Location: Fort Wayne, Indiana
Yeah I thought about problems from both the row and column updating at once. Changing that doesn't seem to fix it, and it's hard to tell exactly what's failing. I probably need to start over on the scrolling code but I don't really have correct guidelines to work from.


Top
 Profile  
 
PostPosted: Sun Apr 21, 2019 6:31 pm 
Online
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11375
Location: Rio de Janeiro - Brazil
Garbage tiles in diagonal scrolling often happen because of mismatched vertical and horizontal tile map updates in the same frame.

If you generate the new tile data for movement in one axis immediately after moving in that axis but before moving in the other axis, consider that that data will be off by one if the movent in the other axis also happens to trigger an update. If the updates are carried out in the same order they were prepared, things are probably gonna be fine, if not, you may end up with old data overwriting new data.

I don't know in which order you're doing everything, so what you need to do is simulate or debug the case when both axis need updating, and see if there are any wholes in your logic that could be causing old data to overwrite new data or leave certain spots with no updates at all.


Top
 Profile  
 
PostPosted: Sun Apr 21, 2019 6:49 pm 
Offline
User avatar

Joined: Fri Feb 27, 2009 2:35 pm
Posts: 360
Location: Fort Wayne, Indiana
I actually move both axes at once, and then after that, check for and handle any updating. You think it's better to move an axis, prepare an update, then move the other axis and prepare that update, then do the two DMAs in the same axis order used here?


Top
 Profile  
 
PostPosted: Sun Apr 21, 2019 6:54 pm 
Offline

Joined: Wed May 19, 2010 6:12 pm
Posts: 2880
Are you allowing 2 tiles on both sides so you can havr hdma effects?


Top
 Profile  
 
PostPosted: Sun Apr 21, 2019 7:13 pm 
Offline
User avatar

Joined: Fri Feb 27, 2009 2:35 pm
Posts: 360
Location: Fort Wayne, Indiana
psycopathicteen wrote:
Are you allowing 2 tiles on both sides so you can havr hdma effects?

I haven't thought that far ahead. I have 2 because my previous game extended far further than 2 due to handling scrolling updates in 32 pixel wide chunks, and I was just being a little safe even while shortening it.

Looks like simply making X scroll changes happen and be handled before Y scroll changes doesn't help. I think I might actually be messing up the part that fetches from the level (or sets up the pointer) since it's fetching from the bottom rather than just being some other kind of garbage. Something tricky here is I don't know if it's the row updater or the column updater that's flawed.


Top
 Profile  
 
PostPosted: Sun Apr 21, 2019 10:02 pm 
Online
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11375
Location: Rio de Janeiro - Brazil
NovaSquirrel wrote:
You think it's better to move an axis, prepare an update, then move the other axis and prepare that update, then do the two DMAs in the same axis order used here?

I'm not sure, it's been a while since I coded an 8-way scrolling engine and I don't have any source code with me right now that I can check... but I distinctly remember having garbage tiles near the corners of the screen when scrolling diagonally and that having to do with the exact order in which horizontal and vertical updates were handled in the same frame.


Top
 Profile  
 
PostPosted: Sun Apr 21, 2019 11:05 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4154
Location: A world gone mad
I truly wish I could help here, but I'm having an extremely difficult time understanding any of what's being discussed. I clearly see "the problem" demonstrated in the screenshot, but I honestly do not understand how you'd end up with said problem unless your tilemap update data was incorrect based on several conditions (i.e. your code). Everything looks fine except for those few tiles; still screenshots don't help, sadly, but I'm not sure an animated GIF would either.

I would suggest stepping through your code in the bsnes-plus or Mesen-S debugger, and see what ends up in PPU RAM right after your DMA completes (since both emulators will let you examine system RAM in addition to PPU RAM). I assume you aren't running out of NMI time, either.

I've done diagonal scrolling before -- you just increment/decrement a 16-bit variable and write it to $210D-2114 as needed -- but this was simply panning backgrounds with a 32x32 layout (i.e. single-screen). I haven't done it with 64x32 or 32x64 or 64x64. But 64x32 may require you -- on vertical scrolls -- to update a row which shouldn't be visible (otherwise the player would see the update) -- most 64x32 games I see use a status bar of some kind to cover this, I believe. That said: if you have something that's two screens tall, why are you using 64x32 rather than 32x64?

As you've learned, the SNES is not like the NES at all -- there really isn't a "you need to do something with the A registers before the B registers" for screen panning. All of that NES-level nonsense is for the most part irrelevant.


Top
 Profile  
 
PostPosted: Mon Apr 22, 2019 5:13 am 
Offline
User avatar

Joined: Fri Feb 27, 2009 2:35 pm
Posts: 360
Location: Fort Wayne, Indiana
Yeah I get the impression this is something I'll have to figure out for myself and that it's probably a problem with my implementation somewhere.

I used 64x32 instead of 32x64 to avoid needing to black out the leftmost 8 pixels, though if it's good enough for Kirby Super Star then perhaps it's good enough for me, at least to have something 100% working for now.

Longer term for actually fixing this I feel like it'd be a good idea to go and thoroughly test the row/column updating code and see if I can give it fixed values that result in a problem every frame, and then from there both know exactly what kinds of values are breaking and have it in a state where I can step in and debug. Or, if necessary, just try and rewrite all the scrolling code fresh, being careful and testing a lot along the way. It's not necessarily a lot of work wasted.


Top
 Profile  
 
PostPosted: Mon Apr 22, 2019 7:48 am 
Offline

Joined: Wed May 19, 2010 6:12 pm
Posts: 2880
Maybe you're not updating the corner tiles.


Top
 Profile  
 
PostPosted: Mon Apr 22, 2019 2:10 pm 
Online
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11375
Location: Rio de Janeiro - Brazil
psycopathicteen wrote:
Maybe you're not updating the corner tiles.

He did say that he's updating the whole 32 or 64 tiles, so I don't think he's missing any corners. It's true that sometimes people forget that they need to update areas wider/taller than the screen, because of the fine scroll.


Top
 Profile  
 
PostPosted: Mon Apr 22, 2019 3:05 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4154
Location: A world gone mad
And in the case of the SNES, there is no "fine scroll".


Top
 Profile  
 
PostPosted: Mon Apr 22, 2019 3:24 pm 
Offline

Joined: Fri Feb 24, 2012 12:09 pm
Posts: 938
The screenshot looks as if only half of the 16bit map entry was written okay.
Ie. wrong tile number, but (seemingly) correct palette... or completely unchanged palette if it's same as old color?

For general testing: Maybe it helps if you remove the horizontal or vertical updating (to see which one is buggy).
And perhaps initially fill the whole screen area with known tiles/colors (to see what does or doesn't get updated later on).
Don't forget debuggers with vram viewers - that might also help so see what happens after each DMA.

koitsu wrote:
And in the case of the SNES, there is no "fine scroll".
I think "fine scroll" was referring to scroll amounts less than 8 pixels (the SNES does fortunately support that) (and it can result in needing 33 tiles per line, instead of 32 tiles in unscrolled pictures).


Top
 Profile  
 
PostPosted: Mon Apr 22, 2019 6:47 pm 
Offline

Joined: Wed May 19, 2010 6:12 pm
Posts: 2880
I looked through the source code and played with the demo and I think there is a missing corner tile. I think lowering the top loading seam would do the trick.


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: Google [Bot] and 7 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