using UnityEngine; public class GlobalGlitchRuntimeController : MonoBehaviour { public static GlobalGlitchRuntimeController Instance { get; private set; } [Header("Runtime Glitch Defaults")] public float defaultTriggerIntensity = 0.7f; public float defaultHoldDuration = 0.05f; public float defaultRecoverDuration = 0.16f; public AnimationCurve recoveryCurve = AnimationCurve.EaseInOut(0f, 1f, 1f, 0f); public bool useUnscaledTime = true; public bool triggerOnEnable = false; private static float sPersistentIntensity; private static float sPulseIntensity; private static float sWaveIntensity; private float activePulseIntensity; private float holdRemaining; private float recoverElapsed; private float recoverDuration; private bool recovering; private float waveTargetIntensity; private float waveEnterDuration; private float waveRecoverDuration; private float waveElapsed; private WavePhase wavePhase = WavePhase.None; public static float RuntimeIntensity => Mathf.Clamp01(sPersistentIntensity + sPulseIntensity + sWaveIntensity); private enum WavePhase { None, Enter, Exit } private void Awake() { if (Instance == null) { Instance = this; return; } if (Instance != this) Destroy(gameObject); } private void OnEnable() { if (triggerOnEnable) TriggerGlitch(); } private void Update() { float delta = useUnscaledTime ? Time.unscaledDeltaTime : Time.deltaTime; UpdatePulse(delta); UpdateWave(delta); } public void TriggerGlitch() { TriggerGlitch(defaultTriggerIntensity, defaultHoldDuration, defaultRecoverDuration); } public void TriggerGlitch(float intensity, float holdDuration, float recoveryDurationSeconds) { intensity = Mathf.Clamp01(intensity); holdDuration = Mathf.Max(0f, holdDuration); recoveryDurationSeconds = Mathf.Max(0f, recoveryDurationSeconds); activePulseIntensity = intensity; holdRemaining = holdDuration; recoverDuration = recoveryDurationSeconds; recoverElapsed = 0f; recovering = recoveryDurationSeconds <= 0f && holdDuration <= 0f; sPulseIntensity = intensity; if (holdDuration <= 0f && recoveryDurationSeconds <= 0f) { activePulseIntensity = 0f; sPulseIntensity = 0f; recovering = false; } } public void SetPersistentIntensity(float intensity) { sPersistentIntensity = Mathf.Clamp01(intensity); } public void ClearPersistentIntensity() { sPersistentIntensity = 0f; } public void TriggerWave(float targetIntensity, float enterDurationSeconds, float recoverDurationSeconds) { waveTargetIntensity = Mathf.Clamp01(targetIntensity); waveEnterDuration = Mathf.Max(0.0001f, enterDurationSeconds); waveRecoverDuration = Mathf.Max(0.0001f, recoverDurationSeconds); waveElapsed = 0f; wavePhase = WavePhase.Enter; sWaveIntensity = 0f; } private void UpdatePulse(float delta) { if (activePulseIntensity <= 0f && !recovering) { sPulseIntensity = 0f; return; } if (holdRemaining > 0f) { holdRemaining -= delta; sPulseIntensity = activePulseIntensity; if (holdRemaining > 0f) return; recovering = recoverDuration > 0f; recoverElapsed = 0f; if (!recovering) { activePulseIntensity = 0f; sPulseIntensity = 0f; return; } } if (!recovering) { activePulseIntensity = 0f; sPulseIntensity = 0f; return; } recoverElapsed += delta; float normalized = recoverDuration <= 0f ? 1f : Mathf.Clamp01(recoverElapsed / recoverDuration); float curveValue = recoveryCurve != null && recoveryCurve.length > 0 ? recoveryCurve.Evaluate(normalized) : 1f - normalized; sPulseIntensity = Mathf.Clamp01(activePulseIntensity * Mathf.Max(0f, curveValue)); if (normalized < 1f) return; recovering = false; activePulseIntensity = 0f; sPulseIntensity = 0f; } private void UpdateWave(float delta) { if (wavePhase == WavePhase.None) { sWaveIntensity = 0f; return; } waveElapsed += delta; if (wavePhase == WavePhase.Enter) { float normalized = Mathf.Clamp01(waveElapsed / waveEnterDuration); sWaveIntensity = Mathf.LerpUnclamped(0f, waveTargetIntensity, normalized); if (normalized < 1f) return; sWaveIntensity = waveTargetIntensity; wavePhase = WavePhase.Exit; waveElapsed = 0f; return; } float exitNormalized = Mathf.Clamp01(waveElapsed / waveRecoverDuration); sWaveIntensity = Mathf.LerpUnclamped(waveTargetIntensity, 0f, exitNormalized); if (exitNormalized < 1f) return; sWaveIntensity = 0f; wavePhase = WavePhase.None; } public static GlobalGlitchRuntimeController EnsureInstance() { if (Instance != null) return Instance; GlobalGlitchRuntimeController existing = SceneObjectLookupCache.FindFirst(); if (existing != null) { Instance = existing; return existing; } GameObject runtimeObject = new GameObject("__global_glitch_runtime"); Instance = runtimeObject.AddComponent(); return Instance; } public static void Trigger(float intensity, float holdDuration, float recoverDurationSeconds) { EnsureInstance().TriggerGlitch(intensity, holdDuration, recoverDurationSeconds); } public static void TriggerDefault() { EnsureInstance().TriggerGlitch(); } public static void SetPersistent(float intensity) { EnsureInstance().SetPersistentIntensity(intensity); } public static void ClearPersistent() { if (Instance != null) Instance.ClearPersistentIntensity(); else sPersistentIntensity = 0f; } public static void TriggerWaveEffect(float enterDurationSeconds, float recoverDurationSeconds) { EnsureInstance().TriggerWave(1f, enterDurationSeconds, recoverDurationSeconds); } }