Smoothly increase light intensity over 2 seconds.

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.

public float lightUp = 0.46f;
public float timeSec = 2.0f;

void Start {
Light.GetComponent<Light>().intensity = 0.0f;
}
void Update {
 if (Vector3.Distance(Player.transform.position, NPC.transform.position) < playerDist)
            {
LightIntensity ();
}
}

void LightIntensity (){
if (Light.GetComponent<Light>().intensity < lightUp)
        {
            Light.GetComponent<Light>().intensity += 0.1f  * timeSec * Time.deltaTime;
        }
}

Now I’m not the best, but you may want to look up interpolation!

Something like this:

using System;
using UnityEngine;

public class LightChanger()
{
    bool isChangingIntensity  = false;
    bool maxIntensity         = false;
    float increasedIntensity  = 0.46f;

    [SerializeField]
    float intensityChangeSpeed = 2f;

    Light _lightToChange;
    Light lightToChange
    {
        get
        {
            if(_lightToChange == null)
            {
                _lightToChange = GetComponent<Light>();
            }

            return _lightToChange;
        }
    }

    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space))
        {
            isChangingIntensity = true;
        }

        if(isChangingIntensity == true)
        {
            if(lightToChange.intensity < increasedIntensity)
            {
                lightToChange.intensity += (intensityChangeSpeed * Time.deltaTime);
                lightToChange.intensity = Mathf.Clamp(lightToChange.intensity, 0f, increasedIntensity);
            }
            else
            {
                isChangingIntensity = false;
            }
        }
    }
}

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.

My little take:

use a coroutine.

    public IEnumerator FadeIntensity(Light light, float target, float nSeconds)
    {
        var current = light.intensity;
        if (nSeconds <= 0) nSeconds = 1;
        while (current != target)
        {
            current = Mathf.MoveTowards(current, target, Time.deltaTime / nSeconds);
            light.intensity = current;
            yield return 0;
        }
    }
1 Like

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.

+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’.

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!

1 Like

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!

Ok, I think I get it. but I read here that the math behind MoveTowards is this:

public static float MoveTowards(float current, float target, float maxDelta)
{
     if (Mathf.Abs(target - current) <= maxDelta)
     {
         return target;
     }
     return current + Mathf.Sign(target - current) * maxDelta;
}

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.

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).

void RaiseLightIntensity (float lightIntensity)
{
    //Lets say the target intensity is 3
    //Lets pretend we are running at a stable 60 fps
    //Lets say we want to do this over 4 seconds
    float target = 3f;
    float nSeconds = 4f;

    //In theory, time.deltaTime should always be around 0.016667f (1 second / 60 FPS)
    //If we pass 0.0166667f (or Time.deltaTime) as a maxDelta value
    //It will take 60 frames (60 x 0.0166667) to reach 1.

    //Since we want to do it over 4 seconds (240 frames), we divide Time.deltaTime by 4.
    //If we pass 0.0041667f (or Time.deltaTime / 4f) as a maxDelta value
    //It will take 240 frames to reach 1.
    //That'd be cool, IF we wanted to reach 1. Our target is 3.

    //Since we don't want to reach 1, but actually 3, we have to multiply by target
    //If we pass 0.0125f (Time.deltaTime / nSeconds * target) as the maxDelta value
    //It will take 240 frames to reach our target (3), rather than the initial 1
    float maxDelta = Time.deltaTime / nSeconds * target;
    lightIntensity = MoveTowards (lightIntensity, target, maxDelta);
    //lightIntensity will return 0.0125f after the first frame
    //0.025f after the second, and this will go on until 3 is reached (240 frames)
}

Is this making anymore sense? Perhaps this is clearer?

1 Like

Yes! I get it now. Thank you! :smile:

So, with this variables:

float initial = 2f;
float target = 5f;
float nSeconds = 4f;

It could be this

initial = Mathf.Lerp(initial, target, Time.deltaTime / nSeconds);

or this

initial = Mathf.MoveTowards(initial, target, Time.deltaTime / nSeconds * Mathf.Abs(target - initial) );

is that right?

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

Here’s a kickass article about lerping (Definitely a must-read in my opinion)

Here’s a different approach on how to lerp over a given amount of time.

IEnumerator CallBatman (Light batmanSpotlight)
{
    float baseValue = 2f;
    float targetValue = 4f;
    float duration = 1.5f;
    float timeStamp = Time.time;
    while (Time.time < timeStamp + duration)
    {
        //(Actual time - Start time) / duration
        float t = (Time.time - timeStamp) / duration;
        batmanSpotlight.intensity = Mathf.Lerp (baseValue, targetValue, t);
        yield return null;
    }
    batmanSpotlight.intensity = targetValue;
}

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.

float totalDistance = Mathf.Abs (target - initial);
while (initial < target)
{
    initial = Mathf.MoveTowards(initial, target, Time.deltaTime / nSeconds * totalDistance));
    yield return null;
}

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” :stuck_out_tongue:

Thank you very much! :smile: