Strange Vector3.Lerp jittering

Hello, i was trying to make items in hands smoothly move with Vector3.Lerp and Quaternion.Lerp, thre is all the code for this

using UnityEngine;

public class SmoothHoldPosition : MonoBehaviour
{
    public float PositionHardness;
    public float RotationHardness;
    public GameObject HoldPosition;
    void Update()
    {
        transform.position = Vector3.Lerp(transform.position, HoldPosition.transform.position, PositionHardness * Time.deltaTime);
        transform.rotation = Quaternion.Lerp(transform.rotation, HoldPosition.transform.rotation, PositionHardness * Time.deltaTime);
    }
}

But Vector3.Lerp gives strange jittering effect.

(you can see a lot of little jittering overall and some big jumps like in 0:07 - 0:08)
I think i may be caused becouse of rigidbody player but i dont know how to fix it
6097398--662727--Issue.png
Also i noticed that jittering is only going on with position of the object, if i replace transform.position = Vector3.Lerp(transform.position, HoldPosition.transform.position, PositionHardness * Time.deltaTime); with transform.position = HoldPosition.transform.position; i will be able to see that rotation is always nice and smooth

You have mismatching update loops. Physics frames can occur 0, 1, 2, or more times in a given game frame (Update). So between any two Updates, your player object will move an inconsistent distance, because a different number of FixedUpdates will have happened. If your object is following a rigidbody in such a way that the distance from the rigidbody matters (as it does here), put its code in FixedUpdate.

1 Like

i already tried it, but the object starts to jitter even more

:eyes: Huh. That’s really bizarre.

Is there a rigidbody on the SmoothHoldPosition object?

No there is not, its just empty with a script

The third argument to Lerp must smoothly go from 0 to 1.

In line 10 and 11 you are multiplying it by Time.deltaTime before passing it in.

That cannot be correct.

Here is how to lerp correctly over any arbitrary time interval.

The Lerp could be any Lerp: Vector2, Vector3, float, Quaternion, doesn’t matter. Pay attention to the code that produces the fraction from 0.0f to 1.0f.

Thanks, but i want object to continually and smoothly move towards needed position with speed that depends on distance between the objects, and Lerp best suited for this for example if i use 0.5f as third argument object position will be just between its last position and nedded position, so if i do it each frame with Update function with each frame object will get closer to needet position by half (something like this)


But time between frames always different so i multiplying the third argument of Lerp function by Deltatime so it will get closer to end position by half not each frame, but each second

If i remove Time.deltaTime from Lerp function it will do just what i expect it to do but it will depend on fps what i dont want, i want it depend on time

If it’s in FixedUpdate then you don’t need Time.deltaTime, since it will get called a fixed number of occurrences over time.

Pardon me, I stand corrected. You are absolutely correct. I use that “lerp myself to there” mechanism all the time for smooth approaches/cameras. I misread what you were feeding in as the first arg, which is of course yourself

In this case I agree with StarManta, it is likely something related to order of execution or Update() vs FixedUpdate().

If you’re doing updating in FixedUpdate(), do camera in there, ideally as an explicit call to your own MyUpdate() function in the camera script, AFTER the move. Here’s the timing diagram:

https://docs.unity3d.com/Manual/ExecutionOrder.html

And keep in mind that each frame the order of scripts run can change.

No, i tried that too, but still get the same result as FixedUpdate with Time.deltaTime. The one way that i dont get jittering is if i do it in Update function without Time.deltaTme and it looks kinda like this

using UnityEngine;

public class SmoothHoldPosition : MonoBehaviour
{
    public float PositionHardness;
    public float RotationHardness;
    public Vector3 Velocity;
    public GameObject HoldPosition;
    void Update()
    {
        transform.position = Vector3.Lerp(transform.position, HoldPosition.transform.position, PositionHardness /* * Time.deltaTime*/);
        transform.rotation = Quaternion.Lerp(transform.rotation, HoldPosition.transform.rotation, RotationHardness /* * Time.deltaTime */);
    }
}

For now i probably will use Application.targetFrameRate to limit max framerate and also will try Kurt’s way but i still have no idea how to fix this issue

I was wrong even this way causing jittering…

