Controlling Play Speed
Moderator: Moderators
My game detects, but does not count, frames missed. If I frame is missed (and the game is compiled in debug mode), the background palette color will change (as a debugging aid). For my game, I consider missing ANY frames a bug (I've blown my CPU budget). However, my NMI handlers and main loop do co-operate so that if I skip a frame the game will not crash - it will continue normally.
That can't be said of all games though. There are some types of games where the CPU load varies too much, mostly those with dynamic object loading/unloading, where certain object combinations in certain parts of the levels can be too CPU-intensive. Simpler games with a constant number of active objects certainly shouldn't suffer from slowdowns though.clueless wrote:For my game, I consider missing ANY frames a bug (I've blown my CPU budget).
Anyway, I don't think that dumbing the entire game down to make it easier on the CPU is a good solution, I'd rather live with a couple of "slow spots" in my games than greatly simplifying them and use less than 30% of the CPU power most of the time. I mean, even games considered to be among the NES' greats (SMB3, Kirby's Adventure, the Mega Man series) suffer from slowdowns once in a while. Even games on newer platforms (SNES and Genesis/MD) suffer from occasional slowdowns. That's not something to be ashamed of, as long as it doesn't happen ALL the time (and it does in some NES games).
I absolutely agree. Frame skipping is ok in some games, and not ok in others. But for the game that I'm creating for the compo (hey, does it still exist??), the number of objects is small and the game should never lag / skip.tokumaru wrote:That can't be said of all games though. There are some types of games where the CPU load varies too much, mostly those with dynamic object loading/unloading, where certain object combinations in certain parts of the levels can be too CPU-intensive. Simpler games with a constant number of active objects certainly shouldn't suffer from slowdowns though.clueless wrote:For my game, I consider missing ANY frames a bug (I've blown my CPU budget).
Anyway, I don't think that dumbing the entire game down to make it easier on the CPU is a good solution, I'd rather live with a couple of "slow spots" in my games than greatly simplifying them and use less than 30% of the CPU power most of the time. I mean, even games considered to be among the NES' greats (SMB3, Kirby's Adventure, the Mega Man series) suffer from slowdowns once in a while. Even games on newer platforms (SNES and Genesis/MD) suffer from occasional slowdowns. That's not something to be ashamed of, as long as it doesn't happen ALL the time (and it does in some NES games).
I can even envision a game divided into three threads:
1) NMI handler: PPU updates, APU updates
2) Main game logic (moving stuff, joypads, physics)
3) Background simulation / AI planning which may take MANY frames.
Items #1 and #2 should run every frame, but when spinning for the NMI, the CPU really should be running thread #3.
Right, anything you can't afford to miss you could put in your NMI. You NMI could handle your Sprite Hit Zero waiting loop. Ofcourse you wouldn't be able to do like TMNT1 which uses sprite 0 but doesn't just waste CPU time for that status bar at the bottom. Ofcourse that method has the drawback of too much going on causing status bar flicker glitches.
When I heard that some games run entirely in NMI I thought that was pretty awful. Whatever works I guess, but it still seems hacky or unprofessional.
There is a Bugs Bunny game on NES that looks awful as I recall as it runs at 30fps or something like that because it was programmed so badly.
When I heard that some games run entirely in NMI I thought that was pretty awful. Whatever works I guess, but it still seems hacky or unprofessional.
There is a Bugs Bunny game on NES that looks awful as I recall as it runs at 30fps or something like that because it was programmed so badly.
Yeah, complex AI can be a problematic thing to handle. But if you can find a way to break it down into smaller steps and execute as many as possible every frame, so that only the AI will slow down as necessary, that's great.clueless wrote:3) Background simulation / AI planning which may take MANY frames.
I agree. I think that these alternate methods should really only be used when you are 100% sure that your frame calculations will always end in time. I mean, if these glitches can be avoided so easily, why take a risk? I guess that programmers who have made this kind of mistake in the 80's can be excused to some extent, as they possibly didn't have full understanding of the machine they were working with.MottZilla wrote:Whatever works I guess, but it still seems hacky or unprofessional.
I can't think of any licensed games right now, but a lot of those Hong Kong pirates suffer from terribly inconstant frame rates all the way through. As if the crappy physics wasn't enough to make the experience of playing them terrible.There is a Bugs Bunny game on NES that looks awful as I recall as it runs at 30fps or something like that because it was programmed so badly.
Choppy licensed Bugs Bunny game: The Bugs Bunny Birthday Blowout aka Happy Birthday Bugs by Kemco.tokumaru wrote:I can't think of any licensed games right now,MottZilla wrote:There is a Bugs Bunny game on NES that looks awful as I recall as it runs at 30fps or something like that because it was programmed so badly.
Also choppy licensed games: Hello Kitty World and any game ghost-developed by Micronics.
Even choppier unlicensed game with a character resembling Bugs Bunny: Wait and See aka Ну, погоди.
Ah. That is a shame. I thought it stayed set until it was read.tokumaru wrote: Not it wouldn't... That flag only stays up during VBlank, and since VBlank doesn't last very long, if your logic took longer than a frame to complete, the chances that it also goes over the time of VBlank are pretty high...

