176 lines
5.4 KiB
C#
176 lines
5.4 KiB
C#
using System;
|
|
using System.Threading;
|
|
using KitsuneCafe.Extension;
|
|
using KitsuneCafe.Sys;
|
|
using KitsuneCafe.Sys.Attributes;
|
|
using R3;
|
|
using UnityEngine;
|
|
using Unit = R3.Unit;
|
|
|
|
namespace KitsuneCafe.Event
|
|
{
|
|
public class FlickeringLight : MonoBehaviour
|
|
{
|
|
private const string EmissionColor = "_EmissionColor";
|
|
|
|
[Header("References")]
|
|
[Tooltip("Light Source")]
|
|
[SerializeField]
|
|
private new Light light;
|
|
|
|
[SerializeField]
|
|
private bool hasEmitter = false;
|
|
|
|
[Tooltip("Emission renderer")]
|
|
[DrawIf(nameof(hasEmitter), true)]
|
|
[SerializeField]
|
|
private Renderer emitter;
|
|
|
|
[Tooltip("Material index if mesh have more then 1 material")]
|
|
[DrawIf(nameof(hasEmitter), true)]
|
|
[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;
|
|
|
|
[SerializeField]
|
|
private bool autostart = true;
|
|
|
|
[SerializeField, DrawIf(nameof(autostart), true)]
|
|
private bool loop = true;
|
|
|
|
[Tooltip("Initial delay")]
|
|
[SerializeField]
|
|
private Duration delay = TimeSpan.Zero;
|
|
|
|
[Tooltip("Delay between iterations")]
|
|
[SerializeField]
|
|
private Duration interval = TimeSpan.FromMilliseconds(5);
|
|
|
|
[Tooltip("Duration of iterations")]
|
|
[SerializeField]
|
|
private Duration duration = TimeSpan.FromMilliseconds(5);
|
|
|
|
private float maxIntensity;
|
|
private float minIntensity = 0;
|
|
private ExponentialMovingAverage ema;
|
|
private float colorIntensity;
|
|
private Color color;
|
|
private float factor;
|
|
private Material material;
|
|
|
|
private CancellationTokenSource disableCancellationTokenSource;
|
|
private CancellationToken disableCancellationToken => disableCancellationTokenSource.Token;
|
|
|
|
private void Awake()
|
|
{
|
|
if (hasEmitter && emitter != null)
|
|
{
|
|
material = emitter.sharedMaterials[materialIndex];
|
|
color = emitter.materials[materialIndex].GetColor(EmissionColor);
|
|
colorIntensity = (color.r + color.g + color.b) / 3f;
|
|
}
|
|
|
|
maxIntensity = light.intensity;
|
|
ema = new(smoothing);
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
disableCancellationTokenSource = new();
|
|
|
|
if (light == null || !autostart)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (loop) { LoopFlicker(interval, duration, delay); }
|
|
else { Flicker(); }
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
disableCancellationTokenSource?.Cancel();
|
|
light.intensity = maxIntensity;
|
|
}
|
|
|
|
public IDisposable LoopFlicker() =>
|
|
LoopFlicker(interval, duration);
|
|
|
|
public IDisposable LoopFlicker(TimeSpan interval, TimeSpan duration) =>
|
|
LoopFlicker(interval, duration, UnityFrameProvider.Update);
|
|
|
|
public IDisposable LoopFlicker(TimeSpan interval, TimeSpan duration, TimeSpan delay) =>
|
|
LoopFlicker(interval, duration, delay, UnityFrameProvider.Update);
|
|
|
|
public IDisposable LoopFlicker(TimeSpan interval, TimeSpan duration, FrameProvider frameProvider) =>
|
|
LoopFlicker(delay, interval, duration, frameProvider, disableCancellationToken);
|
|
|
|
public IDisposable LoopFlicker(TimeSpan interval, TimeSpan duration, TimeSpan delay, FrameProvider frameProvider) =>
|
|
LoopFlicker(interval, duration, delay, frameProvider, disableCancellationToken);
|
|
|
|
public IDisposable LoopFlicker(TimeSpan interval, TimeSpan duration, TimeSpan delay, FrameProvider frameProvider, CancellationToken token) =>
|
|
Observable.Timer(delay, token)
|
|
.SelectSwitch(_ => Observable.Interval(interval, token)
|
|
.Merge(Observable.Return(Unit.Default))
|
|
.SelectMany(_ => Duration(duration, frameProvider, token))
|
|
)
|
|
.Subscribe(
|
|
_ => Tick(),
|
|
onCompleted: SetMaxIntensity
|
|
)
|
|
.RegisterTo(disableCancellationToken);
|
|
|
|
public IDisposable Flicker() => Flicker(duration);
|
|
|
|
public IDisposable Flicker(TimeSpan duration) =>
|
|
Flicker(duration, UnityFrameProvider.Update);
|
|
|
|
public IDisposable Flicker(TimeSpan duration, FrameProvider frameProvider) =>
|
|
Flicker(duration, frameProvider, disableCancellationToken);
|
|
|
|
public IDisposable Flicker(TimeSpan duration, FrameProvider frameProvider, CancellationToken token) =>
|
|
Duration(duration, frameProvider, token)
|
|
.Subscribe(
|
|
_ => Tick(),
|
|
onCompleted: SetMaxIntensity
|
|
)
|
|
.RegisterTo(disableCancellationToken);
|
|
|
|
private Observable<float> Duration(TimeSpan duration) =>
|
|
Duration(duration, UnityFrameProvider.Update);
|
|
|
|
private Observable<float> Duration(TimeSpan duration, FrameProvider frameProvider) =>
|
|
Duration(duration, frameProvider, disableCancellationToken);
|
|
|
|
private Observable<float> Duration(TimeSpan duration, FrameProvider frameProvider, CancellationToken token) =>
|
|
Observable.EveryUpdate(frameProvider, token)
|
|
.Select(_ => Time.deltaTime)
|
|
.Scan((acc, dt) => acc + dt)
|
|
.TakeUntil(t => t >= duration.TotalSeconds);
|
|
|
|
private void SetMaxIntensity(R3.Result result)
|
|
{
|
|
light.intensity = maxIntensity;
|
|
}
|
|
|
|
private void Tick()
|
|
{
|
|
light.intensity = ema.NextValue(UnityEngine.Random.Range(minIntensity, maxIntensity));
|
|
factor = light.intensity / colorIntensity;
|
|
|
|
if (hasEmitter && emitter != null)
|
|
{
|
|
material.SetColor(
|
|
EmissionColor,
|
|
new Color(color.r * factor, color.g * factor, color.b * factor)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|