It is currently Wed Dec 12, 2018 4:33 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 93 posts ]  Go to page 1, 2, 3, 4, 5 ... 7  Next
Author Message
PostPosted: Wed Apr 04, 2018 7:44 pm 
Offline

Joined: Wed Apr 04, 2018 7:29 pm
Posts: 38
Location: Montreal, Canada
Hi.

I'm trying to implement clean 4-way scrolling. Jurassic Park is a good example of this working really well. Im planning to also hide the top/bottom 8 pixels, but that's note done yet.

I'm getting really close to a bug-free version of it working, but now my code has become kind of a mess.

When I started I was hoping for something clean and symmetric, but since then many things crept in:

  • Because of mirroring, vertical and horizontal scrolling have to be treated quite differently, so that almost double the code right off the bat.
  • Crossing the nametable boundary (or wraping) adds some complexity.
  • Having 16x16 metatiles, but scrolling by adding 8x8 tiles at a time requires the code to properly handle coming from the right/left, top/bottom and adding the individual tiles. Also dealing with the possibility of single tiles on the corners.
  • Using the upper left corner as a reference point makes going right/left, up/down ever so slightly different adding more tests/branches.
  • Attributes are also super annoying to manage.

Anyone here managed to implement this while keeping the code relatively compact and clean? If so, do you have any advice on what you did you keep this as painless as possible.

-Mat


Top
 Profile  
 
PostPosted: Wed Apr 04, 2018 8:00 pm 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 4108
You will need separate counters for Camera Y position and Nametable Y position to keep things simple.

_________________
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!


Top
 Profile  
 
PostPosted: Wed Apr 04, 2018 9:14 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11011
Location: Rio de Janeiro - Brazil
bleubleu wrote:
I'm trying to implement clean 4-way scrolling. Jurassic Park is a good example of this working really well. Im planning to also hide the top/bottom 8 pixels, but that's note done yet.

Do note that Jurassic Park blanks scanlines by using blank tiles, rather than forced blanking, so it can still use the MMC3's scanline counter normally. If you have mapper IRQs at your disposal, this is indeed a pretty good solution, but if you don't, timing the blank scanlines right may be harder than it sounds, specially at the bottom of the screen.

Quote:
Because of mirroring, vertical and horizontal scrolling have to be treated quite differently, so that almost double the code right off the bat.

This gets better if you use 4-screen instead.

Quote:
Crossing the nametable boundary (or wraping) adds some complexity.

Having your row/column updates always be composed of 2 parts (even if the length of one of those parts is 0) will make this the norm, instead of an exception.

Quote:
Having 16x16 metatiles, but scrolling by adding 8x8 tiles at a time requires the code to properly handle coming from the right/left, top/bottom and adding the individual tiles. Also dealing with the possibility of single tiles on the corners.

I update whole metatiles at once, so I don't really have a problem with this. If you're updating only half metatiles because of VRAM bandwidth concerns, I suggest you still do things like if you were updating entire metatiles (i.e. decode entire metatiles from the level map and buffer the tiles in RAM), but upload them to VRAM one half at a time on consecutive vblanks. You may need to pick which half goes first depending on the direction the camera is moving, but that should be easy. I don't really get what the problem with the corners is.

Quote:
Using the upper left corner as a reference point makes going right/left, up/down ever so slightly different adding more tests/branches.

You just need to add the camera's dimensions to the respective axes and handle wrapping, no big deal. Or keep a separate set of coordinates for the bottom right corner that you update in sync with the top left, so you can just mix and match coordinates as needed depending on where the new tiles are supposed to go.

Quote:
Attributes are also super annoying to manage.

Yup. I usually keep a mirror of the attribute tables in RAM, which I modify in place when necessary, so that later I can just copy entire rows/columns of bytes to VRAM.

Quote:
Anyone here managed to implement this while keeping the code relatively compact and clean?

I've done it a couple of times for unfinished projects, and the things you mentioned do indeed have to be actively taken care of. The NES wasn't really designed for free scrolling like this, so things like the attribute tables and mirroring will definitely get in the way if you don't design everything around them from the start.

Quote:
If so, do you have any advice on what you did you keep this as painless as possible.

If you can, ditch the mirroring and go with 4-screen, that'll definitely make things easier. I made the switch to 4-screen in my most recent scrolling engine and I don't regret it. It may seem like the cheap way out, but if you're using CHR-RAM in a discrete logic mapper, 4-screen is basically free since you can't even buy 8KB RAM chips anymore, so you can easily wire the cartridge to use the excess CHR-RAM as NT RAM.

Dwedit's advice is good too. Keeping different sets of Y coordinates (one relative to the level and another relative to the name tables) and updating them in sync will save you from having to convert back and forth between them, which is a pain.

