using UnityEngine; using UnityEngine.Rendering; namespace Unheimlich.Features.Hallucination { /// /// Drives a persistent post-process weight targeting either a URP/HDRP /// and/or a exposing a _Weight property, based on an /// internal sanity value [0, 1]. /// Wire the Wrapper methods to any UnityEvent with no arguments. /// public class HallucinationPostProcess : MonoBehaviour { [Header("Targets")] [Tooltip("URP/HDRP Volume driven by the sanity weight.")] [SerializeField] private Volume _volume; [Tooltip("Optional material exposing a _Weight property.")] [SerializeField] private Material _shaderMaterial; [Header("Sanity")] [SerializeField, Range(0f, 1f)] private float _initialSanity = 0f; [Tooltip("Sanity lost per second after each tween completes. 0 = no decay.")] [SerializeField, Min(0f)] private float _autoDecayRate = 0f; [Header("Wrappers")] [Tooltip("Amount added when calling AddSanityWrapper().")] [SerializeField, Range(0f, 1f)] private float _wrapperAddAmount = 0.2f; [Tooltip("Amount removed when calling RemoveSanityWrapper().")] [SerializeField, Range(0f, 1f)] private float _wrapperRemoveAmount = 0.2f; [Tooltip("Transition duration used by all wrappers (seconds).")] [SerializeField, Min(0f)] private float _wrapperDuration = 1f; // ─── State ───────────────────────────────────────────────────────────── private float _sanity; private int _tweenId = -1; private bool _decaying = false; // ─── Lifecycle ───────────────────────────────────────────────────────── private void Awake() { _sanity = Mathf.Clamp01(_initialSanity); Apply(_sanity); } private void Update() { if (!_decaying || _autoDecayRate <= 0f || _sanity <= 0f) return; _sanity = Mathf.Max(0f, _sanity - _autoDecayRate * Time.deltaTime); Apply(_sanity); if (_sanity <= 0f) _decaying = false; } // ─── Wrappers ────────────────────────────────────────────────────────── /// Adds sanity over seconds. /// Auto-decay starts when the tween completes, if is greater than 0. public void AddSanityWrapper() => TweenSanity(_sanity + _wrapperAddAmount, _wrapperDuration); /// Removes sanity over seconds. public void RemoveSanityWrapper() => TweenSanity(_sanity - _wrapperRemoveAmount, _wrapperDuration); /// Snaps sanity to 0 instantly and stops decay. public void ResetSanityWrapper() { CancelCurrent(); _decaying = false; _sanity = 0f; Apply(0f); } // ─── Private ─────────────────────────────────────────────────────────── private void TweenSanity(float target, float duration) { target = Mathf.Clamp01(target); _decaying = false; if (duration <= 0f) { CancelCurrent(); _sanity = target; Apply(target); if (_autoDecayRate > 0f) _decaying = true; return; } CancelCurrent(); float from = _sanity; _tweenId = LeanTween.value(gameObject, from, target, duration) .setEase(LeanTweenType.easeInOutQuad) .setOnUpdate((float w) => { _sanity = w; Apply(w); }) .setOnComplete(() => { // Start decay only after the tween has fully completed. if (_autoDecayRate > 0f) _decaying = true; }).id; } private void Apply(float weight) { if (_volume != null) _volume.weight = weight; if (_shaderMaterial != null) _shaderMaterial.SetFloat("_Weight", weight); } private void CancelCurrent() { if (_tweenId == -1) return; LeanTween.cancel(_tweenId); _tweenId = -1; } } }