I even tried that and it didnt work, same jitter result

        Vector3 Velosity = LastFramePlayerPosition - PlayerObj.transform.position;
        LastFramePlayerPosition = PlayerObj.transform.position;
        transform.position = HoldPosition.transform.position + (Velosity * PositionHardness);

Have you tried setting your character/object Rigidbody’s “Interpolate” setting to Interpolate? You could also try setting the Collision Detection on the Rigidbody to “Continuous Dynamic” or something like that.

Also, have you tried “lerping” both objects. Try moving the first object with linear interpolation, and the second follow object as well. So all objects are lerping.

To be fair, I’ve never actually gotten Unity to be “perfectly” smooth at anything. Even using linear interpolation in code and Rigidbody settings. But you can get pretty close.

Like people have been saying above this, usually placing your code in the FixedUpdate function tends to work, so that is a bit unexpected that it’s not for you.

  • Note: Also you could try checking/changing some of your physics settings generally (in the project settings). If all else fails, sometimes changing your FixedUpdate time (in time settings) is an option (but this can heavily affect performance in the rest of your game). Be sure to keep track of all of your settings so you can revert back to them if you need to, and/or save a copy of your project before messing with a ton of settings.

Did you test outside of the editor in an executable?

There is my Rigidbody and Physics settings I tried to adjust some of this but it doesnt do anything6101733--663627--Rigidbody.png 6101733--663630--Physics.png

I made test build and there were a little bit less jittering but it still pretty annoying

What’s the code that moves the rigidbody? Do you apply forces? Do you set the velocity? Do you set the regidbody’s position or its transform’s position? Does it use FixedUpdate or Update for movement of the target object?

Also note that with your current setup, the rigidbody necessarily needs the interpolation setting if the movement is driven by physics, because your camera is a child of it.

Detach your camera and check which one actually moves jittery, the target object, or the object’s that’s following. You should see jitter for one of them if there’s any at all. It’s not very unlikely that the target object is the one that actually experiences jitter. Having the camera as a child might fool you into thinking that everything else jitters.

It’s generally a good practice to move the camera in LateUpdate anyway.

Small side note: probably not the primary issue in your case because you’ve already tested the behaviour in a build, but still a good thing to know about - make sure you always test movements without having any of the objects selected in the hierarchy - the editor GUI and its overhead tends to make some object’s movement look jittery when they’re selected, because it updates the inspector very often.

Suddoha brings up a good point; there might be other physics forces causing that. He appears to be just lerping position which should not cause behavior he is seeing.

I move player with AddForce relative to HeadHolder object rotation in Fixed Update and Interpolation on.

    void FixedUpdate()
    {
        float AirMultiplier;
        if (IsGrounded())
        {
            AirMultiplier = 1f;
        }
        else
        {
            AirMultiplier = 0.1f;
        }
        Vector2 mag = FindVelRelativeToLook();
        float xMag = mag.x, yMag = mag.y;
        if (Math.Abs(mag.x) > threshold && Math.Abs(InputX) < 0.05f || (mag.x < -threshold && InputX > 0) || (mag.x > threshold && InputX < 0))
        {
            rb.AddForce(speed * HeadHolder.transform.right * Time.deltaTime * -mag.x * counterMovement * AirMultiplier);
        }
        if (Math.Abs(mag.y) > threshold && Math.Abs(InputY) < 0.05f || (mag.y < -threshold && InputY > 0) || (mag.y > threshold && InputY < 0))
        {
            rb.AddForce(speed * HeadHolder.transform.forward * Time.deltaTime * -mag.y * counterMovement * AirMultiplier);
        }
        if (Mathf.Sqrt((Mathf.Pow(rb.velocity.x, 2) + Mathf.Pow(rb.velocity.z, 2))) > maxSpeed)
        {
            float fallspeed = rb.velocity.y;
            Vector3 n = rb.velocity.normalized * maxSpeed;
            rb.velocity = new Vector3(n.x, fallspeed, n.z);
        }
        maxSpeed = InputmaxSpeed * (Input.GetAxisRaw("Sprint") * (SprintMultiplier - 1) + 1);
        /*
        //Square Limit
        if (InputX > 0 && xMag > maxSpeed) InputX = 0;
        if (InputX < 0 && xMag < -maxSpeed) InputX = 0;
        if (InputY > 0 && yMag > maxSpeed) InputY = 0;
        if (InputY < 0 && yMag < -maxSpeed) InputY = 0;
        */
        //Circle Limit
        if(rb.velocity.magnitude > maxSpeed)
        {
            if (xMag != 0) InputX = 0;
            if (yMag != 0) InputY = 0;
        }
        float DiagonalLimit = 1;
        if(Mathf.Abs(InputX) + Mathf.Abs(InputY) != 0) DiagonalLimit = 1 / (Mathf.Abs(InputX) + Mathf.Abs(InputY));
        rb.AddForce(HeadHolder.transform.forward * InputY * speed * Time.deltaTime * AirMultiplier * DiagonalLimit);
        rb.AddForce(HeadHolder.transform.right * InputX * speed * Time.deltaTime * AirMultiplier * DiagonalLimit);
    }