Another thing worth mentioning is that there's another technique that can be used to achieve glitch-free 8-way scrolling on the NES, particularly useful if you don't have mapper IRQs at your disposal: use horizontal mirroring and have the PPU hide the leftmost 8 pixels of the screen, while sprites are used to hide the rightmost 8 pixels. Alfred Chicken and Felix the Cat do this. The main drawbacks are the amount of sprites used and the fact that you only get to place 7 more sprites worth of actual game objects per scanline before flickering begins. These 2 games are pretty cool though... Felix the Cat even manages to display pretty large sprites, like vehicles Felix can get in, despite having lost 1 sprite per scanline.


Top
 Profile  
 
PostPosted: Thu Apr 05, 2018 1:35 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 3141
Location: Tampere, Finland
It does get pretty messy no matter what you do.

Here's my Lua prototype implementation of what I was planning to put in my engine: https://github.com/fo-fo/ngin/blob/mast ... roller.lua. It might give you some ideas.

It's completely generic, supporting any mirroring mode. It can scroll 8 pixels at a time, but multiple calls to the function can be made to scroll more pixels per frame. The API basically consists of scrollHorizontal() and scrollVertical(). Those then call scroll() which is sort of a generic routine that can scroll in any direction. Then scroll() calls update() and updateAttributes() to fill the PPU update buffer. There are some simplifications for purposes of prototyping, though. For example, map data is read by "random access" (MapData.readTile( mapX, mapY )). In an optimized implementation you would want to avoid recalculating the map data address all the time (but this, too, can get tricky if you implement stuff like metatiles in metatiles...)

If you can, I'd suggest using macros to write the code for just a single scroll direction (e.g., to the right), and then expand that macro for all 4 scroll directions. It might turn into a bit of an if/else mess, but it's still better than duplicating a ton of code. (Just a fair warning: it's very easy to make oversimplifications in the code when considering only one scroll direction...)

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi


Top
 Profile  
 
PostPosted: Thu Apr 05, 2018 2:36 am 
Offline
Formerly WheelInventor

Joined: Thu Apr 14, 2016 2:55 am
Posts: 1786
Location: Gothenburg, Sweden
It has sort of already been mentioned but conditionally updating 2 rows and 2 columns at any time the NT:s need to be overwritten helps with the attribute problem

_________________
http://www.frankengraphics.com - personal NES blog


Top
 Profile  
 
PostPosted: Thu Apr 05, 2018 4:12 am 
Online
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7602
Location: Chexbres, VD, Switzerland
thefox wrote:
It does get pretty messy no matter what you do.

That. I couldn't have said it better.


Top
 Profile  
 
PostPosted: Thu Apr 05, 2018 7:09 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11011
Location: Rio de Janeiro - Brazil
Just plan well from the beginning and it isn't so bad, specially if you use 4-screen.


Top
 Profile  
 
PostPosted: Thu Apr 05, 2018 7:10 am 
Offline
User avatar

Joined: Sat Jan 09, 2016 9:21 pm
Posts: 518
Location: Central Illinois, USA
tokumaru wrote:
If you can, ditch the mirroring and go with 4-screen, that'll definitely make things easier.


That was going to be my advice also. The other thing that can make things easier (in some ways) is using 32x32 metatiles. That way you can write an entire attribute byte when you draw instead of having to mirror attribute ram or read-then-write it.

_________________
My games: http://www.bitethechili.com


Top
 Profile  
 
PostPosted: Thu Apr 05, 2018 7:18 am 
Online
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7602
Location: Chexbres, VD, Switzerland
Quote:
This gets better if you use 4-screen instead.

Quote:
Just plan well from the beginning and it isn't so bad, specially if you use 4-screen.

If for some reason you give any care at developing a game with the same limitations as actual games were developed in the NES' life, do not use "4-screen" unless you absolutely must, as only 3 games did that, period. RAM was expensive back then and wasting it just for ease of coding was unrealistic.

Now if you don't care, then go ahead and use 4-screen, then it sounds like it'd be much easier but it'd feel like cheating.

For "perfect" scrolling with no actifacts you need to blank at least the top 8 scanlines (if using 8x8 sprites), or even the top 16 scanlines (when using 8x16 sprites) in order to avoid sprite pop-up when scrolling vertically. For that reason you'll need to mess with timing and PPU/mapper registers mid-frame for any clean vertical scrolling. So the only con of using vertical mirroring instead of 4-screen is that you need to blank 16 scanlines, and 8 is not an option anymore. (Technically 15 would be enough when using 8x16 sprites, but that doesn't make a great difference).


Top
 Profile  
 
