• Smooth scrolling inside caves on Dragon Warrior for the NES

    Lately I’ve been wondering if it’s possible to achieve smooth scrolling in Dragon Warrior on the NES.

    This post describes the problem and a possible solution. I haven’t tried this solution, but based on my understanding of the NES’s PPU (picture processing unit), I think it could work. At least, maybe it’ll inspire someone to try it or to propose different techniques.

    Background

    In Dragon Warrior, scrolling is smooth in totally-exposed areas, which means all areas outside of caves. On the NES, this is achieved by using a method typical of the NES: by updating the PPU’s scroll registers and updating the tiles just outside the visible screen area while scrolling. I’m not going to explain PPU mirroring and scrolling; there are explanations of the topic like Retro Game Mechanics Explained’s great video on the topic.

    Dragon Warrior uses vertical mirroring, which means that the video memory can support two whole screens wide and tiles can be updated off-screen while moving horizontally; but the tiles being updated on the top and bottom of the screen are visible during a vertical scroll. This is hidden by most TVs (and emulators!) because it’s outside the physically visible screen area, due to overscan.

    In caves, Dragon Warrior doesn’t scroll smoothly at all. This is caused by two conflicting factors:

    • To hide the tiles being updated as they change or become visible/invisible, they need to be on the screen edges.
    • In caves, you can only see the area immediately surrounding you, which varies from a 1x1-tile area (no lighting) to a 7x7-tile area (RADIANT spell). This doesn’t reach the edges of the screen.

    As a result, stepping by one “Dragon Warrior” tile in a cave only has two frames of animation: One stepping halfway (one 8x8 character) and then one to step the next half.

    Aside: While viewing the name table in FCEUX I observed that the X scroll position alternates between 32 ⯈ 0 ⯈ 32 during a cave step:

    1. X scroll = 32 (at idle)
    2. Update all the tiles for the half-step in the off-screen nametable at X = 0
    3. Set X scroll to 0
    4. Update all the tiles for the full step in the off-screen nametable at X = 32
    5. Set X scroll to 32

    The problem

    This feels sluggish and disorienting, especially when you’re only using a torch or have no light at all. In fact, the brick floor is only an 8x8 repeating pattern, and since a half-tile step moves by 8 pixels at a time, you can’t tell if you’re moving at all. Pressing in a direction isn’t a reliable way to move because move inputs only seem to register on certain frames, so depending on when you press a direction and for how long, you might turn & move, or just turn.

    Clip of cave movement with no light.

    Most players would be using a torch or the RADIANT spell, so they can orient themselves using the walls visible around them. But speedrunners, especially those playing Dragon Warrior randomizer, are more likely to lack or avoid using torches or RADIANT, and they’re familiar enough with the cave layouts that they don’t need to see where they are in them. However, they still need to know whether or not their inputs registered as movements.

    In a situation like this, players typically bonk against walls to orient themselves, which plays a sound effect. But sometimes you need to make a turn before reaching a wall, so the problem isn’t solved for all cases.

    Proposed solution

    I think it’s possible to use the NES’s smooth scrolling in caves, while still keeping a limited visible area.

    The process is essentially the same as full-screen smooth scrolling, but instead of updating characters that are just off the edges of the screen, we’ll update them right outside of the player’s lit area, and try to hide them so that the result looks clean.

    This is a little different for horizontal and vertical scrolling because of the PPU’s limitations.

    Horizontal scrolling

    This process uses black sprites on the left and right of the lit area to cover our tiles while they’re updated. This is a pretty common technique for full-screen horizontal scrolling, where the left edge of the screen is masked using a PPU feature that does precisely that; and the right edge is covered by sprites.

    When the player moves left or right:

    1. Place a column of black sprites just outside the left and right of the lit area. These will cover the characters as they’re being updated.
    2. Place the new set of characters on the side that’s coming into view – eg. on the right when the player moves right. They’ll be obscured by the column of black sprites on that side.
    3. Update the X scroll as usual during a horizontal scroll. The outgoing side will also be covered by a column of black sprites that we added in step 1.
    4. Remove the characters and the sprites.

    Since the hero is typically the only sprite visible while in caves, sprite limitations are unlikely to be a problem. The only other example I can think of is that the princess is probably also a sprite in the swamp cave. The Dragonlord at the bottom of Charlock is also a sprite, but that area isn’t dark; it’s a full-screen fully-visible area.

    Vertical scrolling

    This is the same process as horizontal scrolling, but we can’t use sprites to obscure the characters being updated, because there’s a limit of 8 sprites per scanline. However, it’s possible to update the X scroll position between scanlines, which games often use to achieve a status bar that stays in position instead of scrolling with the rest of the screen. We’re going to use that to display an empty part of the nametable for the areas where we’re updating the characters during a scroll.

    First, we need to ensure that the nametable is empty (all black characters) at Xscroll + 32, so that when we swap over to it, we won’t show anything. This only needs to be done when entering the cave, because we will never have any reason to draw anything in that space while doing other cave stuff.

    Then, during a vertical move, we need to do this on every frame:

    1. Increment X scroll by 32 so that we’re showing the empty area.
    2. Update the Y scroll to keep a smooth scroll.
    3. After drawing the last empty scanline, increment X scroll by 32 again so it wraps around and starts showing our tiles.
    4. Allow it to draw the entire lit area.
    5. After the last lit scanline is drawn, increment X scroll by 32 again so that we show the empty area again.
    6. At the end of the frame, increment X scroll by 32 again to return it back to the visible area. This is where we want to stay while idle, and during a movement we can repeat this process. As an optimization, we can skip this step and step 1 if the next frame is a continuation of the vertical scroll, because they cancel each other out.

    As with horizontal scrolling, we’ll need to update the incoming and outgoing tiles during the scroll, but this is also as usual during a vertical scroll; we’re just doing it near the lit area rather than at the screen edges.

    Split scrolling can cause some artifacting/jitter as the scroll position might not get updated before the next scanline starts to be drawn, but since we have just empty black space at the start of each scanline, I don’t think the artifacting will even be visible.

    Possible issues and limitations

    Visible updates during scroll

    Although I think this process will work, I haven’t actually tested it. My primary concern is that we don’t have enough time to update the incoming characters before we need to show them. I think it would be unacceptable to delay a tile movement to update those characters before the animation, because this would make continuous movement (eg. holding right for 2+ tile movements) jittery as it would need to pause on each tile.

    I’m also not sure if we would leak palette changes into the lit area during scroll.

    UI interference

    It’s possible that this solution would interfere with the UI and battle view, but I don’t think it will because none of those can happen during a scroll, and when the hero’s position is aligned with the tile we don’t need to do any work to scroll at all.

    Mitigating these issues

    In existing full-screen scroll implementations, games ensure that the characters immediately off-screen are ready for the next scroll. If we did that, we’d need to maintain the X scroll split and the “hiding” sprites at all times. I think this would solve the problem with having enough time to update characters before the scroll, because we can update them during the scroll instead – just like games already do for full-screen scrolling.

    This would definitely cause interference with the UI. To mitigate that, it might be acceptable to clear all of our scroll machinery (our hiding sprites and pre-drawn characters) when we need to show UI, and restore it when the UI disappears. This would probably add some delay to UI appearance and removal, but I think that would be acceptable as the UI isn’t particularly quick to begin with.

    Terms used in this post

    • PPU: The NES’s picture processing unit that manages video memory, the nametable, scrolling, and everything else connected to the display.
    • Player: The player of the game.
    • Hero: The hero sprite that the player controls.
    • Character: A PPU character, which is a single 8x8-pixel entry in the PPU’s nametable.
    • Tile: A “Dragon Warrior” tile, a 16x16-pixel background tile composed of four 8x8-pixel nametable characters arranged in a 2x2 fashion. This is a common arrangement on the NES because even though characters are 8x8, color palettes apply only to a 16x16-pixel space.
    • Lit area: The visible tiles in the cave. The area varies from 1x1 to 7x7 depending on whether the player is using the RADIANT spell (which gradually decays from 7x7 to 1x1), a torch (3x3 decaying to 1x1), or neither (1x1).

  • Updated Jekyll

    Today I updated Jekyll, which I use for this site, and fixed some problems with URLs that had affected this site ever since I created it at the start of 2016.

    I switched from the BlackDoc theme to Midnight with some customizations (mostly, simplified). I’m not especially experienced with HTML or CSS/SCSS so I limited myself to simple changes only.

    I think the URL problems were caused by some bad assumptions in some layout files; possibly assumptions I had made, I’m not sure. The result was that the RSS and Atom feeds had URLs with too many slashes, such as https://mrb0.com///. This was caused by two things in my _config.yml:

    1. url was https://mrb0.com/ with a trailing slash that I’ve removed.
    2. baseurl was /, but should be "".

    However, when I fixed both of those things, only pages in the root of the URL were properly-styled, because the layout files used baseurl to reference the CSS resources (and probably others); eg. <link href="style.css">, which now resolves to style.css instead of /style.css.

    Ultimately, I decided to switch to the Midnight theme after seeing Shamus Peveril use it on his new site.

    There are still a few style tweaks I’d like to make, but I’m happy with the site so far. Syntax highlighting works in code blocks; I’m not sure it did before. I’m not sure I ever tested it!


subscribe via RSS