Files
aibis-dream/Assets/Scripts/SceneManagement/TimelineKit/StarfieldDefocusController.cs
T

175 lines
5.8 KiB
C#

using UnityEngine;
using UnityEngine.Playables;
namespace AibisDream
{
/// <summary>
/// Drives the deliberately unsuccessful focus pulls at the start of the
/// starfield-to-sea Timeline. The effect is evaluated from Timeline time so
/// pausing, frame drops and repeated playback remain deterministic.
/// </summary>
public sealed class StarfieldDefocusController : MonoBehaviour
{
private static readonly int BlurAmountId = Shader.PropertyToID("_BlurAmount");
private static readonly int BlurSizeId = Shader.PropertyToID("_BlurSize");
[Header("References")]
[SerializeField] private PlayableDirector director;
[SerializeField] private SpriteRenderer starfieldRenderer;
[Header("Defocus Look")]
[Range(0f, 1f)]
[SerializeField] private float initialBlur = 0.78f;
[Range(0f, 0.05f)]
[SerializeField] private float blurSize = 0.009f;
[Header("Focus Attempt 1")]
[Min(0f)]
[SerializeField] private float firstAttemptStart = 0.45f;
[Min(0.01f)]
[SerializeField] private float firstFocusDuration = 0.48f;
[Range(0f, 1f)]
[SerializeField] private float firstNearFocusBlur = 0.38f;
[Min(0f)]
[SerializeField] private float firstHoldDuration = 0.14f;
[Min(0.01f)]
[SerializeField] private float firstSlipDuration = 0.38f;
[Range(0f, 1f)]
[SerializeField] private float firstSlipBlur = 0.72f;
[Header("Focus Attempt 2")]
[Min(0f)]
[SerializeField] private float secondAttemptStart = 1.72f;
[Min(0.01f)]
[SerializeField] private float secondFocusDuration = 0.55f;
[Range(0f, 1f)]
[SerializeField] private float secondNearFocusBlur = 0.28f;
[Min(0f)]
[SerializeField] private float secondHoldDuration = 0.18f;
[Min(0.01f)]
[SerializeField] private float secondSlipDuration = 0.46f;
[Range(0f, 1f)]
[SerializeField] private float unresolvedBlur = 0.62f;
[Header("Downward Pan")]
[Tooltip("Timeline time at which the starfield starts moving down toward the sea.")]
[Min(0f)]
[SerializeField] private float blurFadeStart = 4f;
[Min(0.01f)]
[SerializeField] private float blurFadeDuration = 3.2f;
private MaterialPropertyBlock propertyBlock;
private void Reset()
{
director = GetComponent<PlayableDirector>();
starfieldRenderer = GetComponentInChildren<SpriteRenderer>(true);
}
private void Awake()
{
propertyBlock = new MaterialPropertyBlock();
ApplyAtTime(director != null ? (float)director.time : 0f);
}
private void OnEnable()
{
ApplyAtTime(director != null ? (float)director.time : 0f);
}
private void LateUpdate()
{
if (director == null || starfieldRenderer == null)
{
return;
}
ApplyAtTime((float)director.time);
}
private void ApplyAtTime(float time)
{
if (starfieldRenderer == null)
{
return;
}
propertyBlock ??= new MaterialPropertyBlock();
starfieldRenderer.GetPropertyBlock(propertyBlock);
propertyBlock.SetFloat(BlurAmountId, EvaluateBlur(time));
propertyBlock.SetFloat(BlurSizeId, blurSize);
starfieldRenderer.SetPropertyBlock(propertyBlock);
}
private float EvaluateBlur(float time)
{
float firstFocusEnd = firstAttemptStart + firstFocusDuration;
float firstHoldEnd = firstFocusEnd + firstHoldDuration;
float firstSlipEnd = firstHoldEnd + firstSlipDuration;
float secondFocusEnd = secondAttemptStart + secondFocusDuration;
float secondHoldEnd = secondFocusEnd + secondHoldDuration;
float secondSlipEnd = secondHoldEnd + secondSlipDuration;
if (time < firstAttemptStart)
{
return initialBlur;
}
if (time < firstFocusEnd)
{
return SmoothLerp(initialBlur, firstNearFocusBlur, InverseLerp(firstAttemptStart, firstFocusEnd, time));
}
if (time < firstHoldEnd)
{
return firstNearFocusBlur;
}
if (time < firstSlipEnd)
{
return SmoothLerp(firstNearFocusBlur, firstSlipBlur, InverseLerp(firstHoldEnd, firstSlipEnd, time));
}
if (time < secondAttemptStart)
{
return firstSlipBlur;
}
if (time < secondFocusEnd)
{
return SmoothLerp(firstSlipBlur, secondNearFocusBlur, InverseLerp(secondAttemptStart, secondFocusEnd, time));
}
if (time < secondHoldEnd)
{
return secondNearFocusBlur;
}
if (time < secondSlipEnd)
{
return SmoothLerp(secondNearFocusBlur, unresolvedBlur, InverseLerp(secondHoldEnd, secondSlipEnd, time));
}
if (time < blurFadeStart)
{
return unresolvedBlur;
}
return SmoothLerp(unresolvedBlur, 0f, InverseLerp(blurFadeStart, blurFadeStart + blurFadeDuration, time));
}
private static float InverseLerp(float from, float to, float value)
{
return Mathf.Clamp01((value - from) / Mathf.Max(0.0001f, to - from));
}
private static float SmoothLerp(float from, float to, float t)
{
t = Mathf.Clamp01(t);
t = t * t * (3f - 2f * t);
return Mathf.LerpUnclamped(from, to, t);
}
}
}