peterHasemann
peterHasemann

Reputation: 1580

Increase and decrease light intensity overtime

I want to create some fireflies in Unity. I want to Increase light intensity then wait some seconds and then decrease it in Unity. When they get spawned, I want them increasing their light intensity, wait some seconds and then fade out. How can I create this "process" in a clean way?

private Light pointLight; // The light component of the firefly

private float minLuminosity = 0; // min intensity
private float maxLuminosity = 1; // max intensity

private float luminositySteps = 0.005f; // factor when increasing / decreasing

private float shineDuration = 3; // wait 3 seconds when faded in

private void Start()
{
    pointLight = GetComponent<Light>();
    pointLight.intensity = Random.Range(minLuminosity, maxLuminosity); // start with a random intensity
    StartCoroutine(ChangeIntensity()); // start the process
}

private IEnumerator ChangeIntensity()
{
    pointLight.intensity += luminositySteps; // increase the firefly intensity / fade in
    yield return new WaitWhile(() => pointLight.intensity >= maxLuminosity); // wait for the maximum intensity

    yield return new WaitForSeconds(shineDuration); // wait 3 seconds

    pointLight.intensity -= luminositySteps;
    yield return new WaitWhile(() => pointLight.intensity <= maxLuminosity); // wait for the minimum intensity

    StartCoroutine(ChangeIntensity()); // do it again
}

So obviously the coroutine stops forever at the first WaitWhile() How can I create such a code chain? When fading in or out, I just mean changing the light intensity.

Upvotes: 2

Views: 6430

Answers (3)

Programmer
Programmer

Reputation: 125275

Even though this has been solved, the current solutions are just decrementing the variable and also creates new object (WaitForSeconds) every frame.

The proper way of doing this in Unity is using Mathf.Lerp and Time.deltaTime. This type of operation is what Mathf.Lerp is made for which is to go from your minLuminosity to maxLuminosity. You can read more about this in my other question for fading out/in GameObject using its alpha component here.

I took the fadeInAndOut function from that answer and ported it to work with the Light component. Here is a simple light fade in/out function:

IEnumerator fadeInAndOut(Light lightToFade, bool fadeIn, float duration)
{
    float minLuminosity = 0; // min intensity
    float maxLuminosity = 1; // max intensity

    float counter = 0f;

    //Set Values depending on if fadeIn or fadeOut
    float a, b;

    if (fadeIn)
    {
        a = minLuminosity;
        b = maxLuminosity;
    }
    else
    {
        a = maxLuminosity;
        b = minLuminosity;
    }

    float currentIntensity = lightToFade.intensity;

    while (counter < duration)
    {
        counter += Time.deltaTime;

        lightToFade.intensity = Mathf.Lerp(a, b, counter / duration);

        yield return null;
    }
}

Now, to create the exact effect you want which is to increase light intensity then wait some seconds and then decrease it, create another coroutine function that calls the function above and waits for it to finish. You can do that by yielding the fadeInAndOut function. Notice how WaitForSeconds is declared outside the while loop so that it does not create new Object each time.

//Fade in and out forever
IEnumerator fadeInAndOutRepeat(Light lightToFade, float duration, float waitTime)
{
    WaitForSeconds waitForXSec = new WaitForSeconds(waitTime);

    while (true)
    {
        //Fade out
        yield return fadeInAndOut(lightToFade, false, duration);

        //Wait
        yield return waitForXSec;

        //Fade-in 
        yield return fadeInAndOut(lightToFade, true, duration);
    }
}

USAGE:

public Light lightToFade;
public float eachFadeTime = 2f;
public float fadeWaitTime = 5f;

void Start()
{
    StartCoroutine(fadeInAndOutRepeat(lightToFade, eachFadeTime, fadeWaitTime));
}

Upvotes: 5

Izuka
Izuka

Reputation: 2612

The problem in your code was that you applied your luminosity change only once, thus your WaitWhile condition would never be reached. I would change both WaitWhile into simple while loops, and then use WaitForEndOfFrame:

private IEnumerator ChangeIntensity()
{
    while(true)
    {
        while(pointLight.intensity <= maxLuminosity)
        {
            pointLight.intensity += luminositySteps; // increase the firefly intensity / fade in
            yield return new WaitForEndOfFrame();
        }

        yield return new WaitForSeconds(shineDuration); // wait 3 seconds

        while(pointLight.intensity > minLuminosity)
        {
            pointLight.intensity -= luminositySteps;
            yield return new WaitForEndOfFrame();
        }
    }
}

Upvotes: 2

peterHasemann
peterHasemann

Reputation: 1580

So I got it by using the following while loop

private IEnumerator ChangeIntensity()
{
    while (true)
    {
        pointLight.intensity += isIncreasing ? luminositySteps : -luminositySteps;

        if (pointLight.intensity <= minLuminosity)
            isIncreasing = true;

        if (pointLight.intensity >= maxLuminosity)
        {
            isIncreasing = false;
            yield return new WaitForSeconds(shineDuration);
        }

        yield return null;
    }
}

Upvotes: 0

Related Questions