using UnityEngine; /// /// Runtime driver for materials made from the Rainbow-Cats dissolve / HDR-outline shaders. /// Attach to any Renderer and it will animate the shader's exposed properties so you get the /// dissolve fade or the pulsing HDR outline without touching material properties by hand. /// /// Works on an instanced copy of the material via MaterialPropertyBlock, so multiple objects /// sharing one material can dissolve independently. /// [RequireComponent(typeof(Renderer))] public class DissolveEffectController : MonoBehaviour { public enum Effect { /// Drives "_Fade": 0 = fully dissolved/hidden, 1 = fully visible (Dissolve shader). Dissolve, /// Drives "_Emission": pulses the HDR outline glow (HDR Outline shader). OutlinePulse } [Header("Which effect")] public Effect effect = Effect.Dissolve; [Header("Dissolve")] [Tooltip("If true, the object animates from dissolved -> visible on enable. If false, visible -> dissolved.")] public bool appear = true; [Tooltip("Seconds the dissolve animation takes.")] public float dissolveDuration = 1.5f; [Tooltip("If true the animation plays automatically on enable. Otherwise call Play().")] public bool playOnEnable = true; [Header("Outline pulse")] [Tooltip("Min emission intensity for the outline pulse.")] public float pulseMin = 1f; [Tooltip("Max emission intensity for the outline pulse.")] public float pulseMax = 6f; [Tooltip("Pulses per second.")] public float pulseSpeed = 1.5f; // Property IDs from the shader graphs' exposed reference names. private static readonly int FadeId = Shader.PropertyToID("_Fade"); private static readonly int EmissionId = Shader.PropertyToID("_Emission"); private Renderer targetRenderer; private MaterialPropertyBlock block; private float timer; private bool playing; // Track on-screen visibility so the continuous OutlinePulse write is skipped when the // renderer is offscreen (nothing would be seen anyway). Assume visible until Unity tells // us otherwise, so effects on objects that never entered the culling system still animate. private bool isVisible = true; private void OnBecameVisible() { isVisible = true; } private void OnBecameInvisible() { isVisible = false; } private void Awake() { targetRenderer = GetComponent(); block = new MaterialPropertyBlock(); } private void OnEnable() { if (playOnEnable) { Play(); } } /// Restart the effect from the beginning. public void Play() { timer = 0f; playing = true; } /// Set the dissolve amount directly (0 = hidden, 1 = visible) and stop animating. public void SetFade(float value) { playing = false; targetRenderer.GetPropertyBlock(block); block.SetFloat(FadeId, Mathf.Clamp01(value)); targetRenderer.SetPropertyBlock(block); } private void Update() { if (effect == Effect.OutlinePulse) { if (isVisible) UpdatePulse(); return; } if (playing) { UpdateDissolve(); } } private void UpdateDissolve() { timer += Time.deltaTime; float t = dissolveDuration > 0f ? Mathf.Clamp01(timer / dissolveDuration) : 1f; float fade = appear ? t : 1f - t; targetRenderer.GetPropertyBlock(block); block.SetFloat(FadeId, fade); targetRenderer.SetPropertyBlock(block); if (t >= 1f) { playing = false; } } private void UpdatePulse() { float wave = (Mathf.Sin(Time.time * pulseSpeed * Mathf.PI * 2f) + 1f) * 0.5f; float emission = Mathf.Lerp(pulseMin, pulseMax, wave); targetRenderer.GetPropertyBlock(block); block.SetFloat(EmissionId, emission); targetRenderer.SetPropertyBlock(block); } }