- Home /
Once for all... is Time.deltaTime good or bad with Input.GetAxis?
Hello guys, I'd like to ask something that always bugged me out. I have made a deep search on here and other forums and it seems I collected a fair amount of different opinions about it. Should I use Time.deltaTime when dealing with Input.GetAxis in FixedUpdate(), applied to rigidbody? Specifically, I use mouse (or controller's left analog stick) to rotate my spaceship (rigidbody) through AddRelativeTorque. What I really want is a framerate independent amount of rotation. Honestly, I tried to build my game both ways (with and without Time.deltaTime) and both versions seem to act a bit differently depending on the PC they are running on. I tried 3 computers: 1- the one I use for gaming and Unity (obviously the fastest one); 2- a laptop with a weak but dedicated GPU (MX130); 3- office PC, very slow and equipped with an integrated Intel HD 620. On the slowest one, where the framerate is ridicolously bad, the ship turns faster and it seems to happen with both versions. I'm sure that the different mouse sensitivity between the 3 computers also matters, but the problem seems a bit deeper than just a sensitivity setting. Even on laptop, the ship turns a bit faster than the counterpart on gaming PC. Here's a snippet of my code:
if (Input.GetAxis("Mouse X") != 0f)
{
rb.AddRelativeTorque(0f, Input.GetAxis("Mouse X") * stats.handling * Time.deltaTime, 0f);
}
And the controller version:
if (Input.GetAxis("Controller Left Stick Horizontal") != 0f)
{
rb.AddRelativeTorque(0f, Input.GetAxis("Controller Left Stick Horizontal") * stats.handling * Time.deltaTime, 0f);
}
So, Time.deltaTime or not? Maybe no with mouse, and yes with controller? Or both yes/no? I know this is an old question, asked a million times on here and everywhere else, but in every answer there's something that leaves the door open to doubts. Thank you in advance for your help. :)
Answer by Eno-Khaon · Sep 30, 2021 at 07:16 PM
No, you don't need to multiply by Time.deltaTime in your use-case scenario.
Inputs are provided by the player on a frame-by-frame basis. They never need to be scaled based on framerate, since you don't want the same mouse movement to have an arbitrary effect in-game.
Something to note, however. If you want to "guarantee" inputs aren't missed by the difference between Update() (where inputs are read/registered) and FixedUpdate() (where you're going to be using them), you'll at least want to make an adjustment to your Mouse usage, but more on that in a moment.
Before we move on to the script example, the default way (the way you're using) of applying Physics forces is also framerate-independent already. So, once again, you don't need to multiply by Time.deltaTime in that scenario, either.
For clarification, you multiply changes intended to be used as a rate-over-time by Time.deltaTime, to take something which *should* be framerate-independent (and isn't already) and *make* it framerate-independent. For example, say you want to generate $1-per-second (or a similar concept). You can't guarantee framerate, so you want something you can't guarantee and which *should* be framerate-independent to be so.
currentMoney += 1f * Time.deltaTime; // This spreads "1" out over a second
So, getting back to your use-case scenario, here's an example of un-smoothing your inputs and not-double-dipping on framerate scaling for physics:
// This script example is intentionally different than yours
// here and there as an example of different presentation
Vector2 playerInput;
void Update()
{
if([using mouse])
{
// Gather input and combine it, in case framerate
// is drastically higher than physics update rate.
// This is necessary for mouse (and/or motion) input because
// it's not a fixed value range, but completely based on exact
// input provided by the player
playerInput += new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
}
else if([using controller])
{
// Due to the difference in input mechanism, you only
// need the most recent state of any
// non-motion-driven input. This includes keyboard,
// non-touch-pad controller inputs, etc.
playerInput = new Vector2(Input.GetAxis("Controller Left Stick Horizontal"), Input.GetAxis([etc]));
}
}
// For reference, Time.deltaTime, when called in FixedUpdate(), is
// automatically converted to/treated as Time.fixedDeltaTime
void FixedUpdate()
{
// Apply movements from accumulated input
// Rigidbody force application is already scaled to a per-physics-frame
// rate, so there's no need for another use of Time.fixedDeltaTime.
// Also, I use ForceMode.Acceleration as an example use-case
// that ignores mass, but you might want it factored in, depending
// on the type of game you're making (ForceMode.Force default)
rb.AddRelativeTorque(0f, playerInput.x * stats.handling, 0f, ForceMode.Acceleration);
// This usage case with mouse-input in mind:
// Now, we need to clear that accumulated input after any
// potential use in FixedUpdate(), so this should go at the
// very end of the function
playerInput = Vector2.zero;
}
Hopefully, I've made clear-enough what the differences are between these, and why neither of them happen to need framerate-scale multiplication (Time.deltaTime).
As an additional note, if you intended to use a form of accumulated keyboard/controller input (easier on keyboard, in this case), such as moving a cursor by discrete grid panels with every key press, that would be a case to gather cumulative (+=) input (although also a much less likely time for it to actually need to be physics-driven).
Edit: One... no, two other notes. Since you were currently scaling by Time.fixedDeltaTime (by using Time.deltaTime in FixedUpdate()), your inputs will be 50x (Time.fixedDeltaTime = 0.02f by default) stronger than they previously were. If that's something to adjust in "stats.handling" or a new global modifier, so be it.
Second, for clarification, you saw greater mouse-input rotation on the weakest hardware due to having a lower framerate and, therefore, the fewest dropped inputs due to the difference in timing. If your mouse moved on one frame, didn't on the next, and THEN FixedUpdate() cycled, you were registering 0 input by dumb luck.
Thank you very much for the very detailed answer. It will surely help anyone else who will get stuck with this problem/doubt. Kudos!