257 lines
6.9 KiB
C#
257 lines
6.9 KiB
C#
using System;
|
|
using System.Threading;
|
|
using Cysharp.Threading.Tasks;
|
|
using PrimeTween;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace BriarQueen.Framework.Effects
|
|
{
|
|
[RequireComponent(typeof(Image))]
|
|
public class UILightGlow : MonoBehaviour
|
|
{
|
|
private const string _lightShaderName = "BriarQueen/UI/Light Glow";
|
|
|
|
private static readonly int _lightColorId = Shader.PropertyToID("_LightColor");
|
|
private static readonly int _intensityId = Shader.PropertyToID("_Intensity");
|
|
private static readonly int _flickerOffsetId = Shader.PropertyToID("_FlickerOffset");
|
|
|
|
[Header("References")]
|
|
[SerializeField]
|
|
private Image _image;
|
|
|
|
[SerializeField]
|
|
private Material _lightMaterialTemplate;
|
|
|
|
[Header("Light")]
|
|
[SerializeField]
|
|
private Color _startingColor = new(1f, 0.78f, 0.35f, 1f);
|
|
|
|
[SerializeField]
|
|
private float _startingIntensity = 1.5f;
|
|
|
|
[SerializeField]
|
|
private bool _randomizeFlickerOffset = true;
|
|
[SerializeField]
|
|
|
|
private bool _useStartingValues;
|
|
|
|
[Header("Tween")]
|
|
[SerializeField]
|
|
private float _defaultTweenDuration = 0.35f;
|
|
|
|
[SerializeField]
|
|
private Ease _ease = Ease.InOutSine;
|
|
|
|
[SerializeField]
|
|
private bool _useUnscaledTime = true;
|
|
|
|
private Material _runtimeMaterial;
|
|
private Sequence _lightSequence;
|
|
private CancellationTokenSource _lightCts;
|
|
|
|
public Color LightColor
|
|
{
|
|
get
|
|
{
|
|
if (_runtimeMaterial == null)
|
|
{
|
|
return _startingColor;
|
|
}
|
|
|
|
return _runtimeMaterial.GetColor(_lightColorId);
|
|
}
|
|
}
|
|
|
|
public float Intensity
|
|
{
|
|
get
|
|
{
|
|
if (_runtimeMaterial == null)
|
|
{
|
|
return _startingIntensity;
|
|
}
|
|
|
|
return _runtimeMaterial.GetFloat(_intensityId);
|
|
}
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
if (_image == null)
|
|
{
|
|
_image = GetComponent<Image>();
|
|
}
|
|
|
|
CreateRuntimeMaterial();
|
|
|
|
if(_useStartingValues)
|
|
{
|
|
SetLightColor(_startingColor);
|
|
SetIntensity(_startingIntensity);
|
|
}
|
|
|
|
if (_randomizeFlickerOffset && _runtimeMaterial != null)
|
|
{
|
|
_runtimeMaterial.SetFloat(_flickerOffsetId, UnityEngine.Random.Range(0f, 100f));
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
CancelTween();
|
|
|
|
if (_runtimeMaterial != null)
|
|
{
|
|
Destroy(_runtimeMaterial);
|
|
_runtimeMaterial = null;
|
|
}
|
|
}
|
|
|
|
public UniTask ChangeColor(Color targetColor)
|
|
{
|
|
return ChangeColor(targetColor, _defaultTweenDuration);
|
|
}
|
|
|
|
public UniTask ChangeColor(Color targetColor, float duration)
|
|
{
|
|
return TweenTo(targetColor, Intensity, duration);
|
|
}
|
|
|
|
public UniTask ChangeIntensity(float targetIntensity)
|
|
{
|
|
return ChangeIntensity(targetIntensity, _defaultTweenDuration);
|
|
}
|
|
|
|
public UniTask ChangeIntensity(float targetIntensity, float duration)
|
|
{
|
|
return TweenTo(LightColor, targetIntensity, duration);
|
|
}
|
|
|
|
public UniTask TurnOff()
|
|
{
|
|
return ChangeIntensity(0f, _defaultTweenDuration);
|
|
}
|
|
|
|
public UniTask TurnOn()
|
|
{
|
|
return ChangeIntensity(_startingIntensity, _defaultTweenDuration);
|
|
}
|
|
|
|
public async UniTask TweenTo(Color targetColor, float targetIntensity, float duration)
|
|
{
|
|
if (_runtimeMaterial == null)
|
|
{
|
|
CreateRuntimeMaterial();
|
|
}
|
|
|
|
CancelTween();
|
|
|
|
var fromColor = LightColor;
|
|
var fromIntensity = Intensity;
|
|
var safeDuration = Mathf.Max(0f, duration);
|
|
|
|
_lightCts = new CancellationTokenSource();
|
|
|
|
_lightSequence = Sequence.Create(useUnscaledTime: _useUnscaledTime)
|
|
.Group(Tween.Custom(
|
|
0f,
|
|
1f,
|
|
safeDuration,
|
|
progress =>
|
|
{
|
|
SetLightColor(Color.LerpUnclamped(fromColor, targetColor, progress));
|
|
SetIntensity(Mathf.LerpUnclamped(fromIntensity, targetIntensity, progress));
|
|
},
|
|
_ease,
|
|
useUnscaledTime: _useUnscaledTime));
|
|
|
|
try
|
|
{
|
|
await _lightSequence.ToUniTask(cancellationToken: _lightCts.Token);
|
|
SetLightColor(targetColor);
|
|
SetIntensity(targetIntensity);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// Interrupted by another light tween or object destruction.
|
|
}
|
|
finally
|
|
{
|
|
_lightSequence = default;
|
|
|
|
if (_lightCts != null)
|
|
{
|
|
_lightCts.Dispose();
|
|
_lightCts = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetLightColor(Color color)
|
|
{
|
|
if (_runtimeMaterial == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_runtimeMaterial.SetColor(_lightColorId, color);
|
|
}
|
|
|
|
public void SetIntensity(float intensity)
|
|
{
|
|
if (_runtimeMaterial == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_runtimeMaterial.SetFloat(_intensityId, Mathf.Max(0f, intensity));
|
|
}
|
|
|
|
public void CancelTween()
|
|
{
|
|
if (_lightSequence.isAlive)
|
|
{
|
|
_lightSequence.Stop();
|
|
_lightSequence = default;
|
|
}
|
|
|
|
if (_lightCts != null)
|
|
{
|
|
_lightCts.Cancel();
|
|
_lightCts.Dispose();
|
|
_lightCts = null;
|
|
}
|
|
}
|
|
|
|
private void CreateRuntimeMaterial()
|
|
{
|
|
if (_image == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_lightMaterialTemplate != null)
|
|
{
|
|
_runtimeMaterial = Instantiate(_lightMaterialTemplate);
|
|
_image.material = _runtimeMaterial;
|
|
return;
|
|
}
|
|
|
|
var shader = Shader.Find(_lightShaderName);
|
|
if (shader == null)
|
|
{
|
|
Debug.LogWarning($"[{nameof(UILightGlow)}] Could not find shader '{_lightShaderName}'.");
|
|
return;
|
|
}
|
|
|
|
_runtimeMaterial = new Material(shader)
|
|
{
|
|
name = $"{nameof(UILightGlow)} Runtime Material"
|
|
};
|
|
|
|
_image.material = _runtimeMaterial;
|
|
}
|
|
}
|
|
}
|