using UnityEngine;
using UnityEngine.Rendering.Universal;
namespace RR.Common.Entities
{
///
/// Управляет режимами анимации света (мигание, пульс, шум, кривая) для 2D/3D источников и эмиссивных материалов.
///
public class LightActionsController : MonoBehaviour
{
private enum Mode { Blink, Pulse, Noise, Curve }
[Header("Target")]
[Tooltip("Список 3D-источников света, на которые будет применяться модуляция интенсивности/цвета.")]
[SerializeField] private Light[] lights3D;
[Tooltip("2D-источники URP, подконтрольные этому скрипту.")]
[SerializeField] private Light2D[] lights2D;
[Tooltip("Опционально: Renderer с материалом, чью эмиссию нужно анимировать.")]
[SerializeField] private Renderer emissiveRenderer;
private static readonly int EmissionColorID = Shader.PropertyToID("_EmissionColor");
[Header("Mode")]
[SerializeField] private Mode mode = Mode.Blink;
[Header("Intensity")]
[SerializeField] private float minIntensity = 0f;
[SerializeField] private float maxIntensity = 1f;
[Header("Timings")]
[SerializeField] private float frequency = 2f; // Гц для Pulse/Noise, миганий/сек для Blink
[SerializeField, Range(0f, 1f)] private float duty = 0.2f; // Доля времени ON для Blink [0..1]
[SerializeField] private AnimationCurve curve = AnimationCurve.EaseInOut(0, 0, 1, 1);
[SerializeField] private bool useUnscaledTime = false;
[Header("Randomness (optional)")]
[SerializeField, Range(0f, 1f)] private float noiseAmplitude = 0.3f; // для Noise (0..1)
[SerializeField] private float noiseSpeed = 1.0f; // скорость «пламени»
[SerializeField] private Vector2 blinkJitter = new(-0.1f, 0.1f); // случайный сдвиг фазы Blink
[Header("Color (optional)")]
[SerializeField] private bool modulateColor = false;
[SerializeField] private Gradient colorOverCycle;
private float _blinkOffset;
private Material _matInstance;
private Color _baseEmission = Color.white;
///
/// Подбирает источники света в дочерних объектах при сбросе компонента.
///
private void Reset()
{
lights3D = GetComponentsInChildren();
lights2D = GetComponentsInChildren();
emissiveRenderer = GetComponent();
}
///
/// Кэширует экземпляр материала и рассчитывает индивидуальный сдвиг для режима мигания.
///
private void Awake()
{
if (emissiveRenderer && emissiveRenderer.sharedMaterial && _matInstance == null)
{
_matInstance = emissiveRenderer.material;
if (_matInstance.HasProperty(EmissionColorID))
{
_baseEmission = _matInstance.GetColor(EmissionColorID);
_matInstance.EnableKeyword("_EMISSION");
}
}
_blinkOffset = Random.Range(blinkJitter.x, blinkJitter.y);
}
///
/// Каждый кадр пересчитывает активный режим анимации и применяет интенсивность/цвет.
///
private void Update()
{
float time = useUnscaledTime ? Time.unscaledTime : Time.time;
float phase;
float k; // 0..1 коэффициент заполнения цикла
switch (mode)
{
case Mode.Blink:
// зубчатая волна с джиттером по фазе
phase = Mathf.Repeat((time + _blinkOffset) * Mathf.Max(0.0001f, frequency), 1f);
k = phase < Mathf.Clamp01(duty) ? 1f : 0f;
break;
case Mode.Pulse:
// синус, нормированный в диапазон 0..1
k = 0.5f * (1f + Mathf.Sin(time * Mathf.PI * 2f * frequency));
break;
case Mode.Noise:
// Перлин: базовый пульс + шумовая компонента
float u = time * noiseSpeed;
float n = Mathf.PerlinNoise(u, 0.123f); // 0..1
float baseK = 0.5f * (1f + Mathf.Sin(time * Mathf.PI * 2f * frequency)); // мягкий пульс
k = Mathf.Clamp01(baseK * (1f - noiseAmplitude) + n * noiseAmplitude);
break;
case Mode.Curve:
// нормализованный цикл 0..1, форма задаётся анимационной кривой
phase = Mathf.Repeat(time * Mathf.Max(0.0001f, frequency), 1f);
k = Mathf.Clamp01(curve.Evaluate(phase));
break;
default:
k = 1f;
break;
}
float intensity = Mathf.Lerp(minIntensity, maxIntensity, k);
ApplyIntensity(intensity);
if (modulateColor && colorOverCycle != null)
{
ApplyColor(colorOverCycle.Evaluate(k));
}
}
///
/// Применяет рассчитанную интенсивность ко всем источникам света и эмиссивному материалу.
///
private void ApplyIntensity(float value)
{
if (lights3D != null)
foreach (var t in lights3D)
if (t) t.intensity = value;
if (lights2D != null)
foreach (var t in lights2D)
if (t) t.intensity = value;
if (_matInstance && _matInstance.HasProperty(EmissionColorID))
_matInstance.SetColor(EmissionColorID, _baseEmission * value);
}
///
/// Модулирует цвет всех привязанных источников и эмиссии материала.
///
private void ApplyColor(Color c)
{
if (lights3D != null)
foreach (var t in lights3D)
if (t) t.color = c;
if (lights2D != null)
foreach (var t in lights2D)
if (t) t.color = c;
if (_matInstance && _matInstance.HasProperty(EmissionColorID))
_matInstance.SetColor(EmissionColorID, c * Mathf.Max(0.0001f, _matInstance.GetColor(EmissionColorID).maxColorComponent));
}
///
/// Освобождает созданный экземпляр материала, чтобы избежать утечек.
///
private void OnDestroy()
{
if (_matInstance) Destroy(_matInstance);
}
}
}