135 lines
5.6 KiB
C#
135 lines
5.6 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Video;
|
|
using OctoCore.Transitions;
|
|
using Unheimlich.Transitions;
|
|
|
|
namespace Unheimlich.Features.Hallucination
|
|
{
|
|
/// <summary>
|
|
/// Scene-local trigger for a hallucination blink. Delegates all visual work to the
|
|
/// persistent OctoEyelidBlink singleton. Runs as a single close-hold-open cycle when
|
|
/// the layer swap is enabled (the correct video/eyelid z-order makes one blink enough),
|
|
/// or as the classic double close-hold-open-hold-close-hold-open sequence otherwise.
|
|
/// </summary>
|
|
public class HallucinationFlash : MonoBehaviour
|
|
{
|
|
[SerializeField] private VideoClip _clip;
|
|
[SerializeField] private Material _material;
|
|
|
|
[Header("Timings")]
|
|
[SerializeField, Min(0f)] private float _closeDuration = 0.1f;
|
|
[SerializeField, Min(0f)] private float _holdClosed = 0.05f;
|
|
[SerializeField, Min(0f)] private float _openDuration = 0.15f;
|
|
[Tooltip("How long the video stays visible after the eyes have fully reopened, before it's hidden.")]
|
|
[SerializeField, Min(0f)] private float _visibleHold = 0.5f;
|
|
[SerializeField, Range(0f, 1f)] private float _showVideoAtCloseProgress = 0.25f;
|
|
|
|
[Header("Layering")]
|
|
[Tooltip("If enabled, the eyelid bars render behind the video overlay while closing (video shows on top of them), then swap to render in front of it while opening (video disappears behind the still-closed-looking bars instead of staying visible during the reveal).")]
|
|
[SerializeField] private bool _swapLayerOrderOnTransition = false;
|
|
|
|
[Header("Debug")]
|
|
[Tooltip("Logs the full call stack every time Play() is triggered, so you can trace who requested this hallucination.")]
|
|
[SerializeField] private bool _logCallers = false;
|
|
|
|
/// <summary>
|
|
/// Starts warming up this hallucination's video clip ahead of time, without triggering
|
|
/// the blink sequence. Call this in advance (e.g. when the player enters a trigger volume
|
|
/// near the hallucination point) so the video is ready the moment Play() is actually called,
|
|
/// instead of racing the warm-up against the eyelid close animation.
|
|
/// </summary>
|
|
public void Prewarm()
|
|
{
|
|
OctoEyelidBlink blink = OctoEyelidBlink.Instance;
|
|
|
|
if (blink == null)
|
|
return;
|
|
|
|
blink.SetClip(_clip, _material);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Triggers the hallucination blink. If the layer swap is enabled, runs a single
|
|
/// close-hold-open cycle with the video revealed partway through closing and hidden
|
|
/// after the configured hold once reopened. If disabled, runs the classic double
|
|
/// blink instead. No-op if the singleton is missing or already busy.
|
|
/// </summary>
|
|
public void Play()
|
|
{
|
|
if (_logCallers)
|
|
Debug.LogError($"[HallucinationFlash] Play() called on '{gameObject.name}' at t={Time.time:F2}\n{StackTraceUtility.ExtractStackTrace()}");
|
|
|
|
OctoEyelidBlink blink = OctoEyelidBlink.Instance;
|
|
|
|
if (blink == null)
|
|
{
|
|
Debug.LogWarning("[HallucinationFlash] OctoEyelidBlink singleton not found in scene.");
|
|
return;
|
|
}
|
|
|
|
if (blink.IsBusy)
|
|
return;
|
|
|
|
blink.SetClip(_clip, _material);
|
|
blink.SetLayerSwapEnabled(_swapLayerOrderOnTransition);
|
|
|
|
if (_swapLayerOrderOnTransition)
|
|
PlaySingleBlink(blink);
|
|
else
|
|
PlayDoubleBlink(blink);
|
|
|
|
blink.ShowVideoAfterDelay(_closeDuration * _showVideoAtCloseProgress);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Single close-hold-open cycle: video is revealed partway through closing and hidden
|
|
/// after staying visible for _visibleHold once the eyes are back open. Relies on the
|
|
/// eyelid/video layer swap being correctly set beforehand for the reveal to look right.
|
|
/// </summary>
|
|
private void PlaySingleBlink(OctoEyelidBlink blink)
|
|
{
|
|
blink.CloseEyes(_closeDuration, () =>
|
|
{
|
|
blink.Hold(_holdClosed, () =>
|
|
{
|
|
blink.OpenEyes(_openDuration, () =>
|
|
{
|
|
blink.Hold(_visibleHold, () =>
|
|
{
|
|
blink.HideVideo();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Classic double blink: close, reveal video, open, hold visible, close again,
|
|
/// hide video, hold, open. Used when the layer swap is disabled, since without the
|
|
/// correct z-order a single blink wouldn't hide the video cleanly on its own.
|
|
/// </summary>
|
|
private void PlayDoubleBlink(OctoEyelidBlink blink)
|
|
{
|
|
blink.CloseEyes(_closeDuration, () =>
|
|
{
|
|
blink.Hold(_holdClosed, () =>
|
|
{
|
|
blink.OpenEyes(_openDuration, () =>
|
|
{
|
|
blink.Hold(_visibleHold, () =>
|
|
{
|
|
blink.CloseEyes(_closeDuration, () =>
|
|
{
|
|
blink.HideVideo();
|
|
blink.Hold(_holdClosed, () =>
|
|
{
|
|
blink.OpenEyes(_openDuration, null);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|
|
} |