121 lines
4.9 KiB
C#
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;
|
|
}
|
|
}
|
|
} |