WIP : Props placement and lighting test

This commit is contained in:
Ferlat Thibaud
2026-04-03 19:19:59 +02:00
parent 610f85e4ba
commit fef26f3790
248 changed files with 23629 additions and 2922 deletions
@@ -14,6 +14,8 @@ namespace Unheimlich.Features.Dialogue.UI
/// so the HallucinationManager can drive baseline behaviour without FindObjectsOfType.
/// - Exposes SetShakeParams() and SetGhostTrail() for runtime overrides.
/// - ApplyBaseline() / ApplyOverride() drive the two-layer system.
/// - One-shot trigger distance is now explicit and independent from the visual near-fade.
/// - One-shot fade-out runs independently from the distance-based alpha so it is never overwritten.
/// </summary>
public class FloatingLabel : MonoBehaviour
{
@@ -59,8 +61,14 @@ namespace Unheimlich.Features.Dialogue.UI
[SerializeField] private float _attractionSmoothness = 4f;
[Header("One-Shot (Optional)")]
[SerializeField] private bool _enableOneShot = false;
[SerializeField] [Range(0.01f, 1f)] private float _seenAlphaThreshold = 0.5f;
[Tooltip("Enable one-shot behaviour: the label permanently disappears after the player gets close enough.")]
[SerializeField] private bool _enableOneShot = false;
[Tooltip("If true, the one-shot resets when the player walks far away (outside far-fade zone) without having triggered it.")]
[SerializeField] private bool _isRetriggerableFar = false;
[Tooltip("Distance at which the one-shot triggers, completely independent from the visual near-fade.")]
[SerializeField] private float _oneShotTriggerDistance = 0.5f;
[Tooltip("Duration of the fade-out once the one-shot is triggered.")]
[SerializeField] private float _fadeOutDuration = 0.5f;
[Header("Reappear Fade")]
[SerializeField] private float _fadeInDuration = 0.5f;
@@ -71,7 +79,7 @@ namespace Unheimlich.Features.Dialogue.UI
// ─── Runtime state ─────────────────────────────────────────────────────
private Camera _camera;
private Vector3 _localHomePosition;
private Vector3 _localHomePosition;
private Vector3 _homePosition;
private Vector3 _baseScale;
@@ -90,9 +98,9 @@ namespace Unheimlich.Features.Dialogue.UI
private float _liveShakeSmoothness;
// Ghost trail — live values
private bool _liveGhostEnabled;
private float _liveGhostAlpha;
private TextMeshPro _ghostText;
private bool _liveGhostEnabled;
private float _liveGhostAlpha;
private TextMeshPro _ghostText;
private Queue<Vector3> _positionHistory = new();
private float _historyTimer;
private const float HISTORY_SAMPLE_RATE = 0.016f;
@@ -101,8 +109,14 @@ namespace Unheimlich.Features.Dialogue.UI
private Vector3 _attractionOffset;
// One-shot
private bool _hasBeenSeen;
private bool _permanentlyHidden;
private bool _hasBeenSeen;
private bool _permanentlyHidden;
private float _closestDistanceReached = float.MaxValue;
// One-shot fade-out — runs independently from the distance alpha
private bool _isFadingOut = false;
private float _fadeOutTimer = 0f;
private float _fadeOutStartAlpha = 1f;
// Override coroutine
private Coroutine _overrideCoroutine;
@@ -132,14 +146,12 @@ namespace Unheimlich.Features.Dialogue.UI
private void Start()
{
_localHomePosition = transform.localPosition;
_baseScale = transform.localScale;
_localHomePosition = transform.localPosition;
_baseScale = transform.localScale;
if (_textComponent != null && !string.IsNullOrEmpty(_overrideText))
_textComponent.text = _overrideText;
SaveBaselineSnapshot();
SetupGhostTrail();
@@ -162,10 +174,10 @@ namespace Unheimlich.Features.Dialogue.UI
{
_cullTimer = 0f;
bool shouldBeActive = Vector3.Distance(_camera.transform.position, transform.position)
<= _maxVisibleDistance + _fadeRange;
<= _maxVisibleDistance + _fadeRange;
if (shouldBeActive != _isActive)
{
_isActive = shouldBeActive;
_isActive = shouldBeActive;
_textComponent.enabled = _isActive;
if (_ghostText != null) _ghostText.enabled = _isActive && _liveGhostEnabled;
if (_isActive) _fadeInAlpha = 0f;
@@ -176,22 +188,69 @@ namespace Unheimlich.Features.Dialogue.UI
UpdatePosition();
float dist = Vector3.Distance(_camera.transform.position, transform.position);
float alpha = ComputeAlpha(dist) * _fadeInAlpha;
float dist = Vector3.Distance(_camera.transform.position, transform.position);
// ── One-shot ───────────────────────────────────────────────────────
if (_enableOneShot)
{
if (!_hasBeenSeen && alpha >= _seenAlphaThreshold) _hasBeenSeen = true;
if (_hasBeenSeen && alpha <= 0f)
// Track the closest the player has ever been since the label was seen
if (_hasBeenSeen && dist < _closestDistanceReached)
_closestDistanceReached = dist;
// Mark as seen as soon as the player enters the visible range
if (!_hasBeenSeen && dist <= _maxVisibleDistance)
{
_permanentlyHidden = true;
_textComponent.enabled = false;
if (_ghostText != null) _ghostText.enabled = false;
_hasBeenSeen = true;
_closestDistanceReached = dist;
}
// Fade-out is running — drive alpha independently, skip normal alpha pipeline
if (_isFadingOut)
{
_fadeOutTimer += Time.deltaTime;
float t = 1f - Mathf.Clamp01(_fadeOutTimer / _fadeOutDuration);
float a = _fadeOutStartAlpha * t;
SetTextAlpha(_textComponent, a);
if (_ghostText != null) SetTextAlpha(_ghostText, a * _liveGhostAlpha);
if (_fadeOutTimer >= _fadeOutDuration)
{
_permanentlyHidden = true;
_isFadingOut = false;
_textComponent.enabled = false;
if (_ghostText != null) _ghostText.enabled = false;
}
return;
}
if (_hasBeenSeen)
{
// Trigger: player reached the explicit trigger distance at some point
bool hasTriggered = _closestDistanceReached <= _oneShotTriggerDistance;
// Reset: player walked far away without ever triggering
bool isFarNow = dist > _maxVisibleDistance - _fadeRange;
if (hasTriggered)
{
_isFadingOut = true;
_fadeOutTimer = 0f;
_fadeOutStartAlpha = ComputeAlpha(dist) * _fadeInAlpha;
_closestDistanceReached = float.MaxValue;
return;
}
if (_isRetriggerableFar && isFarNow)
{
_hasBeenSeen = false;
_closestDistanceReached = float.MaxValue;
}
}
}
// ── Normal alpha pipeline ──────────────────────────────────────────
float alpha = ComputeAlpha(dist) * _fadeInAlpha;
UpdateCameraAttraction(alpha);
ApplyAlpha(alpha);
UpdateAdaptiveScale();
@@ -284,10 +343,14 @@ namespace Unheimlich.Features.Dialogue.UI
public void ResetOneShot()
{
_hasBeenSeen = false;
_permanentlyHidden = false;
_hasBeenSeen = false;
_permanentlyHidden = false;
_isFadingOut = false;
_fadeOutTimer = 0f;
_fadeOutStartAlpha = 1f;
_closestDistanceReached = float.MaxValue;
if (_textComponent != null) _textComponent.enabled = true;
if (_ghostText != null) _ghostText.enabled = true;
if (_ghostText != null) _ghostText.enabled = _liveGhostEnabled;
_fadeInAlpha = 0f;
}
@@ -416,13 +479,13 @@ namespace Unheimlich.Features.Dialogue.UI
ghostObj.transform.localScale = transform.localScale;
ghostObj.layer = gameObject.layer;
_ghostText = ghostObj.AddComponent<TextMeshPro>();
_ghostText.font = _textComponent.font;
_ghostText.fontSize = _textComponent.fontSize;
_ghostText = ghostObj.AddComponent<TextMeshPro>();
_ghostText.font = _textComponent.font;
_ghostText.fontSize = _textComponent.fontSize;
_ghostText.alignment = _textComponent.alignment;
_ghostText.text = _textComponent.text;
_ghostText.color = new Color(_textComponent.color.r, _textComponent.color.g,
_textComponent.color.b, _liveGhostAlpha);
_ghostText.text = _textComponent.text;
_ghostText.color = new Color(_textComponent.color.r, _textComponent.color.g,
_textComponent.color.b, _liveGhostAlpha);
MeshRenderer mainRenderer = _textComponent.GetComponent<MeshRenderer>();
MeshRenderer ghostRenderer = _ghostText.GetComponent<MeshRenderer>();