My lizard is the Lizard of Pause
Lizard is rapidly approaching completion, and a lot of what I'm working on right now I'd rather keep a surprise, so instead I thought I'd write a little bit about a very small feature of the game that probably has taken a bigger engineering toll than it looks.
I'm talking about the pause overlay. Here's how it looked in the alpha demo:
Here's a couple of things I was trying to accomplish with this device:
- Indicate a paused game state
- Pause the music
- Display the current lizard and password
- Double purpose for display of text for talking and reading
- Don't hide too much of the action taking place
Since the original concept for Lizard was a single screen game, and the NES is more or less a "two screen" system, it seemed convenient to just put the text on part of that unused second screen. Here's a slow motion peek at the the full video memory:
Aside from needing to put that text somewhere in video memory, I also had to switch the hardware to display it on the correct part of the screen. I covered the technique in my scrolling update, but basically I need to place a single sprite tile at the point on the screen where it switches, and then my program has to wait for the moment when that sprite is drawn. At that moment, it switches the hardware scroll registers to start showing the other screen.
A problem with this, though, is that that sprite has to be on an opaque part of the screen, meaning it can't be the black colour I use for the background. This affects the bottom right corner of every room in the demo, as I have to make sure there is a solid column of opaque pixels for the sprite to overlap; failing to do this would result in a glitchy pause overlay!
You'll notice that the bottom right of many rooms in the demo are actually black, though. There's a workaround here: I can use a second black colour as a substitute, but this takes up very precious space in the palettes! I've only got 12 colours to assign, and having to give up even 1 here is actually a huge problem. The tiles are drawn with 3-colour palettes, so giving up a colour for a redundant black means that I'm stuck with some tiles that can only use 2 colours, or otherwise wasting that whole set of 3.
I elaborated on this point in my update about sprites, but I think managing the limited colour palette is the one place where the NES hardware imposes most strongly on the design of the game.
The other thing that happens at that scrolling split point is I turn off sprite rendering. The sprite layer and the background layer are entirely separate, so I need to do something at this point to hide sprites below the overlay.
Originally I wanted to slide the overlay in as smoothly as possible, one pixel at a time, but it's 64 pixels tall and that takes over a second. It turned out to be way too slow! In the demo I ended up using steps of 4.
Finally I had to do something special for the River Zone. The River Zone always used two screens, so I made a special pause for that one that just erased all the sprites on the screen and replaced them with a row of sprites that said "PAUSED". A lot of NES games hide the action like this when they are paused (Super Mario Bros. 3 is a common example), but I wasn't entirely happy with this.
After the kickstarter campaign, though, I made the switch to double wide rooms and the old overlay wasn't going to work anymore, and this special solution for the River Zone wasn't good enough for the rest of the game either.
So... double wide rooms happened, and I had to find a new way to do the pause overlay.
The first major problem here is that I no longer have free space in the background to put it. I need to draw this overlay directly on top of the visible screen instead. An immediate advantage of this, since it's no longer appearing on a separate region of video memory, I don't need to change the scroll to show it. It appears in its place with no special interaction with the hardware, and no more need for meticulously prepared sprite timing!
Even better: in a lot of rooms I get back that wasted extra black palette colour, since that opaque-pixel requirement is gone! This is a huge bonus.
I have to shift the position of the text to match the scroll, but that part wasn't difficult:
A new problem is introduced, however. The timed split let me simply turn off sprites below the overlay line to hide them. I can't do this anymore, so I have to figure out a different way to keep them off the overlay. I can remove any sprite tiles that are roughly below the overlay, but the tiles are 8 pixels high. This is too coarse, and will leave either a gap or an overlap.
To get that sprite cutoff clean, I ended up intentionally exploiting a flaw in the system. You may remember from my sprite update the cause of "flickering" on the NES being a limit of 8 sprites per scanline. What if I deliberately put 8 sprites on the cutoff region to make the rest drop out?
In the picture above, that one sprite halfway off the edge of the screen on the right is actually a stack of 8 sprites, perfectly cutting off all other sprite tiles that overlap that region of scanlines! You can't actually see these 8 extra sprites, as they're carefully hidden, but they're still there doing their job.
Here it is in action. Sorry about drowning the pandas, but it was for science.
It doesn't really slide up quite like the original version, though it's a bit quicker: 8 frames to draw black lines, 1 more to set the background palette to a text colour, and 5 more frames to draw the text. The last thing drawn is the START indicator, which gives you an immediate visual indication that you can now press START to continue.
This fits the set of goals I had pretty well. It may be surprising to say, but it took a very long time for all of these pieces to come together. Some ideas I did not have right away; other things I put off solving while I worked on different things. It was only a few days ago that I finally implemented the last, most subtle pieces of this. One less thing on my list of unfinished tasks, which is getting very short by now!
I hope you've enjoyed this explanation of a very small but important feature of Lizard. I now go back to working on the game... which is almost complete! (AAAAH!!!)