No*
* To a first approximation, nothing in games is "necessary". If your code compiles, you can ship it. Whether or not anyone thinks the game is any good is another question.
So, what is scaling by framerate or delta time good for?
It's good if your framerate can change. Obviously, if your framerate is constant, then replacing any framerate scaling with a suitably-chosen constant is equivalent. But there are multiple kinds of framerate change we might care about:
Runtime changes: many games render as fast as the hardware can support. That means your framerate can vary between devices, from minimum-spec up to cutting-edge hardware. It also means your framerate can fluctuate during play, if your game is currently processing a heavier-than-normal or lighter-than-normal scene, or if a background process temporarily hogs some of the device's resources.
In these situations, if you've made all your movement a constant each frame, then in scenarios where your framerate is super high your gameplay runs faster than intended. (Ever play an old DOS game on a modern PC, and everything's just a blur?) And in scenarios where your framerate drops, your gameplay becomes more sluggish.
This is bad for the player experience, making the game feel inconsistent, and it can even introduce unfairness in competitive games, by giving some players more reaction time or faster responsiveness than others.
Development changes: your framerate in the shipped game might well be a constant, but will it be the same constant as at the start of your project?
Say you were initially aiming for 60 fps, but it turns out by the time you implement all of your features, you can only support 30 fps on your target hardware, so you have to drop your framerate. If all your gameplay simulation uses hard-coded constants, now you have to go re-tune all of those magic numbers to try to get equivalent behaviour at 30 fps as you had at 60. The same goes if you made your game for 60 FPS, and now want to make a VR port at 90 or 120 fps to keep up with player head movement and minimize nausea.
If you'd coded all your features to scale correctly with the framerate / delta time, even if that value was a constant, now you have just one master constant to change in one place, and most of your tuning should adapt correctly to the new framerate - if you did your math right. Obviously you still need to test and spot-fix, but this can reduce weeks of refactoring to maybe just hours, depending on your scope.
I'd also argue that scaling by framerate / delta time has another useful outcome: it keeps your tuning parameters in intuitive, meaningful units.
position.x += velocity.x * deltaTime;
If position
is in tile units and deltaTime
is in seconds, then velocity
represents tiles per second. This is a nice clear number for a designer to reason about. If I want the player to move 3 tiles in a half-second dash, that's a velocity of 6.
Without the deltaTime
, then I have to mentally figure-in the framerate whenever I'm choosing a tuning value like this. For the tile example, well, if we're running at 60 fps, that 3-tile half-second dash is 0.1 tiles per frame. It takes a bit more mental math and imagination to picture the effect of that number.
Now, there are hazards to using a variable timestep in your game simulation. This introduces some non-determinism into your code. Two runs of your game with identical input might not produce exactly identical results, because of small variances in timing leading to differences in rounding, etc.
This can be a point of frustration during development, making it difficult to replicate, isolate, and fix bugs that rely on very precise timing. And it can lead to inconsistencies in play or exploits (eg. deliberately dropping the framerate until you can clip through walls because the timestep has gotten so large that a single movement step teleports you all the way from one side to the other, without ever colliding with the wall in-between)
For this reason, it's often recommended that games use a "fixed timestep" for gameplay simulation:
Each simulation step always uses the same, constant timestep (but you still scale by this constant delta time, so that your code and tuning all adapt correctly if you ever change the constant)
In order to keep up to the present moment, the game runs the simulation step as many times as necessary to account for the total time elapsed before this frame
In order to present a smooth result, any non-simulation-critical effects (eg. UI animations) are scaled to the actual, variable time delta of the current frame, and simulation outputs are interpolated or extrapolated to the appropriate intermediate state.
This gets us the consistency benefits of a constant framerate, with the flexibility, responsiveness, and scaling benefits of a variable framerate, letting our games run predictably on a range of devices under a range of conditions. I present a more in-depth motivation for this convention in this answer.
I've heard about limiting frame rate and that makes sense in a capping the top way, but is limiting it below also something that is done?
Yes. Fixed timestep game loops typically have a maximum number of times they'll attempt to step the simulation in a single frame. So if frames run longer than that, the simulation is allowed to fall behind real-time, so the gameplay can slow down - hopefully giving the device enough headroom to finish whatever heavy processing is hogging the resources, so the game can recover back to its target framerate later.
This avoids a "death spiral" where frames get longer and longer because we keep running more and more simulation steps to try to catch up to real-time on hardware that just can't go that fast.
But it still has the advantage that on more capable hardware, we can run the rendering loop as fast as we want above this minimum cut-off. 500 fps? Sure, if your video card can take it and your monitor can display it, go for it!
For example, I made a very simple demo where I take a sprite and have it bounce off the edges of the screen. When it reaches the edge the velocity reverses and it goes in the opposite direction. While the numbers show as correctly calculated, visually it's wrong, either rebounding early or beyond the edges of the screen.
It sounds like your code has a bug. Post a question about it, demonstrating a Minimal Complete Verifiable Example, and we can help you debug it to get correct behaviour at any framerate.