Good fade routine?

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Good fade routine?

Post by JoeGtake2 »

So just futzing around with a simple fade screen transition. I have the palette in RAM and have a routine that updates that ram. The palette is updated in the NMI. I have a routine (LoadPaletteRam) that I can call which changes a palette (uses variable backgroundPaletteOffset to pull values from the correct place in a table). That's all working. But I can't get any sort of gradual fade.

I figured something like the following would work - the X register SHOULD work as a timer. However the result still appears as instantaneous...from first palette to last palette rather than showing one at a time. I generally understand why - I'd venture that all the math is happening before the NMI displays the result, giving the impression it was just a single "change" rather than four "changes", but I thought I'd at least share this as my first idea for it.

Code: Select all

ScreenTransition:
        LDX #$20                 ; load 20 into x to act as a countdown
FadeOut:
        DEX                      ; after counts down from 20 to 0
        BNE FadeOut
        LDA backgroundPaletteOffset  
        CLC
        ADC #$10                                 ; increase the table offset by a 'row'
        CMP #$40                                ; the highest 'row' is 40
        BPL doneFadingOut                  ; if it has reached 40, get out of the routine
        STA backgroundPaletteOffset     ; if not, keep the new value
        TAY
        JSR LoadPaletteRam         ;; in this routine, what is in y determines where in the table to grab values from
        JMP ScreenTransition                ; start it over again with the countdown
doneFadingOut:

There's nothing *wrong* with this code - it does exactly what I'm telling it to do, but it's happening all before the screen is updated (my conjecture), so the effect is never seen. How do people generally handle simple fades like this? Anyone have any suggestions?

Thanks!
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Working around the NES's lack of 50 shades of grey

Post by tepples »

I handle fades by subtracting $10 for a few frames, $20 for a few frames, and finally $30 for a few frames before blanking the screen and loading the new graphics into video memory. See, for example, (I think) load_faded_palette in src/help.s in my FizzBuzz project.

On the NES, you'll probably have to live with a stepwise fade. Changing only part of the palette won't work well because, for example, the sky color will change only in steps. It's the same reason you'll never see a game based on Fifty Shades of Grey. To minimize the problem, make fades reasonably fast; for example, don't wait 15 frames for each step. But don't make it too fast either, or the player won't be able to see the fade at all.
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: Good fade routine?

Post by JoeGtake2 »

I'm perfectly ok with a stepwise fade...I feel it looks very "NES". That's the effect I was going for anyway. How are you counting the frames? That's where my issue is, I think. I was using that method to make X into a timer, but that is not counting frames...it's just decreasing many times PER frame, so I don't see the gradual result.

Conceptually, that's very similar to what I'm doing, but I think my idea for timing it is what's throwing me off.

Thanks!

**EDIT: nevermind. Just used my vblanktimer. Works fine now. Another problem it seems I just had to articulate in order to solve. :-)
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Good fade routine?

Post by tepples »

JoeGtake2 wrote:Another problem it seems I just had to articulate in order to solve. :-)
One of the reasons Jeff Atwood started Image Stack Overflow was to help programmers become better communicators. Sometimes this starts with what some call rubber duck debugging, what some call "prayer", and what I call "explaining it to Coorow".

Image
If I can describe the idea to a doll, the solution is likely to pop into my head.
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Good fade routine?

Post by rainwarrior »

A few things:

1. Your palette should be uploaded to the PPU during vblank and nowhere else. Even if you created a much longer delay than your 32 x DEX BEQ, you would have to wait for the next vblank to apply the palette changes anyway. It might work on some emulators, but it will fail on hardware. Also, even if you were changing the palette mid-frame, what the user would see is an image that gets progressively darker in bands from top to bottom, and the effect would only be seen for a single frame.

2. 32 x DEX BEQ is a wait length equivalent to about two scanlines. You need to increase your order of magnitude a couple of times if you want the delay to be noticeable to the user. Though, as you've noticed, it's usually better to count NMIs for that purpose.

3. You can find out how long your delay took by several methods. Here's a few:
- look up the instructions in the loop and count cycles, and learn how long a cycle takes
- use a debugger to count cycles (FCEUX has a cycle counter on the right-middle of its debug panel)
- write to $2001 to change the emphasis bits immediately, this will show you visually onscreen when the instruction took place (FCEUX doesn't support more than one emphasis change per frame very well, though, should use Nintendulator or real hardware for this one)

4. You've probably heard this already, but $0D is a "forbidden" colour, so when fading out it's important to prevent this if you're using the greys from the $XD column. My method is just to avoid that column entirely, but if you are using it you need special logic to prevent the "none more black" $0D that will confuse many televisions (another thing emulators won't show).
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: Good fade routine?

Post by JoeGtake2 »

Rainwarrior - thanks for the input. Definitely don't even have to think it out all that much though...I just needed to figure out how to count frames...which I already am doing. As soon as I wrote it out as part of this post, I had a forehead-smacking moment. The fact I am having those tells me I'm making progress in my understanding, at least, it's just not second nature quite yet. It happened yesterday, too. As of now I can feed the subroutine a 'fadeSpeed' variable and get whatever length speed I want (in frames). It works just as I wanted it to.

