How are you guys handling updating the tilemap? (scrolling)

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
How are you guys handling updating the tilemap? (scrolling)
by on (#176358)
Yeah, I've been running into all types of little issues about how exactly to do this that have been annoying me. My tilemap format in rom is just a regular tilemap with no type of compression, just instead of being multiple 32x32 tilemaps put together, it's just one giant one, so a row can be whatever width. For updating the tilemap horizontally, (columns) I have a buffer that I fill out that basically turns the tilemap sideways, but the buffer is only a couple of tiles wide (like 4x64) instead of the whole theoretical 64x64 because of how much space that would take, especially if I want to update multiple BG layers, and there's no real reason to ever scroll more than 32 pixels per frame. (I doubt even Chemical Plant Zone Act II does that). I also want a buffer for scrolling vertically (rows) because you only need one DMA transfer for multiple rows in each 32x32 tilemap (where it would have to be a DMA transfer for ever row otherwise). However, this one might cover the whole 64x64 space, for if I want to update the entire tilemap in one shot. For scrolling at an angle, I'd probably just end up writing the corner twice for simplicity and the fact that with multiple rows, it should probably be faster to just do one continuous dma transfer. I have no clue how I'm going to handle updating random entries in the tilemap, but that's something to worry about later.
Re: How are you guys handling updating the tilemap? (scrolli
by on (#176384)
Espozo wrote:
Yeah, I've been running into all types of little issues about how exactly to do this that have been annoying me. My tilemap format in rom is just a regular tilemap with no type of compression, just instead of being multiple 32x32 tilemaps put together, it's just one giant one, so a row can be whatever width. For updating the tilemap horizontally, (columns) I have a buffer that I fill out that basically turns the tilemap sideways, but the buffer is only a couple of tiles wide (like 4x64)

That's a fairly standard technique, dating back to NES days when games used unrolled loops instead of DMA to fill VRAM.

Quote:
and there's no real reason to ever scroll more than 32 pixels per frame. (I doubt even Chemical Plant Zone Act II does that).

CPZ2 scrolls 16 pixels per frame in each direction.

Quote:
you only need one DMA transfer for multiple rows in each 32x32 tilemap (where it would have to be a DMA transfer for ever row otherwise).

Plan for one DMA per row anyway because it still takes two to fill the bottom and top rows of a single map.
Re: How are you guys handling updating the tilemap? (scrolli
by on (#176401)
tepples wrote:
CPZ2 scrolls 16 pixels per frame in each direction.

Weak sauce. :lol:

Yeah, I'm just going to implement 16 pixel wide scrolling, because thinking about it, for what I'm doing, that would be overkill...

tepples wrote:
Plan for one DMA per row anyway because it still takes two to fill the bottom and top rows of a single map.

I was thinking about having two DMA transfers that go on at pretty much the same time, except 2KB away (32x32x2). But yeah, that's a bit of a waste of memory to have a buffer for that... :? I think I'll just update it per row like you said. The one thing I won't do during vblank though is find out where the DMA transfer should start, because that's pretty slow because it requires multiplication (multiply the amount of data per row times the amount of rows you've gone downward.)
Re: How are you guys handling updating the tilemap? (scrolli
by on (#176409)
Espozo wrote:
Yeah, I'm just going to implement 16 pixel wide scrolling

That's almost 4 screens per second (in NES/SNES resolution), so I doubt anyone would ever need anything faster. Players couldn't possibly keep up with the action at those speeds.

The procedure for scrolling a map is pretty simple, specially if the dimensions of the tile map (or name table, or whatever) are powers of 2 (which is not the case with the NES, vertically!). Just keep track of the coordinates of the top left corner of the camera, using whatever logic you consider appropriate o make it follow the player, but save the old coordinates so you can compare them against the new ones. Whenever the camera crosses a row/column boundary, you need to update a row/column. For example, if you're doing a maximum of 16 pixels per frame, you need to detect changes on bit 4 of each coordinate. This bit will change whenever a coordinate goes from 15 (00001111) to 16 (00010000), 31 (00011111) to 32 (00100000), 47 (00101111) to 48 (00110000), and so on... in other words, every 16 pixels. To detect this change, you can XOR the old and new coordinates, and then test bit 4: if it's set, you need a new row/column.

Now you have to find out on which end of the camera the new row/column is. Just compare the old and new coordinates, so you know whether a column starts at (CameraX, CameraY) or (CameraX + CameraWidth, CameraY), and whether a row starts at (CameraX, CameraY) or (CameraX, CameraY + CameraHeight). Then it's all about using those coordinates to read tiles from the level map. How exactly you're gonna do that depends on the kind of compression (if any) you're using, so be sure to use a format you're conformable working with.

All that's left is using the same coordinates to calculate the destination address in VRAM. If the dimensions of the tile map are powers of 2, this is dead easy, just ignore the highest bits and use the usual Y * Width + X formula to find the address.

The whole thing is actually way easier on the SNES or the Genesis, compared to the NES, because NES name tables are 30 tiles high, and because of those evil attribute tables!
Re: How are you guys handling updating the tilemap? (scrolli
by on (#176428)
tokumaru wrote:
Espozo wrote:
Yeah, I'm just going to implement 16 pixel wide scrolling

That's almost 4 screens per second (in NES/SNES resolution), so I doubt anyone would ever need anything faster. Players couldn't possibly keep up with the action at those speeds.

Dunno, depending on the game, falling from a high location down to the bottom of the map (e.g. from a missed jump) could risk going faster than 16px per frame downwards, so it's always a good idea to enforce the cap. Also, teleporters within the same map are by definition faster than that =P Imposing a cap has the side effect of creating a scrolling camera when you teleport.

But yes, under normal circumstances you're unlikely to hit even half that.
Re: How are you guys handling updating the tilemap? (scrolli
by on (#176433)
Well, for teleporters, you could always just fade out, update the tilemap in one go, and fade back in, depending on the game. That's assuming you don't want the scrolling effect to begin with, which you might since it lets you see where you ended up in relation to the teleporter you entered.
Re: How are you guys handling updating the tilemap? (scrolli
by on (#176435)
Nothing stops you from implementing different types of background updates for different situations (except for vblank time on the NES!). Another instance where you might need to update more data is when the player goes into a room that's normally covered up. Sonic does that in Flying Battery Zone, for example. 8-bit games have a harder time implementing this, but Land of Illusion on the Master System does it (the update might be spread across several frames, tough, I can't remember).

That being said, I think most teleporters within the same map will scroll to the destination, rather than instantaneously change the entire background, and I agree that for the player this is much better. Even if you don't want to scroll, some sort of effect is necessary to let the player know that a teleportation is taking place, and that should buy you enough time to draw the new background row by row if you have to.

Falling can indeed be a problem though. I've seen Sonic falling faster than the screen can keep up (not sure if under normal circumstances or just in debug mode), but at that point you have more problems than just the camera, since even the collision detection can get wonky at extreme speeds. It's better to avoid extremely deep pits in the level layout altogether, or cap the vertical speed at 16 (or however big the blocks you collide with are).
Re: How are you guys handling updating the tilemap? (scrolli
by on (#176438)
I remember measuring one of the jumps you can make in Dragon's Castle as going as fast as 13px per frame downwards if you miss and fall. Not as bad there (it breaks at 32px per frame instead), but it does certainly make me wonder and the fall isn't that particularly long either (344.5 pixels or barely a bit more than 1½ screens high - you accelerate downwards at 0.25px per frame). But yeah, a speed cap on falling is probably a good idea, not like the player would notice or care at that point anyway.

tokumaru wrote:
Sonic does that in Flying Battery Zone, for example.

Right, S3K really loves swapping maps:

  • Angel Island (the drum you run up before it gets on fire)
  • Hydrocity (wall of doom™)
  • Marble Garden (the collapsing part)
  • Ice Cap (background swap)
  • Launch Base (indoors vs outdoors)
  • Flying Battery (background swap)
  • Sandopolis (the pyramid)
  • Sky Sanctuary (when 2nd foreground appears)

Not sure how many of those involve huge nametable rewrites but it definitely does it a bunch of times.
Re: How are you guys handling updating the tilemap? (scrolli
by on (#176440)
Bartman Meets Radioactive Man isn't a scene or an arms race, but it is a falling race. It has a cap on the vertical scroll rate, and if Bart falls faster than that cap, he is assumed to have died due to landing damage.
Re: How are you guys handling updating the tilemap? (scrolli
by on (#176443)
tepples wrote:
It has a cap on the vertical scroll rate, and if Bart falls faster than that cap, he is assumed to have died due to landing damage.

Donkey Kong Land 2 does this too, which is BS because Donkey Kong Country doesn't.

Anyway, for vram, I'm probably just going to implement some generic system, like how much data to transfer, the source address, where in vram to transfer the data, the amount of data to skip when writing to the destination (like if you want to write to the tilemap vertically) and I'll have a system for actually generating the data for this table based on how the screen is scrolling. Hell, for different situations, I could even go to a different scrolling routine if I need something more hardcoded, like updating the bottom of a tilemap in columns but not the top, like if you have a horizontal split a BG layer.
Re: How are you guys handling updating the tilemap? (scrolli
by on (#176446)
Donkey Kong Land 2 runs on the original Game Boy. Donkey Kong Country runs on the Super NES. Donkey Kong Country (for Game Boy Color) runs on the Game Boy Color and displays an error message on the original Game Boy. The Super NES and Game Boy Color have three advantages over the original Game Boy: a faster CPU, DMA to VRAM (not just OAM), and a much faster LCD for less motion blur. I can see how each of those three advantages might contribute to a terminal velocity effect on the weaker system.