How to enforce a framerate?

I’ve been creating my game using the principles of framerate independence (time.deltaTime).

However, after extensive testing I realized my game mechanics just won’t work within a framerate independent setup. There are highly sensitive timing issues where I need things to play out over a specific range of frames with 100% consistency.

Analysis of games in somewhat similar territory (fighting games, lets’ say) shows they also run at locked framerates.

My temporary solution is to continue coding as if it’s framerate independent, but wrapping deltaTime behind a property that can set it to a constant value instead.

This is just so I can keep developing without it all breaking in the end. But I don’t see any way of shipping this game with a framerate independent setup, so I’m only doing this to cover all of my bases.

Everything is working fantastically if I treat it as non-framerate independent, but modifying that property relative to a specific framerate. So if it were 120fps, the property would return half of what it returns at 60fps. Everything works deterministically and great! If framerate slows a bit, the game slows. No big deal, the crucial input timing windows are still preserved.

However, if I set targetFramerate to 60fps, but allow Vsync, when I build the project the Vsync FORCES the game to run at 120fps (my monitor refresh) and everything goes twice as fast!

The solution seems to be to force 60fps (which just about every display out there respects as a valid rate), but I can’t seem to do this while allowing users to Vsync. Or, possibly they will just override vsync anyways in their graphics settings. I just can’t seem to figure the way out of this even though plenty of games ship that only respect a very specific refresh rate. Any help would be greatly appreciated!

Interesting… it’s like the higher refresh is “pulling” updates?? That seems … unlikely, but I suppose…

Are you testing this from built game binaries? I think none of the vsync or FPS stuff works in editor…

VSync does work, but only in play mode. I don’t know if the underlying wait-for-frame method is exactly the same (I’m assuming it is), but the Stats (and the sound of my GPU fans) both agree that the frame rate is capped.
(2021.3.8f1)

edit: Previously you had to turn on VSync through code, then it was buried somewhere in the settings, but nowadays VSync is accessible from the aspect menu in Game window.

edit2: It also makes sense to check articles regarding maxQueuedFrames
here’s just one regarding fixing Time.deltaTime in 2020.2 that explains the whole mystery surrounding VBLANK and screen tearing, and was very fun to read to a geek.

1 Like

If you’re an FPS player, check whether you’ve perhaps set some external NVidia utility to fix your framerate via drivers.

@SkillBased
Btw to specifically answer your question, you set Application.targetFrameRate
And on this page there are some further explanations which might have something to do with the scenario you’ve described (namely VSync multipliers, but this topic seems to be related to mobile platforms, idk).

edit: see also edit2 in my post #3

Thanks for your replies. Yes the specific issue is that Application.targetFrameRate is not being respected. It is overriden by Vsync.

So the problem isn’t that this is unusual behaviour, it’s that its seemingly how Unity is supposed to work. Vsync syncs to the monitor refresh, as per the documentation you linked to:

“On all other (non-mobile, non-VR) platforms, Unity ignores the value of targetFrameRate if you set vSyncCount”

Obviously many users choose to impose vsync through their graphics card, or otherwise expect it through the game menu, which is a problem because the game really has to run at 60FPS no matter what the monitor refresh settings are on the user end. Many games have this requirement so I know it’s not just a quirk of my development choices.

The Vsync-Count can do things like skip render frames (so if you have a 120hz monitor and set VsyncCount = 2, it will skip every other frame, making it appear to run at 60fps despite being on a 120hz refresh.)

This seems a partial solution (if I can poll the set monitor refresh), but what to do if the user has their monitor refresh of 144hz, which isn’t such a nice multiple!

No matter how I look at this, problems keep popping up. What I don’t understand is why this isn’t simple given that so many games were made with fixed rates in mind.

The higher refresh is increasing the update calls. That’s how it’s supposed to be of course so it’s not a surprise to me that 120hz doubles the updates.

I’m testing this in built executables, because as you say the vsync doesn’t seem to work the same way in the editor.

I guess that depends entirely on what “target” means in Application.targetFrameRate

To me if I set the target framerate to 60, I don’t ever expect you to give me 61fps, but that might not be the function of that setting.

Yeah I agree that I wouldn’t expect 61fps, but that’s a guarantee that Unity doesn’t give us.

As an example, lets say you have an oddball monitor running at 70hz. A targetFrameRate=60, vsyncCount=1 will produce a result of 70fps and possibly anything lower if your system can’t keep up rendering update calls at that monitor refresh rate.

I’m actually surprised this isn’t talked about all that often, because it’s clearly a problem for some specific game types.

I’ll add this because someone will likely consider this possible fix as well.

I briefly considered putting all of my update logic in FixedUpdate, and setting its fixed call interval to whatever produces 60FPS (something like Time.fixedDeltaTime = 0.0166).

The trouble is that user inputs are still being polled at the refresh rate! So you could get double the amount of inputs within the same FixedUpdate call if the user has a 120hz monitor than if running at 60hz.