PostPosted: Thu Apr 05, 2018 7:20 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11011
Location: Rio de Janeiro - Brazil
gauauu wrote:
The other thing that can make things easier (in some ways) is using 32x32 metatiles. That way you can write an entire attribute byte when you draw instead of having to mirror attribute ram or read-then-write it.

Except that every other screen, vertically, is misaligned with the attribute grid due to name tables being 30 tiles (7.5 attribute bytes) tall. Unless you "cheat" and treat the last row of each screen of the level map as non-existent, simplifying rendering but complicating collision detection.


Top
 Profile  
 
PostPosted: Thu Apr 05, 2018 7:35 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11011
Location: Rio de Janeiro - Brazil
Bregalad wrote:
RAM was expensive back then and wasting it just for ease of coding was unrealistic.

Tons of games packed an extra 8KB of WRAM for "ease of coding" though.

Some NES programmers nowadays like to role-play and pretend they are in the 80's, so they won't do things that weren't commonly done back then (they'll hardly give up modern emulators, debuggers and tools though), even if they're cheap/trivial today.

I prefer to think that as technology advanced, the way things were done changed during the years the NES was active. Hardware got cheaper, tools got better, and so on, and it's only natural that development happening after that time continues to change. Each developer will have a different opinion on what's cheating and what's not.


Top
 Profile  
 
PostPosted: Thu Apr 05, 2018 7:37 am 
Online
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7602
Location: Chexbres, VD, Switzerland
tokumaru wrote:
Except that every other screen, vertically, is misaligned with the attribute grid due to name tables being 30 tiles (7.5 attribute bytes) tall. Unless you "cheat" and treat the last row of each screen of the level map as non-existent, simplifying rendering but complicating collision detection.

Even then, you can work with attribute nybbles of 4x2 tiles instead of having to work with quantities of 2-bit at a time; this is still complicated but less than working with entirely 16x16 metatiles.

Quote:
I prefer to think that as technology advanced, the way things were done changed during the years the NES was active. Hardware got cheaper, tools got better, and so on, and it's only natural that development happening after that time continues to change. Each developer will have a different opinion on what's cheating and what's not.

In this case, why limit yourself to 4 screens ? You can just put enough RAM so that the entiere level is decoded to VRAM at once, and nametables are just bankswitched in and out as the player scrolls. This makes a lot of sense.


Top
 Profile  
 
PostPosted: Thu Apr 05, 2018 7:43 am 
Offline
User avatar

Joined: Sat Jan 09, 2016 9:21 pm
Posts: 518
Location: Central Illinois, USA
tokumaru wrote:
gauauu wrote:
The other thing that can make things easier (in some ways) is using 32x32 metatiles. That way you can write an entire attribute byte when you draw instead of having to mirror attribute ram or read-then-write it.

Except that every other screen, vertically, is misaligned with the attribute grid due to name tables being 30 tiles (7.5 attribute bytes) tall. Unless you "cheat" and treat the last row of each screen of the level map as non-existent, simplifying rendering but complicating collision detection.


That's absolutely true. Which is why I qualified the "some ways" -- attributes are easier. Other factors become issues instead.

(I "cheat" in my game, and skip the last 16 pixels of each room. It does complicate things in other spots)

_________________
My games: http://www.bitethechili.com


Top
 Profile  
 
PostPosted: Thu Apr 05, 2018 7:53 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11011
Location: Rio de Janeiro - Brazil
Bregalad wrote:
You can just put enough RAM so that the entiere level is decoded to VRAM at once, and nametables are just bankswitched in and out as the player scrolls.

I wouldn't do this myself, but if someone decided that this was worth the trouble of creating a new mapper (including modifying emulators for testing), I wouldn't think this was a bad idea. It's pretty cool, actually.

Like I said, everyone draws the line somewhere. Creating mappers is a little beyond my skillset, so I'd rather use what's readily available, but I won't restrict myself to the configurations that were common because that's supposedly more "authentic". We're not even talking about something new here, 4-screen WAS used back then, just not by many games.


Top
 Profile  
 
PostPosted: Thu Apr 05, 2018 8:02 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 3141
Location: Tampere, Finland
tokumaru wrote:
Bregalad wrote:
You can just put enough RAM so that the entiere level is decoded to VRAM at once, and nametables are just bankswitched in and out as the player scrolls.

I wouldn't do this myself, but if someone decided that this was worth the trouble of creating a new mapper (including modifying emulators for testing), I wouldn't think this was a bad idea. It's pretty cool, actually.

It's a fun idea for sure... could even be prototyped on PowerPak with its 512 KB PPU-mappable RAM.

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 93 posts ]  Go to page 1, 2, 3, 4, 5 ... 7  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 2 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