Skip to content

Instantly share code, notes, and snippets.

@sinbad
Last active September 15, 2024 15:08
Show Gist options
  • Save sinbad/4a9ded6b00cf6063c36a4837b15df969 to your computer and use it in GitHub Desktop.
Save sinbad/4a9ded6b00cf6063c36a4837b15df969 to your computer and use it in GitHub Desktop.
Unity simple & fast light flicker script
using UnityEngine;
using System.Collections.Generic;
// Written by Steve Streeting 2017
// License: CC0 Public Domain http://creativecommons.org/publicdomain/zero/1.0/
/// <summary>
/// Component which will flicker a linked light while active by changing its
/// intensity between the min and max values given. The flickering can be
/// sharp or smoothed depending on the value of the smoothing parameter.
///
/// Just activate / deactivate this component as usual to pause / resume flicker
/// </summary>
public class LightFlickerEffect : MonoBehaviour {
[Tooltip("External light to flicker; you can leave this null if you attach script to a light")]
public new Light light;
[Tooltip("Minimum random light intensity")]
public float minIntensity = 0f;
[Tooltip("Maximum random light intensity")]
public float maxIntensity = 1f;
[Tooltip("How much to smooth out the randomness; lower values = sparks, higher = lantern")]
[Range(1, 50)]
public int smoothing = 5;
// Continuous average calculation via FIFO queue
// Saves us iterating every time we update, we just change by the delta
Queue<float> smoothQueue;
float lastSum = 0;
/// <summary>
/// Reset the randomness and start again. You usually don't need to call
/// this, deactivating/reactivating is usually fine but if you want a strict
/// restart you can do.
/// </summary>
public void Reset() {
smoothQueue.Clear();
lastSum = 0;
}
void Start() {
smoothQueue = new Queue<float>(smoothing);
// External or internal light?
if (light == null) {
light = GetComponent<Light>();
}
}
void Update() {
if (light == null)
return;
// pop off an item if too big
while (smoothQueue.Count >= smoothing) {
lastSum -= smoothQueue.Dequeue();
}
// Generate random new item, calculate new average
float newVal = Random.Range(minIntensity, maxIntensity);
smoothQueue.Enqueue(newVal);
lastSum += newVal;
// Calculate new smoothed average
light.intensity = lastSum / (float)smoothQueue.Count;
}
}
@saulrussell
Copy link

Thanks, Steve. Perfectly implemented!

@arthurmarquis
Copy link

Mind if I add slight movement to the light itself to add even more realism?

@sinbad
Copy link
Author

sinbad commented Jun 26, 2019

Mind if I add slight movement to the light itself to add even more realism?

Feel free to fork it to do whatever you want :)

@pourya0111
Copy link

Hello
How to add a emision value with independent min max intensity but the same flicker amount?
I mean light and a emission source (for example the lamp source itself in 3d world)

@arthurmarquis
Copy link

arthurmarquis commented Apr 26, 2020 via email

@arthurmarquis
Copy link

arthurmarquis commented Apr 27, 2020 via email

@pourya0111
Copy link

Just think of a parking sign that will flicker (maybe emission?) like the light

@ziplock9000
Copy link

While this does work, it completely tanks performance changing the lighting on every frame, especially if several are used.

@RockyGitHub
Copy link

This is awesome! Thank you so much :) It's perfect to apply to other things beyond just light.luminence too

@Angryman80
Copy link

Thank you!

@Kiranix
Copy link

Kiranix commented Jul 29, 2022

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

public class FlickerLight : MonoBehaviour
{
	Light myLight;

	float maxIntensity = 80.0f;
	float minIntensity = 20.0f;

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

	IEnumerator Flicker()
	{

		float t = 0.0f;
		float duration = Random.Range(0.06f, 0.3f);
		float currIntensity = myLight.intensity;
		float targetIntensity = (currIntensity > 50.0f) ? minIntensity : maxIntensity;
		float variation = Random.Range(-20.0f, 20.0f);
		targetIntensity += variation;

		while (t < duration)
                {
			myLight.intensity = Mathf.Lerp(currIntensity, targetIntensity, t / duration);
			t += Time.deltaTime;
			yield return null;
                }

		StartCoroutine(Flicker());
	}
}

@stadoblech
Copy link

stadoblech commented Sep 30, 2022

