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;