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);
}
}