Vector3.Lerp is stuttery and jittery when increasing value. Help Needed!

Hello! I am working on this game and I have discovered a bug in the crouching mechanics. I am using vector3.lerp to reduce the scale of the player in order to crouch (the player is a character controller with a hand attached to it so this is the easiest way). When the player crouches the movement is very smooth and silky (perfect!) but when he gets up it stutters horribly and overall just looks bad. See the crouch mechanic code below and any help is welcome!

if(Input.GetKey(KeyCode.LeftControl) && isGrounded)
        {
            if(!crouching)
                elapse1 = 0;
            speed = 1f;
            crouching = true;
           
            if(transform.localScale.y != .5f)
            {
                if (elapse1 < time_lerp)
                {
                    transform.localScale = Vector3.Lerp(transform.localScale, new Vector3(1,0.5f,1), elapse1/time_lerp);
                    elapse1 += 0.001f;
                }
                else
                {
                    transform.localScale = new Vector3(1,.5f,1);
                    elapse1 = 0;
                }
            }
            else
                elapse1 = 0;
               
        }
        else
        {
            if(!Physics.Raycast(transform.position, transform.up, out RaycastHit hit_, 1))
            {
                if(crouching)
                    elapse2 = 0;
                crouching = false;
                if(transform.localScale.y != 1f)
                {
                    if (elapse2 < time_lerp)
                    {
                        transform.localScale = Vector3.Lerp(transform.localScale, new Vector3(1,1,1), elapse2/time_lerp);
                        elapse2 += 0.001f;
                    }
                    else
                    {
                        transform.localScale = new Vector3(1,1,1);
                        elapse2 = 0;
                    }
                }
                else
                    elapse2 = 0;
            }

Instead of feeding back into Lerp, consider using it as it is intended:

Have two scales that you set and NEVER change:

  • standing scale
  • crouching scale

Have one alpha that controls moving between them smoothly.

Each frame, assign the scale based on lerping between standing and crouching with your “how crouched” variable.

For smoothly changing the third term (“how crouched”)m use this nice smooth pattern:

Smoothing movement between any two particular values:

https://discussions.unity.com/t/812925/5

You have currentQuantity and desiredQuantity.

  • only set desiredQuantity
  • the code always moves currentQuantity towards desiredQuantity
  • read currentQuantity for the smoothed value

Works for floats, Vectors, Colors, Quaternions, anything continuous or lerp-able.

The code: https://gist.github.com/kurtdekker/fb3c33ec6911a1d9bfcb23e9f62adac4

Hi! Thanks for the quick reply! I changed my code to the following:

if(Input.GetKey(KeyCode.LeftControl) && isGrounded)
        {
            if(!crouching)
                elapse1 = 0;
            speed = 1f;
            crouching = true;
            transform.localScale = Vector3.MoveTowards(transform.localScale, new Vector3(1,0.5f,1), Time.deltaTime * 2);
             
        }
        else
        {
            if(!Physics.Raycast(transform.position, transform.up, out RaycastHit hit_, 1))
            {
                if(crouching)
                    elapse2 = 0;
                crouching = false;
                transform.localScale = Vector3.MoveTowards(transform.localScale, new Vector3(1,1,1), Time.deltaTime * 2);
            }
        }

The problem still is there. let me explain it visually. I cannot provide footage but i will show a scheme (see attached) :

7595104--942121--scheme.png

That’s not relevant to what I posted. You’re still feeding the last scale back into the lerp.

transform.localScale = Vector3.MoveTowards(transform.localScale ...

Re-read what I suggested. I’m not sure how to reason about Lerp when you’re feeding it back into itself.

okay i’ll reread and modify the code. i’ll reply soon. thanks

Let’s simplify things, you will need to refactor your code, it’s a big mess.

For starters, let’s make it more readable, make two “constant” Vector3s:

private readonly Vector3 _crouching = new Vector3(1,0.5f,1);
private readonly Vector3 _standing = new Vector3(1,1,1);

Then replace the 12th line t this:
transform.localScale = Vector3.Lerp(_standing, _crouching, elapse1/time_lerp);
and the 36th to this:
transform.localScale = Vector3.Lerp(_crouching, _standing, elapse2/time_lerp);

Hi, I tried this, the problem remains. When the player crouches it’s smooth and nice, only when he gets up the movement is stuttery. Also this gameobject is the parent of the maincamera. Can it be that?

Don’t ask us… rip the camera off the player and try crouching and uncrouching.

nope, it’s not that

it’s like the movement is only one way because only when he stands up it is stuttery.

Okay so update. I managed to fix it by using vector3.smoothdamp but the function seems to not finish (stuck at 0.99999 and around there) and is constantly changing the velocity which does not allow my character to jump. How can I fix it?

Current code:

if(Input.GetKey(KeyCode.LeftControl) && isGrounded)
        {
            speed = 1f;
            if(!crouching)
                crouching = true;
                //transform.localScale = new Vector3(1,Mathf.MoveTowards(transform.localScale.y, 0.5f, Time.deltaTime * 2),1);
                transform.localScale = Vector3.SmoothDamp(transform.localScale, _crouching, ref velocity, 0.1f);
               
        }
        else
        {
            if(!Physics.Raycast(transform.position, transform.up, out RaycastHit hit_, 1))
            {
                if(crouching)
                crouching = false;
                //transform.localScale = new Vector3(1,Mathf.MoveTowards(transform.localScale.y, 1f, Time.deltaTime * 2),1);
                transform.localScale = Vector3.SmoothDamp(transform.localScale, _standing, ref velocity, 0.1f);
            }
        }

Also, is it actually stuttery or is there just a snap when he stands? The various methods suggested here except for your initial one should not stutter, so I’m wondering maybe you mean something else?

Could you maybe post a video or gif of the issue?

I would not suggest smoothdamp for this.

I stand by my original suggestion.

See enclosed package. Supports toggle button or hold-to-crouch (see code).

Note how it does precisely what my first post above says.

Script here, full package attached:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// @kurtdekker - ultra simple crouch sequence, smooth and reliable.
public class Croucher : MonoBehaviour
{
    public Transform ObjectToScale;

    Vector3 StandingScale = new Vector3( 1, 1, 1);
    Vector3 CrouchScale = new Vector3( 1, 0.5f, 1);

    // this tweens between the two above values
    float CurrentCrouchAlpha;

    // and this is how fast it tweens (alpha per second)
    float CrouchAlphaRate = 5.0f;

    bool crouched;

    // the ONLY output of this function should be to set the bool crouched
    void GatherInputToggle()
    {
        // simple toggle, use two keys if you want, or a held key, doesn't matter
        if (Input.GetKeyDown( KeyCode.C))
        {
            crouched = !crouched;
        }
    }

    void GatherInputHold()
    {
        // straight reads the C key into crouched
        crouched = Input.GetKey( KeyCode.C);
    }

    void ProcessCrouching()
    {
        // determine the alpha (terp between standing and crouched)
        float DesiredCrouchAlpha = 0.0f;    // means "standing"
        if (crouched)
        {
            DesiredCrouchAlpha = 1.0f;        // means "crouched"
        }

        // tween the alpha smoothly
        CurrentCrouchAlpha = Mathf.MoveTowards(
            CurrentCrouchAlpha,
            DesiredCrouchAlpha,
            CrouchAlphaRate * Time.deltaTime);

        // drive the scale appropriately.
        ObjectToScale.localScale = Vector3.Lerp(
            StandingScale,            // alpha of 0.0
            CrouchScale,            // alpha of 1.0
            CurrentCrouchAlpha);    // the alpha
    }

    void Update()
    {
        GatherInputToggle();

        // uncomment this next line if you want "hold to stay crouched.
        // GatherInputHold();

        ProcessCrouching();
    }
}

7595584–942169–Croucher.unitypackage (5.39 KB)

This fixed it! thank you!

1 Like