Search Unity

Smoothly increase light intensity over 2 seconds.

Discussion in 'Scripting' started by cristo, Aug 7, 2017.

  1. cristo

    cristo

    Joined:
    Dec 31, 2013
    Posts:
    265
    Hi, I've been trying to see how others have dealt with this topic, but I can't get it working. I just want a light's intensity to go from 0.0f to 0.46f smoothly over a period of 2 seconds.
    If anyone can please offer any insight I would be grateful.



    Code (CSharp):
    1. public float lightUp = 0.46f;
    2. public float timeSec = 2.0f;
    3.  
    4. void Start {
    5. Light.GetComponent<Light>().intensity = 0.0f;
    6. }
    7. void Update {
    8.  if (Vector3.Distance(Player.transform.position, NPC.transform.position) < playerDist)
    9.             {
    10. LightIntensity ();
    11. }
    12. }
    13.  
    14. void LightIntensity (){
    15. if (Light.GetComponent<Light>().intensity < lightUp)
    16.         {
    17.             Light.GetComponent<Light>().intensity += 0.1f  * timeSec * Time.deltaTime;
    18.         }
    19. }
     
  2. TSC

    TSC

    Joined:
    Oct 15, 2012
    Posts:
    271
    Now I'm not the best, but you may want to look up interpolation!
     
  3. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    825
    Something like this:
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. public class LightChanger()
    5. {
    6.     bool isChangingIntensity  = false;
    7.     bool maxIntensity         = false;
    8.     float increasedIntensity  = 0.46f;
    9.  
    10.     [SerializeField]
    11.     float intensityChangeSpeed = 2f;
    12.  
    13.     Light _lightToChange;
    14.     Light lightToChange
    15.     {
    16.         get
    17.         {
    18.             if(_lightToChange == null)
    19.             {
    20.                 _lightToChange = GetComponent<Light>();
    21.             }
    22.  
    23.             return _lightToChange;
    24.         }
    25.     }
    26.  
    27.     private void Update()
    28.     {
    29.         if(Input.GetKeyDown(KeyCode.Space))
    30.         {
    31.             isChangingIntensity = true;
    32.         }
    33.  
    34.         if(isChangingIntensity == true)
    35.         {
    36.             if(lightToChange.intensity < increasedIntensity)
    37.             {
    38.                 lightToChange.intensity += (intensityChangeSpeed * Time.deltaTime);
    39.                 lightToChange.intensity = Mathf.Clamp(lightToChange.intensity, 0f, increasedIntensity);
    40.             }
    41.             else
    42.             {
    43.                 isChangingIntensity = false;
    44.             }
    45.         }
    46.     }
    47. }
    Its hand written but you get the point.
    When something happens, change a bool to true, then if that bool is true, increment (or decrement) the intensity over time.
     
  4. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    My little take:

    use a coroutine.

    Code (csharp):
    1.     public IEnumerator FadeIntensity(Light light, float target, float nSeconds)
    2.     {
    3.         var current = light.intensity;
    4.         if (nSeconds <= 0) nSeconds = 1;
    5.         while (current != target)
    6.         {
    7.             current = Mathf.MoveTowards(current, target, Time.deltaTime / nSeconds);
    8.             light.intensity = current;
    9.             yield return 0;
    10.         }
    11.     }
     
    TaleOf4Gamers likes this.
  5. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
    I'd probably use a tweening engine. We use LeanTween in a lot of projects at work, so using it for something like this is fairly easy to setup. There is also Dotween. Both have free versions that work really well.
     
  6. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    825
    +1 to this - I forgot about Mathf.MoveTowards, a nice and simple piece of code.
    I would use yield return null instead though. Just because ive never seen yield return 0. Does that work?

    For the sake of learning or to not bulk up the project I would say just write it yourself. Unless you want to do tons of 'tweens'.
     
  7. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
    If it's just one thing, sure, add it yourself, but it's rare we use a single tween in an entire project.

    Always good to learn multiple ways of doing something!
     
    TaleOf4Gamers likes this.
  8. LeiaMAC

    LeiaMAC

    Joined:
    Oct 20, 2017
    Posts:
    5
    Hello!

    I have a question, what's the math behind Time.deltaTime / nSeconds? Why the result yields the value it has to move in a frame? I tried to make sense of it (I like to learn why, instead of using something because it just works) but I couldn't.

    Thank you!
     
  9. LeiaMAC

    LeiaMAC

    Joined:
    Oct 20, 2017
    Posts:
    5
    Ok, I think I get it. but I read here that the math behind MoveTowards is this:

    Code (CSharp):
    1. public static float MoveTowards(float current, float target, float maxDelta)
    2. {
    3.      if (Mathf.Abs(target - current) <= maxDelta)
    4.      {
    5.          return target;
    6.      }
    7.      return current + Mathf.Sign(target - current) * maxDelta;
    8. }
    so the third parameter in MoveTowards, maxDelta, is not the interpolation (as in Lerp) but the maximum change, and that's why I don't understand it well.

    Sorry if I get some things mixed up! I really should start revisiting some math.
     
  10. ADNCG

    ADNCG

    Joined:
    Jun 9, 2014
    Posts:
    994
    Aye, I stand corrected, I was wrong above. Deleted post to avoid misleading people.

    The difference between Mathf.Lerp and Mathf.MoveTowards is that in MoveTowards, maxDelta is treated like the value to add to the current completion rather than the percentage of completion between the start and the finish values.

    Time.DeltaTime / nSeconds is basically like saying you should add (time elapsed since the last frame / the amount of time we want the completion to take) to the current progress.

    After nSeconds's worth of Time.deltaTime has elapsed, we will arrive at a 1/1 ratio and the completion will be finished (if our target is 1).
    Code (CSharp):
    1.  
    2. void RaiseLightIntensity (float lightIntensity)
    3. {
    4.     //Lets say the target intensity is 3
    5.     //Lets pretend we are running at a stable 60 fps
    6.     //Lets say we want to do this over 4 seconds
    7.     float target = 3f;
    8.     float nSeconds = 4f;
    9.  
    10.     //In theory, time.deltaTime should always be around 0.016667f (1 second / 60 FPS)
    11.     //If we pass 0.0166667f (or Time.deltaTime) as a maxDelta value
    12.     //It will take 60 frames (60 x 0.0166667) to reach 1.
    13.  
    14.     //Since we want to do it over 4 seconds (240 frames), we divide Time.deltaTime by 4.
    15.     //If we pass 0.0041667f (or Time.deltaTime / 4f) as a maxDelta value
    16.     //It will take 240 frames to reach 1.
    17.     //That'd be cool, IF we wanted to reach 1. Our target is 3.
    18.  
    19.     //Since we don't want to reach 1, but actually 3, we have to multiply by target
    20.     //If we pass 0.0125f (Time.deltaTime / nSeconds * target) as the maxDelta value
    21.     //It will take 240 frames to reach our target (3), rather than the initial 1
    22.     float maxDelta = Time.deltaTime / nSeconds * target;
    23.     lightIntensity = MoveTowards (lightIntensity, target, maxDelta);
    24.     //lightIntensity will return 0.0125f after the first frame
    25.     //0.025f after the second, and this will go on until 3 is reached (240 frames)
    26. }
    27.  
    Is this making anymore sense? Perhaps this is clearer?
     
    Last edited: Oct 23, 2017
    LeiaMAC likes this.
  11. LeiaMAC

    LeiaMAC

    Joined:
    Oct 20, 2017
    Posts:
    5
    Yes! I get it now. Thank you! :D

    So, with this variables:
    Code (CSharp):
    1. float initial = 2f;
    2. float target = 5f;
    3. float nSeconds = 4f;
    It could be this
    Code (CSharp):
    1. initial = Mathf.Lerp(initial, target, Time.deltaTime / nSeconds);
    or this
    Code (csharp):
    1. initial = Mathf.MoveTowards(initial, target, Time.deltaTime / nSeconds * Mathf.Abs(target - initial) );
    is that right?
     
  12. ADNCG

    ADNCG

    Joined:
    Jun 9, 2014
    Posts:
    994
    The first one not quite! Since lerp returns a percentage of the distance between a and b, it will never reach b the way it's setup, and will feel like it's smoothing because the increment gained will be worth less and less every frames.

    Here's a post from Eric5h5 explaining why your current approach won't work
    http://answers.unity3d.com/questions/14288/can-someone-explain-how-using-timedeltatime-as-t-i.html
    Here's a kickass article about lerping (Definitely a must-read in my opinion)
    https://chicounity3d.wordpress.com/2014/05/23/how-to-lerp-like-a-pro/

    Here's a different approach on how to lerp over a given amount of time.
    Code (CSharp):
    1. IEnumerator CallBatman (Light batmanSpotlight)
    2. {
    3.     float baseValue = 2f;
    4.     float targetValue = 4f;
    5.     float duration = 1.5f;
    6.     float timeStamp = Time.time;
    7.     while (Time.time < timeStamp + duration)
    8.     {
    9.         //(Actual time - Start time) / duration
    10.         float t = (Time.time - timeStamp) / duration;
    11.         batmanSpotlight.intensity = Mathf.Lerp (baseValue, targetValue, t);
    12.         yield return null;
    13.     }
    14.     batmanSpotlight.intensity = targetValue;
    15. }
    Second one has a similar mistake as well. Since initial is incremented every frames, Mathf.Abs (target-initial) won't remain "3" for the whole duration of the movement. It needs to remain 3 since that's how it provides you with the correct constant increments.
    Code (CSharp):
    1. float totalDistance = Mathf.Abs (target - initial);
    2. while (initial < target)
    3. {
    4.     initial = Mathf.MoveTowards(initial, target, Time.deltaTime / nSeconds * totalDistance));
    5.     yield return null;
    6. }
     
  13. LeiaMAC

    LeiaMAC

    Joined:
    Oct 20, 2017
    Posts:
    5
    I totally get it now, the article about lerp was really informative. And I don't know why I didn't see the mistake in the second one! I believe the lesson is "Always write the full code" :p

    Thank you very much! :D