diff --git a/Assets/GameContent/Scene_Dream/Materials.meta b/Assets/GameContent/Scene_Dream/Materials.meta
new file mode 100644
index 000000000..802236d6d
--- /dev/null
+++ b/Assets/GameContent/Scene_Dream/Materials.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 65a0de17c7fc4cc39ae305f6bb18ac77
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/GameContent/Scene_Dream/Materials/DreamDataOverflow.mat b/Assets/GameContent/Scene_Dream/Materials/DreamDataOverflow.mat
new file mode 100644
index 000000000..89cc8cc8d
--- /dev/null
+++ b/Assets/GameContent/Scene_Dream/Materials/DreamDataOverflow.mat
@@ -0,0 +1,39 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 8
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_Name: DreamDataOverflow
+ m_Shader: {fileID: 4800000, guid: 6c30d8e44b1b4b4ab9414a90e069f290, type: 3}
+ m_Parent: {fileID: 0}
+ m_ModifiedSerializedProperties: 0
+ m_ValidKeywords: []
+ m_InvalidKeywords: []
+ m_LightmapFlags: 4
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap: {}
+ disabledShaderPasses: []
+ m_LockedProperties:
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _MainTex:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Ints: []
+ m_Floats:
+ - _OverflowChromatic: 0.006
+ - _OverflowExposure: 1.45
+ - _OverflowGlow: 0.65
+ - _OverflowIntensity: 0
+ - _OverflowNoise: 0.08
+ m_Colors:
+ - _Color: {r: 1, g: 1, b: 1, a: 1}
+ m_BuildTextureStacks: []
diff --git a/Assets/GameContent/Scene_Dream/Materials/DreamDataOverflow.mat.meta b/Assets/GameContent/Scene_Dream/Materials/DreamDataOverflow.mat.meta
new file mode 100644
index 000000000..5f9152ca1
--- /dev/null
+++ b/Assets/GameContent/Scene_Dream/Materials/DreamDataOverflow.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: db54ed6c97bf4f05a79f53b268f0c52a
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/GameContent/Scene_Dream/Materials/DreamDataOverflow.shader b/Assets/GameContent/Scene_Dream/Materials/DreamDataOverflow.shader
new file mode 100644
index 000000000..d274de458
--- /dev/null
+++ b/Assets/GameContent/Scene_Dream/Materials/DreamDataOverflow.shader
@@ -0,0 +1,154 @@
+Shader "AibisDream/DreamDataOverflow"
+{
+ Properties
+ {
+ [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
+ _Color("Tint", Color) = (1,1,1,1)
+ _OverflowIntensity("Overflow Intensity", Range(0, 1)) = 0
+ _OverflowExposure("Overflow Exposure", Range(0, 3)) = 1.45
+ _OverflowGlow("Overflow Glow", Range(0, 2)) = 0.65
+ _OverflowChromatic("Overflow Chromatic", Range(0, 0.04)) = 0.006
+ _OverflowNoise("Overflow Noise", Range(0, 0.5)) = 0.08
+ }
+
+ SubShader
+ {
+ Tags
+ {
+ "Queue" = "Transparent"
+ "RenderType" = "Transparent"
+ "RenderPipeline" = "UniversalPipeline"
+ "IgnoreProjector" = "True"
+ "CanUseSpriteAtlas" = "True"
+ }
+
+ Cull Off
+ ZWrite Off
+ Blend SrcAlpha OneMinusSrcAlpha
+
+ HLSLINCLUDE
+ #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
+
+ struct Attributes
+ {
+ float4 positionOS : POSITION;
+ float2 uv : TEXCOORD0;
+ float4 color : COLOR;
+ };
+
+ struct Varyings
+ {
+ float4 positionHCS : SV_POSITION;
+ float2 uv : TEXCOORD0;
+ float4 color : COLOR;
+ float4 screenPosition : TEXCOORD1;
+ };
+
+ TEXTURE2D(_MainTex);
+ SAMPLER(sampler_MainTex);
+ float4 _MainTex_ST;
+ float4 _MainTex_TexelSize;
+ float4 _Color;
+ float _OverflowIntensity;
+ float _OverflowExposure;
+ float _OverflowGlow;
+ float _OverflowChromatic;
+ float _OverflowNoise;
+
+ float hash21(float2 value)
+ {
+ value = frac(value * float2(123.34, 456.21));
+ value += dot(value, value + 45.32);
+ return frac(value.x * value.y);
+ }
+
+ Varyings vert(Attributes input)
+ {
+ Varyings output;
+ output.positionHCS = TransformObjectToHClip(input.positionOS.xyz);
+ output.uv = TRANSFORM_TEX(input.uv, _MainTex);
+ output.color = input.color * _Color;
+ output.screenPosition = ComputeScreenPos(output.positionHCS);
+ return output;
+ }
+
+ half4 SampleSprite(float2 uv)
+ {
+ return SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv);
+ }
+
+ half4 frag(Varyings input) : SV_Target
+ {
+ float intensity = saturate(_OverflowIntensity);
+ float pulse = 0.92 + sin(_Time.y * 17.0 + input.uv.y * 23.0) * 0.08;
+ float activeIntensity = saturate(intensity * pulse);
+
+ float2 radial = input.uv - 0.5;
+ float radialLength = max(length(radial), 0.001);
+ float2 chromaticDirection = radial / radialLength;
+ float chromaticOffset = _OverflowChromatic * activeIntensity;
+
+ half4 center = SampleSprite(input.uv);
+ half red = SampleSprite(input.uv + chromaticDirection * chromaticOffset).r;
+ half blue = SampleSprite(input.uv - chromaticDirection * chromaticOffset).b;
+ half3 chromatic = half3(red, center.g, blue);
+
+ float2 texel = _MainTex_TexelSize.xy * lerp(1.5, 7.0, activeIntensity);
+ half4 blur = center * 0.2;
+ blur += SampleSprite(input.uv + float2(texel.x, 0)) * 0.1;
+ blur += SampleSprite(input.uv - float2(texel.x, 0)) * 0.1;
+ blur += SampleSprite(input.uv + float2(0, texel.y)) * 0.1;
+ blur += SampleSprite(input.uv - float2(0, texel.y)) * 0.1;
+ blur += SampleSprite(input.uv + texel) * 0.1;
+ blur += SampleSprite(input.uv - texel) * 0.1;
+ blur += SampleSprite(input.uv + float2(texel.x, -texel.y)) * 0.1;
+ blur += SampleSprite(input.uv + float2(-texel.x, texel.y)) * 0.1;
+
+ half luminance = dot(chromatic, half3(0.2126, 0.7152, 0.0722));
+ half glowLuminance = dot(blur.rgb, half3(0.2126, 0.7152, 0.0722));
+ half whiteAmount = saturate(activeIntensity * (_OverflowExposure * 0.38 + luminance * 0.8));
+ half3 exposed = lerp(chromatic, half3(1, 1, 1), whiteAmount);
+ exposed += blur.rgb * glowLuminance * _OverflowGlow * activeIntensity;
+
+ float2 screenUv = input.screenPosition.xy / max(input.screenPosition.w, 0.0001);
+ float noiseSeed = floor(_Time.y * 24.0);
+ float noise = hash21(floor(screenUv * _ScreenParams.xy * 0.32) + noiseSeed);
+ float speck = smoothstep(0.82, 1.0, noise) * _OverflowNoise * activeIntensity;
+ float scan = sin((screenUv.y * _ScreenParams.y + _Time.y * 90.0) * 0.12) * 0.5 + 0.5;
+ exposed += speck * lerp(0.55, 1.0, scan);
+
+ half4 result;
+ result.rgb = exposed * input.color.rgb;
+ result.a = max(center.a, blur.a * activeIntensity * _OverflowGlow * 0.45) * input.color.a;
+ clip(result.a - 0.001);
+ return result;
+ }
+ ENDHLSL
+
+ Pass
+ {
+ Name "DreamDataOverflow2D"
+ Tags { "LightMode" = "Universal2D" }
+
+ HLSLPROGRAM
+ #pragma vertex vert
+ #pragma fragment frag
+ #pragma target 3.0
+ ENDHLSL
+ }
+
+ Pass
+ {
+ Name "DreamDataOverflowUnlit"
+ Tags { "LightMode" = "SRPDefaultUnlit" }
+
+ HLSLPROGRAM
+ #pragma vertex vert
+ #pragma fragment frag
+ #pragma target 3.0
+ ENDHLSL
+ }
+ }
+
+ Fallback "Sprites/Default"
+}
diff --git a/Assets/GameContent/Scene_Dream/Materials/DreamDataOverflow.shader.meta b/Assets/GameContent/Scene_Dream/Materials/DreamDataOverflow.shader.meta
new file mode 100644
index 000000000..a2f307aeb
--- /dev/null
+++ b/Assets/GameContent/Scene_Dream/Materials/DreamDataOverflow.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 6c30d8e44b1b4b4ab9414a90e069f290
+ShaderImporter:
+ externalObjects: {}
+ defaultTextures: []
+ nonModifiableTextures: []
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scenes/梦境.unity b/Assets/Scenes/梦境.unity
index aaa0635f8..cdb494efa 100644
--- a/Assets/Scenes/梦境.unity
+++ b/Assets/Scenes/梦境.unity
@@ -325,6 +325,7 @@ GameObject:
- component: {fileID: 132823524}
- component: {fileID: 132823525}
- component: {fileID: 132823526}
+ - component: {fileID: 132823527}
m_Layer: 0
m_Name: "\u573A\u666F\u52A8\u753B"
m_TagString: Untagged
@@ -406,6 +407,36 @@ MonoBehaviour:
autoBindCinemachineBrain: 0
resetToStartWhenFinished: 0
resetToStartDelay: 0
+--- !u!114 &132823527
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 132823522}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: c0c737b07db74d53bd47f7b140346ad4, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ director: {fileID: 132823524}
+ starfieldRenderer: {fileID: 1420961919}
+ initialBlur: 0.78
+ blurSize: 0.009
+ firstAttemptStart: 0.45
+ firstFocusDuration: 0.48
+ firstNearFocusBlur: 0.38
+ firstHoldDuration: 0.14
+ firstSlipDuration: 0.38
+ firstSlipBlur: 0.72
+ secondAttemptStart: 1.72
+ secondFocusDuration: 0.55
+ secondNearFocusBlur: 0.28
+ secondHoldDuration: 0.18
+ secondSlipDuration: 0.46
+ unresolvedBlur: 0.62
+ blurFadeStart: 4
+ blurFadeDuration: 3.2
--- !u!1 &218986882
GameObject:
m_ObjectHideFlags: 0
@@ -458,7 +489,7 @@ SpriteRenderer:
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- - {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2}
+ - {fileID: 2100000, guid: db54ed6c97bf4f05a79f53b268f0c52a, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
@@ -1036,7 +1067,7 @@ SpriteRenderer:
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- - {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2}
+ - {fileID: 2100000, guid: db54ed6c97bf4f05a79f53b268f0c52a, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
diff --git a/Assets/Scripts/Dialog System/BaseYarnCommand.cs b/Assets/Scripts/Dialog System/BaseYarnCommand.cs
index a300f15dc..4f4c24820 100644
--- a/Assets/Scripts/Dialog System/BaseYarnCommand.cs
+++ b/Assets/Scripts/Dialog System/BaseYarnCommand.cs
@@ -92,6 +92,22 @@ namespace AibisDream
yield return SpriteShowcase.Instance.ShowObj(picName, duration);
}
+ [YarnCommand("dream_overflow_on")]
+ public static IEnumerator DreamOverflowOn(string preset = "entry", float duration = 0.35f)
+ {
+ var showcase = SpriteShowcase.Instance;
+ if (showcase == null) yield break;
+ yield return showcase.SetDreamOverflow(preset, 1f, duration);
+ }
+
+ [YarnCommand("dream_overflow_off")]
+ public static IEnumerator DreamOverflowOff(float duration = 0.8f)
+ {
+ var showcase = SpriteShowcase.Instance;
+ if (showcase == null) yield break;
+ yield return showcase.SetDreamOverflow(null, 0f, duration);
+ }
+
[YarnCommand("show_sprite_motion")]
public static IEnumerator ShowSpriteMotion(string picName, string motionProfile, float duration = 0.35f)
{
diff --git a/Assets/Scripts/SceneManagement/SpriteShowcase.cs b/Assets/Scripts/SceneManagement/SpriteShowcase.cs
index 3979e0ab1..96e8ad112 100644
--- a/Assets/Scripts/SceneManagement/SpriteShowcase.cs
+++ b/Assets/Scripts/SceneManagement/SpriteShowcase.cs
@@ -20,6 +20,33 @@ namespace AibisDream
[SerializeField] private SpriteRenderer _backgroundSpriteRenderer;
[SerializeField] private SpriteRenderer _largeSpriteRenderer;
+ private struct DreamOverflowSettings
+ {
+ public float exposure;
+ public float glow;
+ public float chromatic;
+ public float noise;
+
+ public DreamOverflowSettings(float exposure, float glow, float chromatic, float noise)
+ {
+ this.exposure = exposure;
+ this.glow = glow;
+ this.chromatic = chromatic;
+ this.noise = noise;
+ }
+ }
+
+ private static readonly int OverflowIntensityId = Shader.PropertyToID("_OverflowIntensity");
+ private static readonly int OverflowExposureId = Shader.PropertyToID("_OverflowExposure");
+ private static readonly int OverflowGlowId = Shader.PropertyToID("_OverflowGlow");
+ private static readonly int OverflowChromaticId = Shader.PropertyToID("_OverflowChromatic");
+ private static readonly int OverflowNoiseId = Shader.PropertyToID("_OverflowNoise");
+
+ private MaterialPropertyBlock _dreamOverflowBlock;
+ private DreamOverflowSettings _dreamOverflowSettings;
+ private float _dreamOverflowIntensity;
+ private int _dreamOverflowTransitionVersion;
+
private SpriteRenderer _smallMotionRenderer;
private SpriteMask _smallMotionMask;
private DG.Tweening.Sequence _smallMotionSequence;
@@ -35,6 +62,8 @@ namespace AibisDream
public override void OnSingletonInit()
{
+ _dreamOverflowSettings = EntryOverflowSettings();
+ ResetDreamOverflow();
CaptureSmallBaseTransform();
// 初始状态:隐藏
@@ -49,6 +78,131 @@ namespace AibisDream
}
}
+ private void OnDisable()
+ {
+ ResetDreamOverflow();
+ }
+
+ public override void OnSingletonDestroy()
+ {
+ ResetDreamOverflow();
+ }
+
+ private static DreamOverflowSettings EntryOverflowSettings()
+ {
+ return new DreamOverflowSettings(
+ exposure: 1.45f,
+ glow: 0.65f,
+ chromatic: 0.006f,
+ noise: 0.08f);
+ }
+
+ private static DreamOverflowSettings RuptureOverflowSettings()
+ {
+ return new DreamOverflowSettings(
+ exposure: 2.4f,
+ glow: 1.15f,
+ chromatic: 0.018f,
+ noise: 0.2f);
+ }
+
+ private static DreamOverflowSettings ResolveDreamOverflowSettings(string preset)
+ {
+ var normalized = string.IsNullOrWhiteSpace(preset)
+ ? "entry"
+ : preset.Trim().ToLowerInvariant();
+
+ switch (normalized)
+ {
+ case "entry":
+ return EntryOverflowSettings();
+ case "rupture":
+ return RuptureOverflowSettings();
+ default:
+ Debug.LogWarning($"[SpriteShowcase] Unknown dream overflow preset '{preset}', using 'entry'.");
+ return EntryOverflowSettings();
+ }
+ }
+
+ ///
+ /// Drives the local overexposure/glow effect used by dream sprites.
+ /// The effect is applied through MaterialPropertyBlock so no material instances are created.
+ ///
+ public IEnumerator SetDreamOverflow(string preset, float target, float duration)
+ {
+ if (!string.IsNullOrWhiteSpace(preset))
+ {
+ _dreamOverflowSettings = ResolveDreamOverflowSettings(preset);
+ }
+
+ target = Mathf.Clamp01(target);
+ duration = Mathf.Max(0f, duration);
+
+ var version = ++_dreamOverflowTransitionVersion;
+ var start = _dreamOverflowIntensity;
+
+ if (duration <= 0f || !gameObject.activeInHierarchy)
+ {
+ _dreamOverflowIntensity = target;
+ ApplyDreamOverflow();
+ yield break;
+ }
+
+ var elapsed = 0f;
+ while (elapsed < duration)
+ {
+ if (version != _dreamOverflowTransitionVersion)
+ {
+ yield break;
+ }
+
+ elapsed += Time.deltaTime;
+ var progress = Mathf.SmoothStep(0f, 1f, Mathf.Clamp01(elapsed / duration));
+ _dreamOverflowIntensity = Mathf.Lerp(start, target, progress);
+ ApplyDreamOverflow();
+ yield return null;
+ }
+
+ if (version == _dreamOverflowTransitionVersion)
+ {
+ _dreamOverflowIntensity = target;
+ ApplyDreamOverflow();
+ }
+ }
+
+ private void ResetDreamOverflow()
+ {
+ _dreamOverflowTransitionVersion++;
+ _dreamOverflowIntensity = 0f;
+ ApplyDreamOverflow();
+ }
+
+ private void ApplyDreamOverflow()
+ {
+ ApplyDreamOverflow(_spriteRenderer);
+ ApplyDreamOverflow(_largeSpriteRenderer);
+ ApplyDreamOverflow(_smallMotionRenderer);
+ }
+
+ private void ApplyDreamOverflow(SpriteRenderer target)
+ {
+ if (target == null) return;
+
+ if (_dreamOverflowBlock == null)
+ {
+ _dreamOverflowBlock = new MaterialPropertyBlock();
+ }
+
+ target.GetPropertyBlock(_dreamOverflowBlock);
+ _dreamOverflowBlock.SetFloat(OverflowIntensityId, _dreamOverflowIntensity);
+ _dreamOverflowBlock.SetFloat(OverflowExposureId, _dreamOverflowSettings.exposure);
+ _dreamOverflowBlock.SetFloat(OverflowGlowId, _dreamOverflowSettings.glow);
+ _dreamOverflowBlock.SetFloat(OverflowChromaticId, _dreamOverflowSettings.chromatic);
+ _dreamOverflowBlock.SetFloat(OverflowNoiseId, _dreamOverflowSettings.noise);
+ target.SetPropertyBlock(_dreamOverflowBlock);
+ _dreamOverflowBlock.Clear();
+ }
+
private void CaptureSmallBaseTransform()
{
if (_spriteRenderer == null) return;
@@ -88,6 +242,7 @@ namespace AibisDream
CopySmallRendererSettings(_smallMotionRenderer, 1, SpriteMaskInteraction.VisibleInsideMask);
_smallMotionRenderer.gameObject.SetActive(false);
_smallMotionRenderer.SetAlpha(0);
+ ApplyDreamOverflow(_smallMotionRenderer);
}
private void CopySmallRendererSettings(SpriteRenderer target, int sortingOrderOffset, SpriteMaskInteraction maskInteraction)
diff --git a/Assets/Scripts/SceneManagement/TimelineKit/StarfieldDefocusController.cs b/Assets/Scripts/SceneManagement/TimelineKit/StarfieldDefocusController.cs
new file mode 100644
index 000000000..e11595444
--- /dev/null
+++ b/Assets/Scripts/SceneManagement/TimelineKit/StarfieldDefocusController.cs
@@ -0,0 +1,174 @@
+using UnityEngine;
+using UnityEngine.Playables;
+
+namespace AibisDream
+{
+ ///
+ /// 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.
+ ///
+ 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();
+ starfieldRenderer = GetComponentInChildren(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);
+ }
+ }
+}
diff --git a/Assets/Scripts/SceneManagement/TimelineKit/StarfieldDefocusController.cs.meta b/Assets/Scripts/SceneManagement/TimelineKit/StarfieldDefocusController.cs.meta
new file mode 100644
index 000000000..c2b938490
--- /dev/null
+++ b/Assets/Scripts/SceneManagement/TimelineKit/StarfieldDefocusController.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c0c737b07db74d53bd47f7b140346ad4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Yarn/FP/FP_Day2_sleep/FP_Day2_sleep.yarn b/Assets/Yarn/FP/FP_Day2_sleep/FP_Day2_sleep.yarn
index 346f5ad27..25c56e748 100644
--- a/Assets/Yarn/FP/FP_Day2_sleep/FP_Day2_sleep.yarn
+++ b/Assets/Yarn/FP/FP_Day2_sleep/FP_Day2_sleep.yarn
@@ -20,11 +20,13 @@ position: 2,-331
<>
+<>
+<>
jg: 数据溢出:未知来源的记忆… #line:09952c6
jg: VERYFYING…bool seaExists = false; waiting for imput… #auto_next #line:05a8257
<>
+<>
// <>
-<>
???: …下来… #line:0c8c60b
???: 下来…下来。 #line:07bfa4b
<>
@@ -43,8 +45,10 @@ me: 这到底是… #line:0ef08ad
jg: 数据溢出:未知来源的记忆… #line:007d96c
me: 啊…又来了… #line:0b0acd0
jg: VERYFYING…bool seaExists = waiting f 【WARNING: UNEXP #auto_next #line:07f7aba
-<>
-<>
+<>
+<>
+<>
+<>
// 场景闪白 #line:0d8d75a
// <>
你感到一阵眩晕。 #line:0ebd65e