Search Unity

While loop and Mathf.Lerp

Discussion in 'Scripting' started by Clydey2Times, Dec 24, 2019.

  1. Clydey2Times

    Clydey2Times

    Joined:
    Oct 24, 2017
    Posts:
    242
    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.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class FlashLightSystem : MonoBehaviour
    6. {
    7.     [SerializeField] float lightDecay = 2f;
    8.     [SerializeField] float spotAngleDecay = 1f;
    9.     [SerializeField] float minAngle = 40f;
    10.  
    11.     Light myLight;
    12.  
    13.     private void Start()
    14.     {
    15.         myLight = GetComponent<Light>();
    16.     }
    17.  
    18.     private void Update()
    19.     {
    20.         DescreaseLightIntensity();
    21.         DecreaseLightAngle();
    22.     }
    23.  
    24.     private void DescreaseLightIntensity()
    25.     {
    26.         myLight.intensity = Mathf.Lerp(myLight.intensity, myLight.intensity - lightDecay, Time.deltaTime);
    27.     }
    28.  
    29.     private void DecreaseLightAngle()
    30.     {
    31.         while(myLight.spotAngle > minAngle)
    32.         {
    33.             myLight.spotAngle = Mathf.Lerp(myLight.spotAngle, myLight.spotAngle - spotAngleDecay, Time.deltaTime);
    34.         }
    35.     }
     
  2. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    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.
     
  3. Clydey2Times

    Clydey2Times

    Joined:
    Oct 24, 2017
    Posts:
    242
    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.
     
  4. NanushTol

    NanushTol

    Joined:
    Jan 9, 2018
    Posts:
    131
    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();
    https://docs.unity3d.com/ScriptReference/WaitForEndOfFrame.html

    or change the while to if, something like this:
    Code (CSharp):
    1. if(myLight.spotAngle > minAngle)
    2. {
    3.     myLight.spotAngle = Mathf.Lerp(myLight.spotAngle, myLight.spotAngle - spotAngleDecay, Time.deltaTime);
    4. }
    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
     
    Last edited: Dec 24, 2019
  5. Clydey2Times

    Clydey2Times

    Joined:
    Oct 24, 2017
    Posts:
    242
    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.
     
  6. NanushTol

    NanushTol

    Joined:
    Jan 9, 2018
    Posts:
    131
    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
    Code (CSharp):
    1. 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
     
    Last edited: Dec 24, 2019
  7. Clydey2Times

    Clydey2Times

    Joined:
    Oct 24, 2017
    Posts:
    242
    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.