Time.delta problems!Are im using it wrong?

Hello Everybody,

i hvae my player script and i know that time.delta time makes code lines frame - independent. So my character should move frame independent. I Multiple all movement lines with * Time.delta time but it doesn work!!

Heres my code:

void Update()
    {
        if (!isDrone)
        {
            if (cc.isGrounded && canControl)
            {
                move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
                move = transform.TransformDirection(move);
                remainingEnergy += energyIncrease;
                move *= walkSpeed * Time.deltaTime;
                movementState = 0;
                movementInfo.text = "Movement state: Standing";
                //if the player is walking or crouching it does not cost any energy

                if (move.x != 0 && move.z != 0)
                {
                    movementState = 1;
                    step = 0.5f;
                    movementInfo.text = "Movement state: Walking";
                }



                if (Input.GetButton("Sprint") && canRun && remainingEnergy > sprintEnergyCost)
                {
                    move *= runSpeed * Time.deltaTime;
                    remainingEnergy -= sprintEnergyCost;
                    movementState = 2;
                    step = 0.3f;
                    movementInfo.text = "Movement state: Sprinting";
                }


                if (Input.GetButtonDown("Jump") && canJump && remainingEnergy > jumpEnergyCost)
                {
                    move.y += jumpSpeed * Time.deltaTime;
                    remainingEnergy -= jumpEnergyCost;
                    movementState = 3;
                    footstepsAudio.clip = Jump;
                    footstepsAudio.Play();
                    movementInfo.text = "Movement state: Jumping";


                }

                if (Input.GetButton("Crouch"))
                {
                    cc.height = 1f;
                    move /= crouchSpeed * Time.deltaTime;
                    movementState = 4;
                    step = 0.8f;
                    canRun = false;
                    movementInfo.text = "Movement state: Crouching";

                }

                else
                {
                    cc.height = 2;
                    canRun = true;
                }

            }
        }

        move.y -= gravity * Time.deltaTime;
        cc.Move(move * Time.deltaTime);

You can’t just slap Time.deltaTime on everything and it will magically fix things. Time.deltaTime’s value is simply a float number that represents the time in seconds the last frame took to calculate and render.
You can treat it like “1/s” or “per second”.

So when you say

cc.Move(move * Time.deltaTime);

then you can say “x meters into some direction” to “move”, making the whole statement “x meters into some direction per second”, which is correct.

The problem… well… one problem is that you already multiplied the movement direction with Time.deltaTime earlier. So move already is “x meters into some direction per second” at this point, changing the statement to “x meters into some direction per second per second”, or “x meters into some direction per second squared”. As known from physics class, “distance / time²” is not movement, but acceleration. However, CharacterController.Move should obviously get a movement vector and not an acceleration vector.

The only other place where an additional Time.deltaTime factor is correct is this one:

move.y -= gravity * Time.deltaTime;

Here, you are changing move.y over time, and that change, aka. that acceleration, should also be frame independent. So we have an acceleration here, which makes “1/time²”, or in other words

* Time.deltaTime * Time.deltaTime

correct.

Onto another problem. Removing all the faulty Time.deltaTimes is a good step in the right direction, but there is a general problem with using Update for literally anything that is not linear. Imagine this:

You have a ball that starts with a velocity in a specific upwards direction. The flight curve of the ball is exactly so that the flight takes one second in reality. Now, if you have 60 FPS, you might get the ball to land after pretty much one second when updating its vertical speed downwards due to gravity every time.

Imagine this scenario in an extreme case of 1 FPS. You start with the same velocity, but after one Update call the ball should have landed. Of course it hasn’t, because it didn’t change its direction between start and predicted end of the flight. After one second, it has reached a point somewhere above ground and then changes direction in order to probably land after the next second.

As you might imagine, this extreme case does not just happen exactly when we hit 1 FPS, but rather we get increasingly close to this wrong result the closer we get to 1 FPS. In other words: Even when multiplying Time.deltaTime onto the correct values, when using Update on things that are not linear, the results still vary with FPS. Imagine the same thing for something that constantly moves in the same direction at the same speed - no problem there.

So how to fix this? The simple answer is: Use FixedUpdate for anything that is relevant to your gameplay and not linear. For example, your character movement. FixedUpdate is way less magic than it first seems once you get behind it. Basically, the number of FixedUpdate calls per time is as constant as possible and when there’s too few calls, Unity calls it more often. On Average, FixedUpdate gets calles exactly once every 0.02 seconds or whatever you set as fixed timestep in the project’s time settings.
Unity does this by checking the time after each Update and when 0.02 seconds have past, it calls FixedUpdate once. If the Update took long and FixedUpdate is overdue multiple times, it calls FixedUpdate multiple times and subtracts 0.02 for each call. So FixedUpdate can get called multiple times, once, or even not at all between two Update calls.
So if you have 1 FPS, you get one Update call, but still 50 FixedUpdate calls. They are all at the end of that one second, but it’s still the same amount with 30, 60 or even 1000 FPS.

The clue: Time.deltaTime becomes that exact value in FixedUpdate. It becomes a constant.
And constants in calculations make constant results.

So instead of moving the ball/player X times in one second and multiplying the movement by 1/X, you use FixedUpdate, move it exactly 50 times per second and multiply your movement by the constant 0.02.

There’s just one tiny problem left: As I mentioned, FixedUpdate might not be called between two Updates. But the scene gets rendered after each Update. So you might have two frames in a row where the object doesn’t move and then it moves again. This means visible stuttering.

I could start to explain how to get around this but this post has become enough of a monster already, so I’ll just link you a script I wrote that fixes this: Interpolates a GameObject's position and rotation while being updated in FixedUpdate. · GitHub
Note the comment about Script Execution Order at the top and slap this script onto any objects you move by code in FixedUpdate. It’s not necessary for Rigidbody based movement as they can do that on their own.