While loop and Mathf.Lerp

I’m currently having a problem using Mathf.Lerp within a while loop. Rather than gradually decreasing the value (in this case the spot angle of a light), it jumps straight to the minimum value.

If I remove the while loop, the value of the spot angle decreases as expected. Something about my loop makes it jump to the minimum value immediately, though. Any ideas? The relevant method is at the bottom of the class.

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

public class FlashLightSystem : MonoBehaviour
{
    [SerializeField] float lightDecay = 2f;
    [SerializeField] float spotAngleDecay = 1f;
    [SerializeField] float minAngle = 40f;

    Light myLight;

    private void Start()
    {
        myLight = GetComponent<Light>();
    }

    private void Update()
    {
        DescreaseLightIntensity();
        DecreaseLightAngle();
    }

    private void DescreaseLightIntensity()
    {
        myLight.intensity = Mathf.Lerp(myLight.intensity, myLight.intensity - lightDecay, Time.deltaTime);
    }

    private void DecreaseLightAngle()
    {
        while(myLight.spotAngle > minAngle)
        {
            myLight.spotAngle = Mathf.Lerp(myLight.spotAngle, myLight.spotAngle - spotAngleDecay, Time.deltaTime);
        }
    }

Your while loop is executed entirely in a single frame, that’s why it appears to be instant. It does actually do what you want, but in a fraction of a millisecond without any chance to render the interim results.

And that’s why it runs as expected without the loop: Update happens once per frame. There’s an internal game loop that calls all the Update functions of your active components. Once the all of the various processing steps for a frame are done, it can be taken to the next step: rendering. So you only want to do a single step per frame in order to have the visual effect that it takes a certain amount of time.

Thanks for the explanation. I’m still confused, though. How does it do it all within one frame? I’m explicitly stating that I want it to decrement by a certain value once per second.

Why does it take the spotAngle down to the min value within a single frame when I’m essentially saying “do this over a period of one second”? I think it’s going to take me a while to wrap my head around this.

the Time.delta time in the t value of the Mathf.Lerp, acts as a factor/percentage to decrease by, if you want to wait a frame you’ll need to make “DecreaseLightAngle()” as a coroutine and use
yield return new WaitForEndOfFrame();

or change the while to if, something like this:

if(myLight.spotAngle > minAngle)
{
    myLight.spotAngle = Mathf.Lerp(myLight.spotAngle, myLight.spotAngle - spotAngleDecay, Time.deltaTime);
}

the while loop will loop as long as the condition is met, it will execute the code inside and then goes back to the beginning of the loop and execute again, it will continue to the next lines of code, ie exit the loop, only once the condition is false.

if you use an if statement it will run the code in the if statement once and then move on, it will only come back to it in the next time the statement is called, in your case the next update call

I just changed it to an if. It works now.

So while Time.deltaTime means the time between the last two frames, the value it yields is being used a percentage? It often functions as “do this over 1 second” but that isn’t literally how it’s used? Sorry, I’m just trying to make sure I understand things properly.

This is how I understand it, this is in the context of the Mathf.Lerp.
Time.deltaTime is basically a float value, it is the time in seconds between the last 2 frames, usually it will be something like 0.15 or so, depending on the frame rate.
but don’t trust me ;), you can read it here
https://docs.unity3d.com/ScriptReference/Time-deltaTime.html

I’m not sure what do you mean by

but I guess what you mean is that it is often used when you want to decrease or increase a value over time
Lets say I have an “energy” value and something that uses this energy, and it uses x energy per second
the code will look like this

energy  -= energyUsagePerSecond * Time.deltaTime;

because the time that has passed from the last frame is not a second but only a portion of it I will multiply my energyUsagePerSecond by the time that has passed since the last frame

Yes, that’s what I mean. I think I’ve got it now.

Thanks for the insight. Helped me grasp it faster than I thought.