The “workaround” in that instance is to set flags everywhere to check inputs and turn them on/off accordingly. Which I’m sure will create a massive mess especially in my game which has a lot of race conditions and complex input commands.

Besides that, it also seems a kludge given that FixedUpdate was intended for physics calculations. And seems to me that putting any routines in FixedUpdate that rendering depends on will probably just create stuttering, juddering an undesirable outcomes.

In short, it seems a resounding “no” to using FixedUpdate as a potential fix to these issues.

Yes, FixedUpdate() is only really used for physics. You CAN use it for other things but almost always you will regret this decision, since that’s not its purpose.

Fortunately for 99.99% of all games, frame rate just has to be “as high as possible” and the game designs will usually be fine, assuming correct use of Time.scaleTime when you are moving things under direct control (eg, not physics or animation)

Exactly. Although I wouldn’t say it’s 99.99% of games. There are plenty of fighting games, beat-em-ups, rhythm games, and even oldschool 2D action games, remakes and ports that ideally require very precise and known frame timings. Maybe in the range of 2% - 5% of games released today.

Imagine if Street Fighter was made in Unity. I don’t even know what they would do to fix this. But that’s because my problem here would be their problem :frowning:

Well I doesn’t matter how frequently they are polled. You still get to process them in a fixed rate. If you mean this is a problem because of deltaTime, you ought to use fixedDeltaTime in that case. There is no way your input can be processed more frequently or with a greater weight when you do that. This is essentially fixing your rate to a technical minimum. And if a target machine is too slow and the frame rate drops, you just don’t account for that. This is why games have minimum specs.

edit:
Yes it’s double amount of inputs, but you still skip to react to it every other monitor frame.

This is why it’s a problem. The background polling happens at unknown intervals (could be 4x every FixedUpdate, could be 3x… or none… as its tied to the refresh rate which is highly variable for reasons we’ve discussed).

Now imagine you have something like:

If (input.GetKeyDown(“Boost”)
playerBoost += 1;

If the end user polls at less than FixedUpdate (lets say, for sake of argument, 59FPS input polling due to slow hardware, while remaining at 60FPS FixedUpdate calls) there will be a random frame where it registers the condition twice, resulting in twice the boost!

And of course you can have any manner of funny business once you enter this type of scenario.

This video explains the problem pretty succinctly.

However, even if using FixedUpdate in this weird way ends up working without side-effects in complicated race conditions, there is still the issue of using FixedUpdate to determine rendering that will likely cause jerkyness issues, particularly when a precise 60FPS can’t be maintained.

Maybe somebody far more clever than myself can figure out how to utilize FixedUpdate to solve these issues, but I’m going to assume it’s just best left for what it was originally intended (physics engine).

Sorry but I don’t get this.

There is no mystery “register”. This code has to live inside

void FixedUpdate() {
  If (input.GetKeyDown("Boost")
    playerBoost += 1;
}

Where FixedUpdate cannot be called more frequently that it is. This is the whole point of it. How do you register something twice?

I would also claim that if you want to make sure your processing isn’t affected by the end-device framerate, it is enough to only treat your input inside FixedUpdate, because this is what modifies your internal state, and when you have a fixed rate of processing, the ensuing consequences are tied to a predictable pattern and rate of events, assuming the end-device can nominally support the frame cost.

edit:
Aha I see now you said if frame rate is 59. Well it’s your problem because it shows an inflexible design on your end. There are no hardware guarantees this is possible. The end user can always have an unfathomable configuration of ups and downs. And there is no game on this world that fixes FPS to a scientifically accurate number. If your application is sensitive to such changes, it’s your job to provide error minimization.

Btw, you cannot just say “what if it’s 59”, because that’s contrary to what it means, if it’s 60, it cannot be 59. However, moment to moment framerates are allowed to deviate, the point of them staying fixed is in their accumulated statistics. Your job is to account for deviations, that’s perfectly reasonable. If you can’t, you can’t possibly blame Unity, because as you said it yourself, so many games did it already, it’s not like they have a magic button to make their game unaffected by boosts and drops.

Yeah I already mentioned why it would register twice. On a random frame there will be two FixedUpdates between an update call. And remember, InputGetKeyDown in FixedUpdate does not poll at the FixedUpdate, it polls before it at whatever the Update interval is, and that’s tied to framerate. If you check out that video link, it’s quite brief and shows how it happens in the editor.

Beyond that, it’s not inflexibility of design because this is how certain games were created before modern engines, and those same games with the same demands still exist, and they are being developed in other engines.

I think you’re mistaking that I need 60FPS and not less. Actually, 60 or less is fine. Obviously if a game is locked to 60FPS, it has to be considered that some hardware can’t keep up. I’ve already mentioned that it’s OK if this happens and the game just slows down. This is the normal and expected behaviour for these types of games. But using FixedUpdate in this way totally messes with that due to the input issues described.

If you play Street Fighter V, the game will enforce 60FPS regardless of your refresh rate because it’s input timings MUST be frame perfect. If 60FPS can’t be achieved, it will slow down. But that’s what that particular game demands and it works great on a great many modern devices.

SFV is created in Unreal. So if Unreal can accomodate it, I don’t see why Unity shouldn’t.

EDIT: found a prior thread partially talking about this. No solutions, but they identified the issues at hand. I’ve looked extensively and have yet to find anyone who has not only identified the issue, but come up with a viable solution.

I got you, maybe you need to refresh to see my post fully.

I just now checked the video and you got it all backwards.
The presenter is clearly talking about input “dropping”, not input “boosting”.
And I’m not sure why he’s talking about Unity’s polling AT ALL.

The reason why input is polled the way he explains it, is not because there is some mystery polling happening in the background, but because HE talks about the events being actively processed input inside FixedUpdate, instead of Update. You just got it all wrong.

And then at 11:44 he deliberately handles Input in Update, which is tied to refresh rate, and thus it begins to feel more responsive.

Basically a game will drop any polled input if you don’t react to it swiftly enough. If it’s polled, it is not queued. I’m not sure why you don’t understand this concept. In essence, you have an issue if the end-device has a LOWER framerate than the rate at which you attempt to process input, because you skip to handle it due to mismatching framerates.

As I said, there are ZERO guarantees on that front, whether the game can run equally good on each computer, and this is why games have a minimum spec. This video doesn’t talk about having a better framerate at all.

Yes you can try to play Street Fighter V on a bad computer, and yes it will react differently if it allows you to engage in gameplay. You can’t physically enforce a framerate, you can only cap it (say with VSync) and it works statistically, over some number of frames, it’s not something that is totally fixed, frame by frame.

I don’t think I can get it wrong because the presenter in that video and me are the same person.

Everything I’m saying here in this thread is contained in that short timespan of the video. Dropped inputs, doubling values depending on those inputs, background polling. All of it. Not sure how you missed it.

You can run your own tests to see how inputs are background polled at the Update intervals, even if you check them in FixedUpdate. The unity doc mentions this in a somewhat vague way, warning that “Note: Input flags are not reset until Update. You should make all the Input calls in the Update Loop.”

As mentioned in that video, that brings me back to messing with flags and the likely visual problem of setting rendering routines in the FixedUpdate. Again, frame data, animation and inputs are crucial to get 100% in sync in these types of combat systems.

That still doesn’t mean that the boost you’re seeing in your framerate will somehow affect your input on its own. On the contrary. You are responsible for your input, by handling it properly, regardless of framerate, unless it’s so low you cannot be responsible.

I mean, here’s a trivial example. You make a system that reacts discretely to some input (i.e. checks for button press and produces a jump). It works fine at 60 fps. Great.

Now you have a situation with twice as many reaction windows. You know for a fact that your deltaTime is twice lower, and you know the fps you’re system is running at double the rate. Sadly, your game doesn’t work properly when this is happening. Question is, how’s that somebody else’s problem and not yours?

Sure, if the underlying system introduces jitter that’s beyond you or if the input is dropped entirely and the whole thing feels clumsy because of it, yes, you might say well I need more information to be able to handle this, but in this case I’m telling you, you want an easy way out?

You don’t have to sample your input in FixedUpdate, do it in Update if you have a feeling that the flags aren’t ok etc. but you can definitely process the input there. When you process input you actuate stuff. If that stuff is actuated too early or too late, well why are you doing this? Check your state and filter accordingly.

You can for example introduce inputStateUpdate to Update, and then introduce inputStateProcessor to FixedUpdate. Your game will run perfectly independent from any shifts in framerate. You obviously need to tweak fixed delta time to your taste, but that’s one easy way to constrain the input rate.

Now if you don’t want to use FixedUpdate for some reason other than superstition, that’s okay, and then you are supposed to track the elapsed time, monitor actual FPS rate and process your input much more delicately. You can’t just set some target frame rate and expect it’ll forever be without any spike whatsoever, or do you really think all of this variable framerate exists because someone thought it was funny?

Anyway, I’m going to pull back from this one, maybe you will listen to someone else.

You can also check whether the new Input System has some provisions in place that might help you.

edit:
And don’t get me wrong there is a lot of depth to what you’re trying to accomplish, it’s a complex topic, and I don’t think it’s wrong per se. I just fail to see how any of this is not your responsibility, maybe you should first try and fix why you’re having such a great FPS on your system when you tell it to cap to 60 Hz and try to understand that first.

Your monitor shouldn’t pull more frames from your drivers. The drivers should be the king, and the monitor can wait (or if it can’t I’m all ears to any details why not). And there should be a way to force the driver behavior from Unity, at least in theory. I just don’t know much about that part.