@Kiranix regarding your code... well... where do i began...
Using looped enumerators? Well... i mean, i can live with that (we all did this at some point) but its generally not good idea to have core update functionality in coroutines. Why not use update loop? Especially when you are relying on counting wait duration for your functionality?
But whats really problematic here is this code constant magic numbers. I mean... why? At least expose it for editor...
Also: what kind of scene are you using? Minimal light intensity 20? Little bit high dont you think?
But mainly: using time delta in coroutine? This is really not great idea. Like... really REALLY bad idea

@RockyGitHub
Copy link

It's not that bad. It's straightforward and gives an idea on a dissipating light. It does cause some garbage. Time.deltaTime is not a big deal in his use because he is yield return null, which is equal to waiting for the next Update() call.
There's no reason to hate on it so much??

@Kiranix
Copy link

Kiranix commented Sep 30, 2022

Hey @stadoblech, thanks a lot for your feedback, I'll take all that into account for future scripting.

About the magic numbers, it was a short quick script, so it was just making it simple to read first sight, I usually prefer that.

About the lighting intensity, I adjusted it for my scene to look as I wanted, that's also why the Magic numbers, just change them according to your scene/pipeline, expose in editor, etc.

And regarding the delta time in coroutine, I would like you to further explain why is that a really bad idea, cause I've used that a lot and I'm really interested on knowing why is that bad. How would you increment the timer "t" inside the while of a coroutine?

Thanks!

@LWART-STUDIO
Copy link

LWART-STUDIO commented Jun 29, 2023

I diсided do like this.

 public class FlickeringLight : MonoBehaviour
    {
        [Header("References")]
        [Tooltip("Light Source")]
        [SerializeField] private Light _light;
        [Tooltip("Emission renderer")]
        [SerializeField] private Renderer _renderer;
        [Tooltip("Material index if mesh have more then 1 material")]
        [SerializeField] private int _materialIndex = 0;
        [Space(3f)]
        [Header("Options")]
        [Tooltip("How much to smooth out the randomness; lower values = sparks, higher = lantern")]
        [Range(1, 50)]
        [SerializeField] private int _smoothing = 5;
        [Range(0, 50)]
        [Tooltip("Delay between iterations")]
        [SerializeField] private int _delay = 5;
        [Tooltip("Duration of iterations")]
        [SerializeField] private float _duration = 5;
        
        
        private float _maxIntensity;
        private float _minIntensity = 0;
        private Queue<float> _smoothQueue;
        private float _lastSum = 0;
        private float _colorIntensity;
        private Color _color;
        private float _factor;
        private Coroutine _flickCoroutine;
        private WaitForSeconds _seconds;
        public void Reset()
        {
            StopCoroutine(_flickCoroutine);
            _smoothQueue.Clear();
            _lastSum = 0;
        }
        private void Start()
        {
            _seconds = new WaitForSeconds(_delay);
            _color = _renderer.materials[_materialIndex].GetColor("_EmissionColor");
            _colorIntensity = (_color.r + _color.g + _color.b) / 3f;
            _maxIntensity = _light.intensity;
            _smoothQueue = new Queue<float>(_smoothing);
            _flickCoroutine = StartCoroutine(Flick());
        }

        private void DoFlick()
        {
            if (_light == null)
                return;

            // pop off an item if too big
            while (_smoothQueue.Count >= _smoothing)
            {
                _lastSum -= _smoothQueue.Dequeue();
            }

            // Generate random new item, calculate new average
            float newVal = Random.Range(_minIntensity, _maxIntensity);
            _smoothQueue.Enqueue(newVal);
            _lastSum += newVal;

            // Calculate new smoothed average
            _light.intensity = _lastSum / (float)_smoothQueue.Count;
            _factor = _light.intensity / _colorIntensity;
            _renderer.sharedMaterials[_materialIndex].SetColor("_EmissionColor",new Color(_color.r*_factor,_color.g*_factor,_color.b*_factor));
        }

        private IEnumerator Flick()
        {
            float t = 0.0f;
            yield return _seconds;
            while (t<_duration)
            {
                DoFlick();
                t += Time.deltaTime;
                yield return null;
            }
            _flickCoroutine  = StartCoroutine(Flick());
        }
Movie.003-1.mp4

@lucasmhdev
Copy link

Forked for 2D URP :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment