From 046b1a9efbb7e506587e236b63ea58e39c4e0c5a Mon Sep 17 00:00:00 2001 From: bottlefish <781230111@qq.com> Date: Tue, 21 Jul 2026 17:41:46 +0800 Subject: [PATCH] =?UTF-8?q?fix(memory):=20=E4=BC=98=E5=8C=96=E5=BD=A9?= =?UTF-8?q?=E8=89=B2=E8=A4=AA=E8=89=B2=E6=89=AB=E5=85=89=E5=B9=B6=E5=A4=8D?= =?UTF-8?q?=E4=BD=8D=E6=8B=89=E6=9D=86=E8=A7=86=E8=A7=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .../Scripts/FixSystem/Memory/MemoryProcess.cs | 83 ++++++++++++++----- .../Scripts/FixSystem/Memory/MemorySlider.cs | 16 ++++ .../Shader/Shader/MemoryTuningSprite.shader | 75 +++++++++++++++-- 3 files changed, 146 insertions(+), 28 deletions(-) diff --git a/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs b/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs index c2fd49ab8..2b71050b2 100644 --- a/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs +++ b/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs @@ -15,6 +15,9 @@ namespace AibisDream.FixSystem { private const string MemorySpritePath = "Sprite/Memory/{0}.png"; private const float FadeMaterialStartValue = -15f; + // ColorFadeOut 专用:从溶解刚要开始的位置起 tween,去掉 -15→-8 的空转,让整段时长都是可见的褪色。 + private const float ColorFadeStartValue = -8.5f; + private const float ColorFadeDuration = 4f; private const float DefaultFadeDuration = 1f; private const float MemoryFinishSnowDuration = 0.1f; private const float SearchStartDelay = 1f; @@ -124,9 +127,8 @@ namespace AibisDream.FixSystem private void PrepareMemoryView() { EnsurePunchTapeSlotReference(); - PunchTapeManager.Instance.OpenFavoritesPanel(); - playBackground.gameObject.SetActive(false); - playBackground.alpha = 0; + ResolvePunchTapeManager()?.OpenFavoritesPanel(); + SetPlayBackgroundVisible(false, 0f); StartStandbyOverlay(); } @@ -139,7 +141,7 @@ namespace AibisDream.FixSystem isPlayingFaderSound = false; } - PunchTapeManager.Instance.CloseFavoritesPanel(); + ResolvePunchTapeManager()?.CloseFavoritesPanel(); CloseMemoryView(); // 离开视图时的逻辑 Debug.Log("MemoryView exited."); @@ -156,6 +158,31 @@ namespace AibisDream.FixSystem } } + private PunchTapeManager ResolvePunchTapeManager() + { + var manager = PunchTapeManager.Resolve(); + if (manager == null) + { + Debug.LogError( + "[MemoryProcess] 未找到 PunchTapeManager。请确认当前 Fix 场景中存在 PunchTapeCanvas。", + this); + } + + return manager; + } + + private void SetPlayBackgroundVisible(bool active, float alpha) + { + if (playBackground == null) + { + Debug.LogWarning("[MemoryProcess] playBackground 未配置。", this); + return; + } + + playBackground.gameObject.SetActive(active); + playBackground.alpha = alpha; + } + private void Awake() { InitSystem(); @@ -293,8 +320,9 @@ namespace AibisDream.FixSystem } /// - /// 单条记忆播放结束后的复位:不调用 (避免再次锁条并触发打开 Timeline), - /// 仅禁用拖动;完整 仅在退出 执行。 + /// 单条记忆播放结束后的复位:不调用 / + /// (避免收起 Timeline),仅禁用拖动并把拉杆视觉回到默认位置; + /// 完整 仅在退出记忆模块时 执行。 /// private void ResetAfterMemoryFinished() { @@ -307,9 +335,15 @@ namespace AibisDream.FixSystem } if (frequencySlider != null) + { frequencySlider.SetInteractable(false); + frequencySlider.ResetVisualToDefault(); + } if (claritySlider != null) + { claritySlider.SetInteractable(false); + claritySlider.ResetVisualToDefault(); + } ResetPresentationState(); } @@ -324,23 +358,24 @@ namespace AibisDream.FixSystem _activePunchTape = null; StartStandbyOverlay(); - playBackground.gameObject.SetActive(false); - playBackground.alpha = 0; + SetPlayBackgroundVisible(false, 0f); } [YarnCommand("MemoryFinished")] public IEnumerator MemoryFinished() { - yield return playBackground.DOFade(0f, DefaultFadeDuration).WaitForCompletion(); - - playBackground.gameObject.SetActive(false); + if (playBackground != null) + { + yield return playBackground.DOFade(0f, DefaultFadeDuration).WaitForCompletion(); + playBackground.gameObject.SetActive(false); + } // 短闪雪花再回待机,避免记忆画面硬切 SetMemoryColor(Color.black); ShowSearchOverlay(); yield return new WaitForSeconds(MemoryFinishSnowDuration); - PunchTapeManager.Instance.OpenFavoritesPanel(); + ResolvePunchTapeManager()?.OpenFavoritesPanel(); ResetAfterMemoryFinished(); EnsurePunchTapeSlotReference(); @@ -356,11 +391,11 @@ namespace AibisDream.FixSystem { if (fadeMaterial == null) yield break; - SetFloatIfExists(DirectionalDistortionFadeId, FadeMaterialStartValue); + SetFloatIfExists(DirectionalDistortionFadeId, ColorFadeStartValue); DG.Tweening.Sequence sequence = DOTween.Sequence(); - AppendMaterialFloatTween(sequence, fadeMaterial, 0f, DirectionalDistortionFadeProperty, 3f); + AppendMaterialFloatTween(sequence, fadeMaterial, 0f, DirectionalDistortionFadeProperty, ColorFadeDuration); if (runtimeNoColorMemoryMaterial != null && runtimeNoColorMemoryMaterial != fadeMaterial) - AppendMaterialFloatTween(sequence, runtimeNoColorMemoryMaterial, 0f, DirectionalDistortionFadeProperty, 3f); + AppendMaterialFloatTween(sequence, runtimeNoColorMemoryMaterial, 0f, DirectionalDistortionFadeProperty, ColorFadeDuration); Tween tween = sequence; yield return tween.WaitForCompletion(); } @@ -369,7 +404,8 @@ namespace AibisDream.FixSystem public IEnumerator PlayMemory(bool useYarn = false, string memName = "") { SetMemoryColor(Color.white); - PunchTapeManager.Instance.CloseFavoritesPanel(); + var punchTapeManager = ResolvePunchTapeManager(); + punchTapeManager?.CloseFavoritesPanel(); if (useYarn) { @@ -395,14 +431,15 @@ namespace AibisDream.FixSystem } } - playBackground.gameObject.SetActive(true); - playBackground.alpha = 0; + SetPlayBackgroundVisible(true, 0f); AudioManager.Instance.PlaySfx("event:/ActionFB/mem_play"); - yield return playBackground.DOFade(1f, DefaultFadeDuration).WaitForCompletion(); + if (playBackground != null) + yield return playBackground.DOFade(1f, DefaultFadeDuration).WaitForCompletion(); if (!useYarn) { - if (!PunchTapeManager.Instance.TryGetDefinition(punchTapeSlot.punchTape, out var definition)) + if (punchTapeManager == null || + !punchTapeManager.TryGetDefinition(punchTapeSlot.punchTape, out var definition)) yield break; DialogController.Instance.StartDialogNode(definition.DialogNode); @@ -419,7 +456,8 @@ namespace AibisDream.FixSystem frequencySlider.TargetValue = GetRandomSearchTarget(frequencySlider); claritySlider.TargetValue = GetRandomSearchTarget(claritySlider); - PunchTapeManager.Instance.CloseFavoritesPanel(); + var punchTapeManager = ResolvePunchTapeManager(); + punchTapeManager?.CloseFavoritesPanel(); if (punchTape == null) { @@ -430,7 +468,8 @@ namespace AibisDream.FixSystem _activePunchTape = punchTape; - if (!PunchTapeManager.Instance.TryGetDefinition(_activePunchTape, out var definition)) + if (punchTapeManager == null || + !punchTapeManager.TryGetDefinition(_activePunchTape, out var definition)) { StartStandbyOverlay(); yield break; diff --git a/Assets/Scripts/FixSystem/Memory/MemorySlider.cs b/Assets/Scripts/FixSystem/Memory/MemorySlider.cs index 146c629be..44ad2e091 100644 --- a/Assets/Scripts/FixSystem/Memory/MemorySlider.cs +++ b/Assets/Scripts/FixSystem/Memory/MemorySlider.cs @@ -63,6 +63,22 @@ namespace AibisDream.FixSystem _volume.weight = 0f; } + /// + /// 将拉杆视觉复位到默认位置并清零调谐输出(不播 Timeline、不触发搜索判定)。 + /// 用于记忆播放结束后回到待机界面。 + /// + public void ResetVisualToDefault() + { + IsFound = false; + if (slider == null) + slider = GetComponent(); + + slider.SetValueWithoutNotify(defaultValue); + if (_volume != null && driveVolume) + _volume.weight = 0f; + OnTuningChanged?.Invoke(0f); + } + /// /// 新一轮搜索前复位滑条与 IsFound(不播 Timeline、不改变视觉锁状态)。 /// 需先设置好 。 diff --git a/Assets/Shader/Shader/MemoryTuningSprite.shader b/Assets/Shader/Shader/MemoryTuningSprite.shader index 30597a706..d416a8bb0 100644 --- a/Assets/Shader/Shader/MemoryTuningSprite.shader +++ b/Assets/Shader/Shader/MemoryTuningSprite.shader @@ -21,6 +21,16 @@ Shader "AibisDream/MemoryTuningSprite" _UVDistortFade ("UV Distort Fade", Range(0,1)) = 0 _MemoryGrayscale ("Memory Grayscale", Range(0,1)) = 0 _MemoryContrast ("Memory Contrast", Range(0,3)) = 1 + _ColorEraseWidth ("Color Erase Width", Range(0.05,2)) = 0.5 + _ColorEraseNoiseScale ("Color Erase Noise Scale", Range(1,64)) = 9 + _ColorEraseNoiseFactor ("Color Erase Noise Factor", Range(0,2)) = 0.2 + _ColorEraseBias ("Color Erase Bias", Range(0,2)) = 0.6 + _ColorEraseContrast ("Color Erase Gray Contrast", Range(0,3)) = 1.8 + _ColorEraseEdgeGlow ("Color Erase Edge Glow", Range(0,3)) = 1.1 + _ColorEraseEdgeWobble ("Color Erase Edge Wobble", Range(0,0.05)) = 0.006 + _ColorEraseSpan ("Color Erase Sweep Span", Range(1,15)) = 7 + _ColorEraseEdgeSharp ("Color Erase Edge Sharpness", Range(1,10)) = 4 + _ColorEraseGlitch ("Color Erase Glitch", Range(0,1)) = 0.35 [HideInInspector] _StencilComp ("Stencil Comparison", Float) = 8 [HideInInspector] _Stencil ("Stencil ID", Float) = 0 @@ -105,8 +115,37 @@ Shader "AibisDream/MemoryTuningSprite" half _UVDistortFade; half _MemoryGrayscale; half _MemoryContrast; + half _ColorEraseWidth; + half _ColorEraseNoiseScale; + half _ColorEraseNoiseFactor; + half _ColorEraseBias; + half _ColorEraseContrast; + half _ColorEraseEdgeGlow; + half _ColorEraseEdgeWobble; + half _ColorEraseSpan; + half _ColorEraseEdgeSharp; + half _ColorEraseGlitch; CBUFFER_END + float Hash21(float2 p) + { + p = frac(p * float2(127.1, 311.7)); + p += dot(p, p + 19.19); + return frac(p.x * p.y); + } + + float ValueNoise(float2 p) + { + float2 i = floor(p); + float2 f = frac(p); + f = f * f * (3.0 - 2.0 * f); + float a = Hash21(i); + float b = Hash21(i + float2(1.0, 0.0)); + float c = Hash21(i + float2(0.0, 1.0)); + float d = Hash21(i + float2(1.0, 1.0)); + return lerp(lerp(a, b, f.x), lerp(c, d, f.x), f.y); + } + Varyings vert(Attributes input) { Varyings output; @@ -204,13 +243,25 @@ Shader "AibisDream/MemoryTuningSprite" half blur = pow(max(frequency, saturate(_GaussianBlurFade)), 0.75); half distortion = pow(max(clarity, saturate(_UVDistortFade)), 0.85); + // 复刻旧版 SSU Directional Distortion 消融:_DirectionalDistortionFade 由 -15 拉到 0, + // 前沿自上而下扫过——一条细的发亮边+极轻微扰动+一点 glitch,其余按方向纯溶解替换为黑白。 + // Span 越大,前沿在整段 fade 里铺得越开,溶解看起来越慢、发光带也越细。 + float eraseNoise = ValueNoise(input.uv * _ColorEraseNoiseScale); + half eraseMask = saturate( + (input.uv.y * _ColorEraseSpan + _DirectionalDistortionFade + _ColorEraseBias + + eraseNoise * _ColorEraseNoiseFactor) / max(_ColorEraseWidth, 0.001)); + // 前沿带:mask 在 0~1 过渡处最亮;用指数把发光带收细 + half eraseBand = saturate(eraseMask * (1.0 - eraseMask) * 4.0); + half glowBand = pow(eraseBand, _ColorEraseEdgeSharp); + float2 uv = input.uv; - half fade = saturate((_DirectionalDistortionFade + 15.0) / 15.0); - if (fade > 0.001) - { - float fadeWave = sin((uv.y + uv.x * 0.25 + _Time.y * 0.4) * 38.0); - uv.x += fadeWave * _FadeDistortStrength * fade; - } + // 前沿极轻微横向摆动(发丝级) + uv.x += sin(input.uv.y * 42.0 + _Time.y * 1.7) * _ColorEraseEdgeWobble * eraseBand; + // 一点 glitch:前沿附近偶发的水平错位切片 + float glitchRow = floor(input.uv.y * 26.0); + float glitchRand = Hash21(float2(glitchRow, floor(_Time.y * 12.0))); + float glitchSlice = step(0.82, glitchRand) * (Hash21(float2(glitchRow, 7.3)) - 0.5) * 2.0; + uv.x += glitchSlice * 0.06 * _ColorEraseGlitch * eraseBand; uv = DistortUv(uv, distortion); float2 pixelUv = PixelSnap(uv, pixelate, _PixelatePixelDensity); @@ -220,6 +271,18 @@ Shader "AibisDream/MemoryTuningSprite" half3 rgb = lerp(sharp.rgb, BlurRgb(uv, blur), blur); half4 col = half4(rgb, sharp.a); col = ChromaticSample(uv, col, clarity); + if (eraseMask > 0.001) + { + // 已扫过区域:按方向纯溶解为黑白(等价旧版露出下层黑白图) + half eraseLuma = dot(col.rgb, half3(0.299, 0.587, 0.114)); + half3 eraseGray = saturate(lerp( + half3(0.5, 0.5, 0.5), + half3(eraseLuma, eraseLuma, eraseLuma), + _ColorEraseContrast)); + col.rgb = lerp(col.rgb, eraseGray, eraseMask); + } + // 前沿细的发亮边 + col.rgb += glowBand * _ColorEraseEdgeGlow; col.a *= input.color.a; col.rgb *= input.color.rgb; return col;