Files
Ipseity-Demo/Assets/_Content/Scripts/Features/Hallucination/Runtime/HallucinationPostProcess.cs
T

121 lines
4.9 KiB
C#

using UnityEngine;
using UnityEngine.Rendering;
namespace Unheimlich.Features.Hallucination
{
/// <summary>
/// Drives a persistent post-process weight targeting either a URP/HDRP <see cref="Volume"/>
/// and/or a <see cref="Material"/> exposing a <c>_Weight</c> property, based on an
/// internal sanity value [0, 1].
/// Wire the Wrapper methods to any UnityEvent with no arguments.
/// </summary>
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 ──────────────────────────────────────────────────────────
/// <summary>Adds <see cref="_wrapperAddAmount"/> sanity over <see cref="_wrapperDuration"/> seconds.
/// Auto-decay starts when the tween completes, if <see cref="_autoDecayRate"/> is greater than 0.</summary>
public void AddSanityWrapper() => TweenSanity(_sanity + _wrapperAddAmount, _wrapperDuration);
/// <summary>Removes <see cref="_wrapperRemoveAmount"/> sanity over <see cref="_wrapperDuration"/> seconds.</summary>
public void RemoveSanityWrapper() => TweenSanity(_sanity - _wrapperRemoveAmount, _wrapperDuration);
/// <summary>Snaps sanity to 0 instantly and stops decay.</summary>
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;
}
}
}