Scaling Object Using Lerp

transform.localScale = Vector3.Lerp(transform.localScale, transform.localScale * 2, Time.deltaTime * 10);
This is the code I am using. However, when I press key to increase scale, scale of object is increased suddenly. Lerp does not work. Any idea?

Lerp works. It just does not work as your want it to work.

The Lerp function is often used (abused) with deltaTime as third parameter to achieve a damping effect. Thought that might yield a acceptable result, that’s not how lerp is supposed to be used. The SmoothDamp (and its variants) function is more appropriate for this usage.

2 Likes

I understand what my problem is. I am using it in getkeydown and since it works for 1 frame my object is scaled instantly. How can I solve this problem?

You may want to try something like this:

    int scalingFramesLeft = 0;


    // Use this for initialization
    void Start ()
    {
    
    }

    // Update is called once per frame
    void Update ()
    {

        if (Input.GetKeyDown (KeyCode.S)) {
            scalingFramesLeft = 10;
        }


        if (scalingFramesLeft > 0) {
            transform.localScale = Vector3.Lerp (transform.localScale, transform.localScale * 2, Time.deltaTime * 10);
            scalingFramesLeft--;
        }



    }

I tried the code, and it does gradually scale the object over 10 frames.

https://www.youtube.com/watch?v=GT7-c6WtMPI

2 Likes

Thank you very much. Saved my life :slight_smile:

Another suggestion is to consider a tween system. There are several free ones on the asset store that work really well and will also allow for other options. I have used itween and leanTween. There is also a dotween that I’ve heard is really good.

Works great for things like this. Normally you can just pass in a start value, end value, and the time you want it to take to get from start to end.

1 Like

Confirming DOTween is a great tweening system. The API is neat and yet allows you to customize the tweening exactly how you want it.

Like ericbegue mentioned Lerp is often being misused :slight_smile: it can work though if you as last parameter add Time.deltaTime * something you will get value with ease out effect as it comes closer to target the increments will become less and less but just so you know if you use it like this it will never reach target value because each iteration will increase for less and less basically indefinitely.

If you wish to use Lerp as it is planned use last parameter as value from 0 to 1. 0 meaning start value 1 meaning end value and 0.5 meaning 50% bigger 1 meaning 100% bigger. And increment between 0 to 1 is left at your discretion.

Also there is another problem with your code:

transform.localScale = Vector3.Lerp(transform.localScale, transform.localScale * 2, Time.deltaTime * 10);

you are lerping by using localScale as start position and localScale * 2 as target so each pass you increase local scale and then use it as target scale again times 2 so it is kinda like endless loop of scaling since you are scaling start and end scale at same time.

I would suggest to cache your target scale so you scale toward independent vector value or at least have check to make sure that you reached the value you wish.

1 Like

Thank you very much for your help but my problem was different. I was trying to use lerp inside the GetKeyDown and since it works for one frame my script does not work. Now I have a different question:

if (scaleFrameLeft > 0)
{
scaleFrameLeft = 10;
}

if (scaleFrameLeft > 0)
{
o1.transform.localScale = Vector3.Lerp(o1.transform.localScale, o1.transform.localScale * 2, Time.deltaTime * 10);
scaleFrameLeft–;
}

As you can see I have to check everyframe whether scaleFrameLeft is greater than 0 or not and this is not a good practice. I want to know if I can create an external object which will be created when I press F and do the job above and will be destroyed after done the job? I hope I am clear.

I am not exactly sure what are you trying to achieve.

But if you wish to spawn an object you can make prefab and use Instantiate() to create object that will have script attached and perform what you want.

Take this extension method code and place it into your project:

public static class LerpHelpExtensions
    {
        public static Coroutine LerpCoroutine(this GameObject gameObj, float time, System.Action<float> block, bool includeZero = false)
        {
            MonoBehaviour behaviour = gameObj.GetComponent<MonoBehaviour> ();
            if ( behaviour == null )
                return null;
           
            return behaviour.LerpCoroutine (time, block, includeZero);
        }
       
       
        public static Coroutine LerpCoroutine(this MonoBehaviour behaviour, float time, System.Action<float> block, bool includeZero = false)
        {
            return behaviour.StartCoroutine (_LerpCoroutine (time, block, includeZero));
        }
       
        static IEnumerator _LerpCoroutine(float time, System.Action<float> block, bool includeZero = false)
        {
            if ( time <= 0f )
            {
                block(1f);
                yield break;
            }
           
            float timer = 0f;
            if ( includeZero )
            {
                block(0f);
                yield return null;
            }
           
            while ( timer < time )
            {
                timer += Time.deltaTime;
                block(Mathf.Lerp(0f, 1f, timer/time));
                yield return null;
            }
        }
       
        public static Coroutine AnimateComponent<T>(this MonoBehaviour behaviour, float time, System.Action<T,float> block) where T : Component
        {
            if ( block == null )
                return null;
           
            T component = behaviour.GetComponent<T> ();
            if ( component == null || !behaviour.gameObject.activeInHierarchy)
                return null;
           
            return behaviour.StartCoroutine (_LerpCoroutine(time, (timer)=>
            {
                block(component, timer);
            }));
           
        }
    }

Now, all you have to do to animate your component is:

public class MyScript : MonoBehaviour
{
     void Start ()
     {
          float duration = 0.16f;
          Vector3 startScale = transform.localScale;
          Vector3 endScale = startScale * 2f;

          this.AnimateComponent<Transform> (duration, (t, time) =>
          {
               t.localScale = Vector3.Lerp (startScale, endScale, time);
          });
     }

     // you can even put it inside a coroutine
     IEnumerator MyCoroutine()
     {
          float duration = 0.16f;
          Vector3 startScale = transform.localScale;
          Vector3 endScale = startScale * 2f;

          yield return this.AnimateComponent<Transform> (duration, (t, time) =>
          {
               t.localScale = Vector3.Lerp (startScale, endScale, time);
          });

          Debug.Log ("finished");
     }
}

Not only you can use this script to handle transform changes over time, but you can use it with any component, perhaps with uGUI components to change alpha value:

void Start()
{
     this.AnimateComponent<CanvasGroup>(0.5f, AnimateCanvas);
}

void AnimateCanvas(CanvasGroup group, float time)
{
     group.alpha = Mathf.Lerp(0f, 1f, time);
}

Hope this little script helps!

Without the technical details, what’s the goal you are trying to achieve?

  1. Scale the object to a target scale when the button is pressed.
  2. Scale the object as long as the button is down.
  3. Something else.

Hey, we actually created a video to cover this type of functionality that I will link to here:
https://www.youtube.com/watch?v=GB5mKBALeZw

This video covers lerp of scale over time so hopefully it helps. We used IEnumerators to actually complete the scale updates, which helps to make the lerp a little smoother.

2 Likes

May be my code can also help someone:
Scaling object over time

private void Start() {
  var scaleTo = new Vector(1.5f, 1.5f, 1.5f);
   StartCoroutine(ScaleOverSeconds(objectToScale, scaleTo, 1.0f);
}

public  IEnumerator ScaleOverSeconds(GameObject objectToScale, Vector3 scaleTo, float seconds)
        {
            float elapsedTime = 0;
            Vector3 startingScale = objectToScale.transform.localScale;
            while (elapsedTime < seconds)
            {
                objectToScale.transform.localScale = Vector3.Lerp(startingScale, scaleTo, (elapsedTime / seconds));
                elapsedTime += Time.deltaTime;
                yield return new WaitForEndOfFrame();
            }
            objectToScale.transform.position = scaleTo;
        }
2 Likes

Just need to change the last line to: objectToScale.transform.localScale = scaleTo; and then works nicely.

1 Like