Last time we added the actual gameplay to our game, but it still needs some polish.
Fade to Black
We want a nice dramatic fade to black before we roll credits.
Instead of diving right into InitCredits(); when we hand over the ring, let’s add a new STATE_FADE and InitFade(); instead.
We still want to play the credits song immediately, but we won’t switch the background just yet.
To achieve our fade, we’re going to make a copy of the current palette (background and sprites) and periodically darken it until every color is black.
We’ll keep track of time via our FrameCount variable that’s updated in the NMI handler every frame and our region-specific PPU framerate (FRAMES_PER_SEC).
For our UpdateFade function, we’ll keep track of how much time has elapsed and take action accordingly:
Wait two seconds and let the player enjoy victory
Fade to black every half second over the next two seconds
Wait another second and roll credits
For our FadeStep method, we want to take our copy of the level palette, make every color a darker shade, then write the altered copy back to PPU_PALETTE in PPU RAM.
If we look at the NES color palette, we can see that the colors range from 0x00 to 0x3F and—conveniently—the colors in each column (i.e. with the same first hex digit) are roughly shades of the same color from dark to light.
To accomplish our fade, let’s just subtract 0x10 from every color in the palette until they’ve reached black:
And now we have a nice dramatic transition to our credits!
For good measure, let’s write the bride’s response to the background just before fading.
If you recall from a previous post, we have the entire alphabet (and a good bit of punctuation) in the pattern table that we’re using for the level and credits screens.
I’m too lazy to rearrange the CHR ROM so that the index of each character’s sprite corresponds with its ASCII code, so let’s create a mapping table so we can use human-readable strings in our code:
And now something like CHR_TO_INDEX['B'] will yield index 1, the index of the B sprite.
We can now define a message and write it out to a location in the background:
We’re also going to make good use of this CHR_TO_INDEX map to render the credits.
While setting background tiles was good enough for writing static text, for our credits we want precision at the pixel level and we want our text to move, so we’re going to need to use sprites.
We want to display one message at a time, moving to the next when it’s finished scrolling.
We’ll cap each message to four rows of text at eight characters per row (notice the choice of powers of two so we can do run-time multiplication using shifts) to fit inside our box.
We needed to use CREDITS_WIDTH + 1 instead of just CREDITS_WIDTH because the C compiler inserts a NULL (\0) byte at the end of string literals.
It’s a few wasted bytes, but worth it so that we can write our data in human-readable text.
We’ll also need to set some boundaries where the sprites are allowed to exist:
And reserve some OAM RAM for our text sprites:
We’ll also want to keep track of which credits message to display and we’re going to buffer changes to the sprites’ y coordinates so that we can handle collisions with the boundaries in a clean way.
We’re now ready to initialize the credits:
The null terminators throw a wrench in things, but we were able to work around them.
Now we can write our UpdateCredits to scroll our credits within our bounding box from bottom to top, moving sprites off screen as they reach the top.
You can play with the speed, but I found 10 pixels/second a good balance.
And last but not least, we want to write the date to the screen after the last message has finished scrolling.
Since it’s not going to move, we can just draw directly to the background:
Our ROM is now complete!
Next time we’ll go over getting it onto a physical cartridge.