That's a good idea. I already use a variable to let the NMI know the buffers that tell the NMI what to write to $2006/7 are finished updating, but I hadn't thought of using that to detect a dropped frame.At the end of my main loop, I set a variable indicating that all the logic has completed, and the NMI uses that to decide if it should update the PPU or not (it doesn't if the frame logic isn't complete), but I could also use it to detect when I missed a frame...
I've toyed with the idea, since my slope detection does many checks that aren't necessary, but make things much smoother. If they cause the game to lag, I could certainly drop some of them for a few frames to save hundreds or thousands of cycles, depending on how many enemies are on screen. I could also update enemies less, or only update the most crucial parts of the AI in times of strain. I really don't want my game to lag as much as Kirby's Adventure, or even Megaman games. I've also thought of using it purely for debug purposes like clueless, so I can do my best to avoid situations that cause heavy strain when designing levels rather than resolving the problem at runtime.MottZilla wrote:What is the point in counting the frame updates missed? I don't think that there will be much you can do about it.
Kirby lags too much in my opinion. Megaman games are mostly fine, but I want to at least beat or tie them.
Well it seems you have a clear path of calculations you could cut out. But then you have to manage when to cut them when not to and you'd want to avoid a see-saw effect where it runs normal for a few frames, then lags for a frame or two. That would be worse than a constant slowdown. So you might want to drop unnecessary calculations based on something better than missed frames and either combine it with missed frames and number of active objects relative to a known amount that causes slowdown. So if the player and a few objects are on screen you can use the extra cpu time but when you get alot of objects and are running out of time you can free some up.
It's an interesting problem. Slowdown should definitely be avoided but it's acceptable in certain places and even in others it might be disliked but still not ruin the entire game.
It's an interesting problem. Slowdown should definitely be avoided but it's acceptable in certain places and even in others it might be disliked but still not ruin the entire game.
I used to do timing that way, but in most cases it turned out to be really limiting compared to using a fixed-point counter. With fixed point for example, if you wanted to do something every 2 frames, you would add $80 to the counter every frame, and trigger on carry. Instead of $80 though you can add any value (zero would stop it) for 256 different speeds.cartlemmy wrote:Ah yes, actually I guess I use a counter. I like using the counter, because you can use it for things like "Do this every n frames":
I've been thinking about this lately. There's two potential problems with updating the sound engine in NMI. The first is it's possible you have a sound effect or something that's synchronized with some graphical animation. So if there's lag, they'll get desynchronized. I think this is probably not a huge problem, practically speaking.MottZilla wrote:I forget which games do it, but some have bad music distorting/slowdown when the game slows down because they do not update the music engine every 1/60th second if the game is slowing down due to heavy cpu load. You should avoid this.
The bigger problem is if you're updating the sound engine in the main loop (like starting a sound effect), and an NMI triggers in the middle of it, things could go pretty wrong. With the PPU the solution is to buffer any changes, and set a flag when it's ready for the NMI to use. But sound engines tend to be more complicated, making such buffering harder (I haven't figured out a good way to do it with my engine yet).
One the other hand, if you only update the sound engine in the main loop, you can just buffer the values to write to the sound registers pretty easily for NMI use. Of course if there's lag, the sound with also slowdown. Worse, if you're using hardware effects (length counters, sweeps etc), those won't lag, which could sound pretty bad.
Tom, you might be able to deal with these problems by using a flag system similar to the one used for PPU stuff:
All sound-related functions could have a protection flag. For example, when you call a function requesting that a new song starts playing, this function will first set this protection flag indicating that critical sound operations are happening. In the NMI, when the time to update the sound comes, check this flag. If a crucial operation was interrupted, meaning that pointers and other variables could be inconsistent, you just clear the flag and return. The CPU will then resume the critical code, and before finishing, it checks the flag. If it is clear, it calls the routine that updates the sound, if not, it just clears it and returns.
The good thing about audio is that, unlike the video, you can update it at any time, so the delays will actually be very subtle, instead of whole frames, and will probably go unnoticed.
As for the sound effects, remember that the NMI routine has ways to know if a frame was missed or not, so as long as your sound engine knows the difference between music and sound effects it can delay the sound effects in case of lag frames. Most of the time I think you'd not want to do that, but if it's really important that some effects are in sync, you could add a flag to each sound effect indicating whether they should lag along with the video.
All sound-related functions could have a protection flag. For example, when you call a function requesting that a new song starts playing, this function will first set this protection flag indicating that critical sound operations are happening. In the NMI, when the time to update the sound comes, check this flag. If a crucial operation was interrupted, meaning that pointers and other variables could be inconsistent, you just clear the flag and return. The CPU will then resume the critical code, and before finishing, it checks the flag. If it is clear, it calls the routine that updates the sound, if not, it just clears it and returns.
The good thing about audio is that, unlike the video, you can update it at any time, so the delays will actually be very subtle, instead of whole frames, and will probably go unnoticed.
As for the sound effects, remember that the NMI routine has ways to know if a frame was missed or not, so as long as your sound engine knows the difference between music and sound effects it can delay the sound effects in case of lag frames. Most of the time I think you'd not want to do that, but if it's really important that some effects are in sync, you could add a flag to each sound effect indicating whether they should lag along with the video.
True, and I do use such a counter as well... But it's cool to not have to use an extra byte of memory (and some cpu time) if your counter happens to need to fire every 2^(0...7) times.Memblers wrote:I used to do timing that way, but in most cases it turned out to be really limiting compared to using a fixed-point counter. With fixed point for example, if you wanted to do something every 2 frames, you would add $80 to the counter every frame, and trigger on carry. Instead of $80 though you can add any value (zero would stop it) for 256 different speeds.
I have considered doing that... In theory it should work, but I can't stop thinking that the limited precision of fixed-point numbers could make some things behave visibly different between versions.Memblers wrote:I have been curious, I haven't tried it yet but how well it would work to use fixed-point for everything timing-related, and then have the assembler calculate the timing for PAL/NTSC. Seems like it should work OK, maybe it could some variations/errors, but probably nothing all that bad.
You also have to be careful about things that could break due to limitations of your engine... Scrolling, for example. If your scrolling engine can update a column of metatiles that's 16-pixels wide every frame, the maximum speed of the camera is 16 pixels per frame. If you are working with NTSC and want to convert that to PAL, the camera would have to move faster than 16 pixels per frame in order to have the same apparent maximum speed, and that would screw up your background rendering. So you have to make sure that 16 pixels per frame is the maximum speed on PAL, but 13 1/3 in NTSC.
But PAL's vblank is over three times as long. So if you have the RAM to spare for a bigger transfer buffer, and you have some CPU time to spare, you could design the engine so that the PAL version's camera can move faster.tokumaru wrote:If you are working with NTSC and want to convert that to PAL, the camera would have to move faster than 16 pixels per frame in order to have the same apparent maximum speed, and that would screw up your background rendering. So you have to make sure that 16 pixels per frame is the maximum speed on PAL, but 13 1/3 in NTSC.