The only thing that may cause jittering is Velocity Limit in the end of the FixedUpdate, but velocity doesnt shake in inspector, and Player always moves smooth
There is all Player script

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;

public class Player: MonoBehaviour
{
    public GameObject HoldingPositionR;
    public GameObject HoldingPositionL;
    public static GameObject PlayerLink;
    public GameObject HoldingR;
    public GameObject HoldingL;
    public bool Dual;
    public GameObject HeadHolder;
    public GameObject Head;
    public GameObject Camera;
    private HeadHolder HeadHolderScript;
    private Head HeadScript;
    public float JumpForce;
    public float InputSpeed;
    private float speed;
    public float SprintMultiplier;
    public float counterMovement = 0.175f;
    public float Aspectr;
    private float threshold = 0.01f;
    float distToGround;
    public float InputmaxSpeed;
    float maxSpeed;
    float InputX, InputY;
    Rigidbody rb;
    private void Awake()
    {
        PlayerLink = gameObject;
    }
    private void Start()
    {
        ObjUpd();
        //Cursor.visible = false;
        //Cursor.lockState = CursorLockMode.Locked;
        rb = GetComponent<Rigidbody>();
        HeadHolderScript = HeadHolder.GetComponent<HeadHolder>();
        HeadScript = Head.GetComponent<Head>();
    }   
    void StartInteract()
    {
        RaycastHit RaycastHit;
        Ray CameraRay = new Ray(Camera.transform.position, Camera.transform.forward);
        Physics.Raycast(CameraRay, out RaycastHit);
        RaycastHit.transform.gameObject.SendMessage("Interact");
    }
    void Update()
    {
        InputX = Input.GetAxisRaw("Horizontal");
        InputY = Input.GetAxisRaw("Vertical");
        if (Input.GetButtonDown("Jump"))
        {
            Jump();
        }
        if (Input.GetMouseButtonDown(0) && HoldingR != null && !InventoryManager.Opened)
        {
            HoldingR.SendMessage("MainActionStart");
        }
        if (Input.GetMouseButtonUp(0) && HoldingR != null && !InventoryManager.Opened)
        {
            HoldingR.SendMessage("MainActionEnd");
        }
        if (Input.GetMouseButtonDown(1) && HoldingL != null && !InventoryManager.Opened && !Dual)
        {
            HoldingL.SendMessage("MainActionStart");
        }
        if (Input.GetMouseButtonUp(1) && HoldingL != null && !InventoryManager.Opened && !Dual)
        {
            HoldingL.SendMessage("MainActionEnd");
        }
        if (Input.GetKeyDown(KeyCode.F))
        {
            StartInteract();
        }
    }
    public Vector2 FindVelRelativeToLook()
    {
        float lookAngle = HeadHolder.transform.eulerAngles.y;
        float moveAngle = Mathf.Atan2(rb.velocity.x, rb.velocity.z) * Mathf.Rad2Deg;

        float u = Mathf.DeltaAngle(lookAngle, moveAngle);
        float v = 90 - u;

        float magnitue = rb.velocity.magnitude;
        float yMag = magnitue * Mathf.Cos(u * Mathf.Deg2Rad);
        float xMag = magnitue * Mathf.Cos(v * Mathf.Deg2Rad);

        return new Vector2(xMag, yMag);
    }
    void FixedUpdate()
    {
        float AirMultiplier;
        if (IsGrounded())
        {
            AirMultiplier = 1f;
        }
        else
        {
            AirMultiplier = 0.1f;
        }
        Vector2 mag = FindVelRelativeToLook();
        float xMag = mag.x, yMag = mag.y;
        if (Math.Abs(mag.x) > threshold && Math.Abs(InputX) < 0.05f || (mag.x < -threshold && InputX > 0) || (mag.x > threshold && InputX < 0))
        {
            rb.AddForce(speed * HeadHolder.transform.right * Time.deltaTime * -mag.x * counterMovement * AirMultiplier);
        }
        if (Math.Abs(mag.y) > threshold && Math.Abs(InputY) < 0.05f || (mag.y < -threshold && InputY > 0) || (mag.y > threshold && InputY < 0))
        {
            rb.AddForce(speed * HeadHolder.transform.forward * Time.deltaTime * -mag.y * counterMovement * AirMultiplier);
        }
        if (Mathf.Sqrt((Mathf.Pow(rb.velocity.x, 2) + Mathf.Pow(rb.velocity.z, 2))) > maxSpeed)
        {
            float fallspeed = rb.velocity.y;
            Vector3 n = rb.velocity.normalized * maxSpeed;
            rb.velocity = new Vector3(n.x, fallspeed, n.z);
        }
        maxSpeed = InputmaxSpeed * (Input.GetAxisRaw("Sprint") * (SprintMultiplier - 1) + 1);
        /*
        //Square Limit
        if (InputX > 0 && xMag > maxSpeed) InputX = 0;
        if (InputX < 0 && xMag < -maxSpeed) InputX = 0;
        if (InputY > 0 && yMag > maxSpeed) InputY = 0;
        if (InputY < 0 && yMag < -maxSpeed) InputY = 0;
        */
        //Circle Limit
        if(rb.velocity.magnitude > maxSpeed)
        {
            if (xMag != 0) InputX = 0;
            if (yMag != 0) InputY = 0;
        }
        float DiagonalLimit = 1;
        if(Mathf.Abs(InputX) + Mathf.Abs(InputY) != 0) DiagonalLimit = 1 / (Mathf.Abs(InputX) + Mathf.Abs(InputY));
        rb.AddForce(HeadHolder.transform.forward * InputY * speed * Time.deltaTime * AirMultiplier * DiagonalLimit);
        rb.AddForce(HeadHolder.transform.right * InputX * speed * Time.deltaTime * AirMultiplier * DiagonalLimit);
    }

