using System.Collections; using UnityEngine; namespace SpriteGlow { /// /// Adds an HDR outline over the SpriteRenderer's sprite borders. /// Can be used in conjuction with bloom post-processing to create a glow effect. /// [AddComponentMenu("Effects/Sprite Glow")] [RequireComponent(typeof(SpriteRenderer)), DisallowMultipleComponent, ExecuteInEditMode] public class SpriteGlowEffect : MonoBehaviour { public SpriteRenderer Renderer { get; private set; } public Color GlowColor { get => glowColor; set { if (glowColor != value) { glowColor = value; SetMaterialProperties(); } } } public float GlowBrightness { get => glowBrightness; set { if (glowBrightness != value) { glowBrightness = value; SetMaterialProperties(); } } } public int OutlineWidth { get => outlineWidth; set { if (outlineWidth != value) { outlineWidth = value; SetMaterialProperties(); } } } public float AlphaThreshold { get => alphaThreshold; set { if (alphaThreshold != value) { alphaThreshold = value; SetMaterialProperties(); } } } public bool DrawOutside { get => drawOutside; set { if (drawOutside != value) { drawOutside = value; SetMaterialProperties(); } } } public bool EnableInstancing { get => enableInstancing; set { if (enableInstancing != value) { enableInstancing = value; SetMaterialProperties(); } } } [Tooltip("Base color of the glow.")] [SerializeField] private Color glowColor = Color.white; [Tooltip("The brightness (power) of the glow."), Range(1, 10)] [SerializeField] private float glowBrightness = 2f; [Tooltip("Width of the outline, in texels."), Range(0, 10)] [SerializeField] private int outlineWidth = 1; [Tooltip("Threshold to determine sprite borders."), Range(0f, 1f)] [SerializeField] private float alphaThreshold = .01f; [Tooltip("Whether the outline should only be drawn outside of the sprite borders. Make sure sprite texture has sufficient transparent space for the required outline width.")] [SerializeField] private bool drawOutside = false; [Tooltip("Whether to enable GPU instancing.")] [SerializeField] private bool enableInstancing = false; private static readonly int isOutlineEnabledId = Shader.PropertyToID("_IsOutlineEnabled"); private static readonly int outlineColorId = Shader.PropertyToID("_OutlineColor"); private static readonly int outlineSizeId = Shader.PropertyToID("_OutlineSize"); private static readonly int alphaThresholdId = Shader.PropertyToID("_AlphaThreshold"); private MaterialPropertyBlock materialProperties; private Coroutine alphaMonitorRoutine; private Coroutine alphaFadeRoutine; private void Awake() { Renderer = GetComponent(); } private void Start() { StartAlphaMonitor(); } private void OnEnable() { SetMaterialProperties(); StartAlphaMonitor(); } private void OnDisable() { if (alphaMonitorRoutine != null) { StopCoroutine(alphaMonitorRoutine); alphaMonitorRoutine = null; } if (alphaFadeRoutine != null) { StopCoroutine(alphaFadeRoutine); alphaFadeRoutine = null; } SetMaterialProperties(); } private void OnValidate() { if (!isActiveAndEnabled) return; SetMaterialProperties(); } private void OnDidApplyAnimationProperties() { SetMaterialProperties(); } private void SetMaterialProperties() { if (!Renderer) return; Renderer.sharedMaterial = SpriteGlowMaterial.GetSharedFor(this); if (materialProperties == null) materialProperties = new MaterialPropertyBlock(); materialProperties.SetFloat(isOutlineEnabledId, isActiveAndEnabled ? 1 : 0); materialProperties.SetColor(outlineColorId, GlowColor * GlowBrightness); materialProperties.SetFloat(outlineSizeId, OutlineWidth); materialProperties.SetFloat(alphaThresholdId, AlphaThreshold); Renderer.SetPropertyBlock(materialProperties); } private void StartAlphaMonitor() { if (alphaMonitorRoutine != null || Renderer == null) return; alphaMonitorRoutine = StartCoroutine(AlphaMonitorRoutine()); } private IEnumerator AlphaMonitorRoutine() { while (true) { if (Renderer != null) { Color color = Renderer.color; if (color.a != 0f) { if (alphaFadeRoutine == null) { alphaFadeRoutine = StartCoroutine(FadeRendererAlphaToZero()); } yield break; } } yield return new WaitForSecondsRealtime(1f); } } private IEnumerator FadeRendererAlphaToZero() { if (Renderer == null) yield break; float duration = 0.18f; float elapsed = 0f; Color startColor = Renderer.color; Color endColor = startColor; endColor.a = 0f; while (elapsed < duration) { if (Renderer == null) yield break; elapsed += Time.unscaledDeltaTime; float t = Mathf.Clamp01(elapsed / duration); Renderer.color = Color.LerpUnclamped(startColor, endColor, t); yield return null; } if (Renderer != null) { Color c = Renderer.color; c.a = 0f; Renderer.color = c; } alphaFadeRoutine = null; alphaMonitorRoutine = null; } } }