The look up cycles count though definitely helps!

Tepples - dude, "rubber duck debugging", or some variant, is definitely making its way into the documentary. Ha! Now I suppose I have to figure out what's an even more ridiculous inanimate object that a rubber duck that would be sensible (or the most nonsensical possible) to be at my work desk!
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Good fade routine?

Post by tokumaru »

JoeGtake2 wrote:dude, "rubber duck debugging", or some variant, is definitely making its way into the documentary.
I'm not sure I could explain something to a rubber duck (or similar) with a straight face, but I did have a pretty good insight recently while explaining a problem to someone else.

Regarding the fading animation: Yes, the code that's supposed to delay the animation is wrong. Everything in a game should be timed in frames, and should not hog the CPU. Each part of the game has to update itself for the next frame only, and free the CPU so that other parts can do the same, otherwise the whole program will pause, and that's hardly the effect you're going for (and even when it is, there are basic maintenance tasks that still have to be performed and shouldn't be paused).

Like any other entity in the game, it's important that you maintain the state of the fading animation so that you know what to do about that particular task every frame. The first information you need is a flag indicating whether the game is fading. If it isn't, you can simply skip the fading code altogether. If it is fading, you have to know if it's fading in or out (a variable could have 3 states: not fading, fading in, fading out), what the current brightness is, and how long to wait before advancing to the next brightness level. That should be all the state you need.

With this setup, initiating a fading sequence would require resetting the delay, setting the brightness level to the minimum or maximum, and setting the type of fade (in or out). After that, the fade code will start to run every frame (because a fade in our out has been signaled, as opposed to no fading), with a logic similar to this:

Code: Select all

- decrement delay;
- if delay is 0:
    - make a copy of the palette adjusted according to the current brightness level;
    - if the target brightness has not been reached:
        - update the brightness according to the type of fade;
        - reset delay;
    - if the target brightness has been reached:
        - change the state to "not fading";
Or something along these lines. Just keep in mind that everything in a game is part of something bigger, so it can't take over the CPU and use all of the system resources exclusively for itself indefinitely. Each part of the game (be it the scrolling engine, the camera, the player, the enemies, the fading animations, whatever) is an entity with a state, and a piece of logic that runs every frame to control that state. This ensures that each part of the engine gets its own slice of time to take care of itself and everything will appear to be running in parallel, like is supposed to be in a game.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Good fade routine?

Post by tepples »

Unless you decide that pausing is acceptable for a fade. Does Super Mario Bros. 3 run any game logic during its fades?

Do we need to brainstorm things more ridiculous than "rubber duck debugging"? Perhaps "talk to the snowman" or "talk to the Weeble".
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Re: Good fade routine?

Post by Celius »

I seem to remember discussing this a while back (wow, apparently 7 years ago):

viewtopic.php?f=2&t=4536

One of the ideas was using fade tables, rather than doing the math to subtract $10 from each color. I have a feeling it would look more realistic to kind of "scale" the colors down, given a brightness level. So if there were 5 brightness levels, they would represent 100%, 75%, 50%, 25%, and 0%. You would have a look up table for each level of brightness, where you see what color it would be at 100%, and use that as an index in the other tables to see what color it becomes at that brightness.
Because when I think about it, at 50% brightness, color $35 should become $15, and color $15 should perhaps become $05 instead of black (because you halved the brightness of that color).

The other good thing about the tables approach is that it wouldn't have to stay in the same "hue" in the palette. So $35 doesn't have to change to $3x, it can change to $2x, if you want.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Good fade routine?

Post by tokumaru »

tepples wrote:Unless you decide that pausing is acceptable for a fade. Does Super Mario Bros. 3 run any game logic during its fades?
Can you really go by several frames not updating anything other than the palettes in your game? I mean, even if you want everything to look still during fades (which is not what happens in movies, BTW), I'm sure thete's some sort of low level maintenance that would ideally keep going. I don't know, it sounds to me like bad practice to hijack all the resources for a single task.

As for the actual color processing, I'm with Celius. Tables allow for smoother fades than the ridiculous 4 step sequence you can get with usual methods. If I can't waste much space on such tables, I at least arrange the colors in a made up way that's more friendly for doing math (e.g. I won't land on forbidden colors) and use a single table for converting colors in that format into what the NES actually uses.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Good fade routine?

Post by tepples »

rainwarrior wrote:You've probably heard this already, but $0D is a "forbidden" colour, so when fading out it's important to prevent this if you're using the greys from the $XD column. My method is just to avoid that column entirely
I agree. Colors $2D and $3D aren't different enough from $00 and $10 to matter, and they're the same as black on 2C03/05 RGB PPUs.
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: Good fade routine?

Post by JoeGtake2 »

Yeah, all this made sense to me. I like the table idea for finer control purposes, and I should have the ROM space for the tables. Right now, I *am* hijacking the resources to run the fade. I already have conceptualized how to change it with just a few tweaks...but right now I'm going to put that on the list of to-dos. I realized that even things like the music engine would be hijacked and there'd be an x-number-of-frames pause to that, which would just get annoying.

I do have a Darth Vader head cookie jar next to my desk...maybe that's my "rubber duck". haha
Post Reply