    bool IsGrounded()
    {
        return Physics.Raycast(transform.position + new Vector3(-Aspectr, 0, 0), -Vector3.up, distToGround + 0.1f) || Physics.Raycast(transform.position + new Vector3(Aspectr, 0, 0), -Vector3.up, distToGround + 0.1f) || Physics.Raycast(transform.position + new Vector3(0, 0, -Aspectr), -Vector3.up, distToGround + 0.1f) || Physics.Raycast(transform.position + new Vector3(0, 0, Aspectr), -Vector3.up, distToGround + 0.1f);
    }
    public void ObjUpd()
    {
        distToGround = GetComponent<Collider>().bounds.extents.y;
        speed = InputSpeed * 1000;
    }
    public void Jump()
    {
        if (IsGrounded())
        {
            rb.AddForce(Vector3.up * JumpForce);
        }
    }
    public void Flick(Vector2 offset, Vector2 middle, Vector2 end, Vector2 time, Vector2 power)
    {
        StartCoroutine(HeadHolderScript.Flick(offset.x,middle.x,end.x,time.x,power.x));
        StartCoroutine(HeadScript.Flick(offset.y, middle.y, end.y, time.y, power.y));
    }
    public void FLickLinear(Vector2 time, Vector2 power)
    {
        StartCoroutine(HeadHolderScript.FlickLinear(time.x, power.x));
        StartCoroutine(HeadScript.FlickLinear(time.y, power.y));
    }
}

All the time camera was child of a player, if i detach camera it becomes hard to tell what jittering more and looks like both objects is shaking

https://www.youtube.com/watch?v=u4EUrhPIgaU