using UnityEngine; using System.Collections; namespace SynapticPro { /// /// Controller for Synaptic DissolvePro shader /// Provides easy animation control and particle system integration /// [ExecuteAlways] public class DissolveController : MonoBehaviour { [Header("Target")] public Renderer targetRenderer; public int materialIndex = 0; [Header("Dissolve Settings")] [Range(0f, 1f)] public float dissolveAmount = 0f; [Header("Direction")] public DissolveDirection direction = DissolveDirection.Up; public Vector3 customDirection = Vector3.up; public Transform directionSource; [Header("Animation")] public float animationDuration = 2f; public AnimationCurve dissolveCurve = AnimationCurve.EaseInOut(0, 0, 1, 1); public bool autoReverse = false; public float reverseDelay = 0.5f; [Header("Particles")] public ParticleSystem dissolveParticles; public bool spawnParticlesOnEdge = true; public float particleEmissionRate = 50f; [Header("Audio")] public AudioSource dissolveAudio; public AudioClip dissolveSound; [Header("Events")] public UnityEngine.Events.UnityEvent onDissolveStart; public UnityEngine.Events.UnityEvent onDissolveComplete; public UnityEngine.Events.UnityEvent onAppearStart; public UnityEngine.Events.UnityEvent onAppearComplete; public enum DissolveDirection { Up, Down, Left, Right, Forward, Back, Spherical, Custom } private Material material; private Coroutine animationCoroutine; private bool isAnimating = false; // Shader property IDs private static readonly int DissolveAmountID = Shader.PropertyToID("_DissolveAmount"); private static readonly int DissolveDirectionID = Shader.PropertyToID("_DissolveDirection"); private static readonly int DirectionalDissolveID = Shader.PropertyToID("_DirectionalDissolve"); private void OnEnable() { SetupMaterial(); } private void SetupMaterial() { if (targetRenderer == null) targetRenderer = GetComponent(); if (targetRenderer != null && targetRenderer.sharedMaterials.Length > materialIndex) { // Use instance material in play mode, shared in edit mode if (Application.isPlaying) { material = targetRenderer.materials[materialIndex]; } else { material = targetRenderer.sharedMaterials[materialIndex]; } } } private void Update() { if (material == null) return; // Update dissolve amount material.SetFloat(DissolveAmountID, dissolveAmount); // Update direction Vector3 dir = GetDissolveDirection(); material.SetVector(DissolveDirectionID, new Vector4(dir.x, dir.y, dir.z, 0)); material.SetFloat(DirectionalDissolveID, direction == DissolveDirection.Spherical ? 0f : 1f); // Update particles UpdateParticles(); } private Vector3 GetDissolveDirection() { switch (direction) { case DissolveDirection.Up: return Vector3.up; case DissolveDirection.Down: return Vector3.down; case DissolveDirection.Left: return Vector3.left; case DissolveDirection.Right: return Vector3.right; case DissolveDirection.Forward: return Vector3.forward; case DissolveDirection.Back: return Vector3.back; case DissolveDirection.Spherical: return Vector3.zero; case DissolveDirection.Custom: if (directionSource != null) return (directionSource.position - transform.position).normalized; return customDirection.normalized; default: return Vector3.up; } } private void UpdateParticles() { if (dissolveParticles == null || !spawnParticlesOnEdge) return; var emission = dissolveParticles.emission; // Only emit particles while dissolving is active and in progress if (isAnimating && dissolveAmount > 0.01f && dissolveAmount < 0.99f) { emission.rateOverTime = particleEmissionRate; // Position particles at dissolve edge (approximate) Vector3 edgePosition = transform.position + GetDissolveDirection() * dissolveAmount * 2f; dissolveParticles.transform.position = edgePosition; } else { emission.rateOverTime = 0; } } /// /// Start dissolving the object /// public void Dissolve() { if (animationCoroutine != null) StopCoroutine(animationCoroutine); animationCoroutine = StartCoroutine(AnimateDissolve(0f, 1f, onDissolveStart, onDissolveComplete)); } /// /// Make the object appear (reverse dissolve) /// public void Appear() { if (animationCoroutine != null) StopCoroutine(animationCoroutine); animationCoroutine = StartCoroutine(AnimateDissolve(1f, 0f, onAppearStart, onAppearComplete)); } /// /// Toggle between dissolved and visible states /// public void Toggle() { if (dissolveAmount < 0.5f) Dissolve(); else Appear(); } /// /// Set dissolve amount instantly /// public void SetDissolveInstant(float amount) { if (animationCoroutine != null) StopCoroutine(animationCoroutine); dissolveAmount = Mathf.Clamp01(amount); isAnimating = false; } private IEnumerator AnimateDissolve(float from, float to, UnityEngine.Events.UnityEvent onStart, UnityEngine.Events.UnityEvent onComplete) { isAnimating = true; onStart?.Invoke(); // Play audio if (dissolveAudio != null && dissolveSound != null) { dissolveAudio.clip = dissolveSound; dissolveAudio.Play(); } float elapsed = 0f; dissolveAmount = from; while (elapsed < animationDuration) { elapsed += Time.deltaTime; float t = elapsed / animationDuration; float curveT = dissolveCurve.Evaluate(t); dissolveAmount = Mathf.Lerp(from, to, curveT); yield return null; } dissolveAmount = to; isAnimating = false; onComplete?.Invoke(); // Auto reverse if (autoReverse) { yield return new WaitForSeconds(reverseDelay); if (to > 0.5f) Appear(); else Dissolve(); } } /// /// Trigger dissolve from damage or hit /// public void OnDamage(Vector3 hitPoint) { // Set direction from hit point direction = DissolveDirection.Custom; customDirection = (transform.position - hitPoint).normalized; Dissolve(); } #if UNITY_EDITOR private void OnValidate() { SetupMaterial(); } private void OnDrawGizmosSelected() { // Draw dissolve direction Gizmos.color = Color.cyan; Vector3 dir = GetDissolveDirection(); if (dir.magnitude > 0.01f) { Gizmos.DrawRay(transform.position, dir * 2f); Gizmos.DrawWireSphere(transform.position + dir * dissolveAmount * 2f, 0.1f); } } #endif } }