From 164e3dd50e7ec9952538b54d6ba046fab98e1661 Mon Sep 17 00:00:00 2001 From: bottlefish <781230111@qq.com> Date: Tue, 21 Jul 2026 00:03:31 +0800 Subject: [PATCH] =?UTF-8?q?revert(huoshan):=20=E5=B0=86=E9=87=8A=E6=94=BEl?= =?UTF-8?q?og=E8=A1=A8=E7=8E=B0=E5=9B=9E=E9=80=80=E8=87=B36=E6=9C=88?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 保留 Stage6 当前台词,仅恢复粒子与完成表现及 start_expression 参数。 Co-authored-by: Cursor --- .../HuoShan/Language/ExpressionManager.cs | 110 +-- .../Language/LanguageParticleManager.cs | 797 +++--------------- .../HuoShan/Language/LanguageYarnCommand.cs | 13 +- .../Language/README_语言系统使用说明.md | 3 - Assets/Yarn/FP/FP_Huoshan1/Stage6.yarn | 12 +- 5 files changed, 117 insertions(+), 818 deletions(-) diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionManager.cs b/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionManager.cs index f566455b6..ee27d806d 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionManager.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionManager.cs @@ -32,19 +32,6 @@ namespace AibisDream [Tooltip("噪波故障效果控制器,可选;置于 Expression View 子物体上")] [SerializeField] private SpriteNoiseGlitchController glitchController; - [Header("常驻细密噪点(不随 glitch 强度消失,营造老屏幕底噪)")] - [Tooltip("常驻噪点密度,0 = 关闭(恢复旧行为:LOG1 无噪点)")] - [SerializeField, Range(0f, 0.3f)] private float ambientNoiseDensity = 0f; - [Tooltip("噪点细密程度,越大颗粒越细(glitch 主噪点默认 80)")] - [SerializeField, Range(80f, 800f)] private float ambientNoiseScale = 360f; - [Tooltip("常驻噪点亮度(低于 glitch 主噪点,保持底噪感)")] - [SerializeField, Range(0f, 1f)] private float ambientNoiseBrightness = 0.5f; - - // 游玩期干扰联动的 glitch 基线;yarn glitch_transition 调用后暂停联动直到下一轮 - private float gameplayGlitchBaseline; - private bool yarnGlitchOverride; - private Coroutine glitchPulseRoutine; - void Start() { // 注册到系统字典 @@ -71,24 +58,6 @@ namespace AibisDream { glitchController = GetComponentInChildren(true); } - - // 释放log屏幕常驻细密噪点(shader 默认关闭,不影响其他使用该 shader 的场景) - glitchController?.SetAmbientNoise(ambientNoiseDensity, ambientNoiseScale, ambientNoiseBrightness); - } - - private void LateUpdate() - { - if (particleManager == null || glitchController == null) - return; - - if (particleManager.TryGetFocusedTextBounds(out Bounds textBounds)) - { - glitchController.SetCalmField(textBounds); - } - else - { - glitchController.ClearCalmField(); - } } /// @@ -97,8 +66,6 @@ namespace AibisDream /// public void OpenView() { - glitchController?.ClearCalmField(true); - // 先设置所有初始状态再打开 if (particleManager != null) { @@ -118,33 +85,25 @@ namespace AibisDream /// 目标句子 /// 完成时触发的对话节点名称(可选) /// 非目标候选粒子总数;<0 时使用 Inspector 默认候选池大小 - /// 干扰表现档位索引;<0 时使用粒子管理器的全局干扰参数 - public void StartSystem(List anxietyPhrases, string targetSentence, string completionNodeName = null, int nonTargetParticleTotal = -1, int interferencePresetIndex = -1) + public void StartSystem(List anxietyPhrases, string targetSentence, string completionNodeName = null, int nonTargetParticleTotal = -1) { - glitchController?.ClearCalmField(true); - - // 新一轮重置干扰联动(解除 yarn 覆盖、基线归零) - yarnGlitchOverride = false; - gameplayGlitchBaseline = 0f; - glitchController?.TransitionTo(0f, 0.25f); - if (particleManager != null) { // 使用传入的配置,如果没有则使用默认配置 var phrases = anxietyPhrases ?? defaultAnxietyPhrases; var sentence = targetSentence ?? defaultTargetSentence; - - particleManager.InitializeSystem(phrases, sentence, completionNodeName, nonTargetParticleTotal, interferencePresetIndex); + + particleManager.InitializeSystem(phrases, sentence, completionNodeName, nonTargetParticleTotal); } } /// /// 启动粒子系统(字符串数组版本,方便 Yarn 调用) /// - public void StartSystem(string[] anxietyPhrases, string targetSentence, string completionNodeName = null, int nonTargetParticleTotal = -1, int interferencePresetIndex = -1) + public void StartSystem(string[] anxietyPhrases, string targetSentence, string completionNodeName = null, int nonTargetParticleTotal = -1) { List phrasesList = anxietyPhrases != null ? new List(anxietyPhrases) : null; - StartSystem(phrasesList, targetSentence, completionNodeName, nonTargetParticleTotal, interferencePresetIndex); + StartSystem(phrasesList, targetSentence, completionNodeName, nonTargetParticleTotal); } /// @@ -163,9 +122,6 @@ namespace AibisDream /// public IEnumerator FadeOutParticlesBeforeNewRound(float duration = 0f) { - glitchController?.ClearCalmField(true); - glitchController?.TransitionTo(0f, 0.25f); - if (particleManager == null) yield break; yield return StartCoroutine(particleManager.FadeOutBeforeNewExpressionRound(duration)); } @@ -205,9 +161,6 @@ namespace AibisDream /// public void CloseView() { - glitchController?.ClearCalmField(true); - glitchController?.TransitionTo(0f, 0f); - if (expressionView != null) { expressionView.SetActive(false); @@ -224,9 +177,6 @@ namespace AibisDream /// public void ResetSystem(bool playEntranceEffect = true) { - glitchController?.ClearCalmField(true); - glitchController?.TransitionTo(0f, 0.25f); - if (particleManager != null) { particleManager.ResetGame(playEntranceEffect); @@ -258,68 +208,21 @@ namespace AibisDream spriteRenderer.gameObject.SetActive(false); } - /// - /// 游玩期干扰联动:平滑过渡到基线强度。yarn 手动驱动过 glitch 后(override)忽略, - /// 直到下一次 StartSystem 重置。 - /// - public void SetGameplayGlitchBaseline(float target, float smoothDuration = 0.5f) - { - if (glitchController == null || yarnGlitchOverride) return; - gameplayGlitchBaseline = Mathf.Clamp01(target); - if (glitchPulseRoutine != null) return; // 脉冲进行中,结束后会回落到新基线 - glitchController.TransitionTo(gameplayGlitchBaseline, smoothDuration); - } - - /// - /// Glitch 短脉冲:快速升到 peak 再回落到游玩基线(用于完成瞬间等强调时刻)。 - /// - public void PlayGlitchPulse(float peak, float duration = 0.5f) - { - if (glitchController == null || yarnGlitchOverride) return; - if (glitchPulseRoutine != null) - { - StopCoroutine(glitchPulseRoutine); - } - glitchPulseRoutine = StartCoroutine(GlitchPulseRoutine(Mathf.Clamp01(peak), duration)); - } - - private IEnumerator GlitchPulseRoutine(float peak, float duration) - { - yield return glitchController.StartCoroutine( - glitchController.TransitionToAndWait(peak, duration * 0.35f)); - yield return glitchController.StartCoroutine( - glitchController.TransitionToAndWait(gameplayGlitchBaseline, duration * 0.65f)); - glitchPulseRoutine = null; - } - /// /// Glitch 噪波过渡:从当前强度平滑过渡到 to,不瞬移(类似 DOTween)。 - /// yarn 手动驱动后,游玩期干扰联动暂停(避免互相打架)。 /// public void TransitionGlitch(float to, float duration = 0f) { if (glitchController == null) return; - MarkYarnGlitchOverride(); glitchController.TransitionTo(to, duration); } - private void MarkYarnGlitchOverride() - { - yarnGlitchOverride = true; - if (glitchPulseRoutine != null) - { - StopCoroutine(glitchPulseRoutine); - glitchPulseRoutine = null; - } - } - /// /// Glitch 噪波过渡并等待完成:从当前强度平滑过渡到 to。 /// public IEnumerator TransitionGlitchAndWait(float to, float duration = 0f) { if (glitchController == null) yield break; - MarkYarnGlitchOverride(); yield return glitchController.StartCoroutine(glitchController.TransitionToAndWait(to, duration)); } @@ -329,9 +232,8 @@ namespace AibisDream public IEnumerator TransitionGlitchAndWait(float from, float to, float duration) { if (glitchController == null) yield break; - MarkYarnGlitchOverride(); yield return glitchController.StartCoroutine(glitchController.TransitionToAndWait(to, duration)); } } -} +} \ No newline at end of file diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs b/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs index 03ca80d68..2821a6b6a 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs @@ -96,36 +96,6 @@ namespace AibisDream.MiniGame.Language [SerializeField, Range(0.1f, 1f)] private float nonTargetChangeInterval = 0.4f; // 非目标粒子变字间隔(越小越快) [SerializeField, Range(0f, 0.03f)] private float nonTargetFloatAmplitude = 0.015f; // 非目标粒子漂浮幅度 - /// - /// 按轮干扰表现档位:递进主要靠"文字更躁"传达,不实质加难 - /// - [System.Serializable] - public class InterferencePresentationPreset - { - [Tooltip("非目标粒子抖动强度")] public float shakeIntensity = 0.02f; - [Tooltip("非目标粒子抖动速度")] public float shakeSpeed = 8f; - [Tooltip("非目标粒子变字间隔(越小越快越躁)")] public float changeInterval = 0.4f; - [Tooltip("非目标粒子漂浮幅度")] public float floatAmplitude = 0.015f; - [Tooltip("干扰计数映射的 glitch 基线上限(0 关闭联动)")] [Range(0f, 0.3f)] public float glitchBaselineMax = 0f; - [Tooltip("非目标粒子向目标簇的轻微漂移加速度(仅观感,远小于玩家施力,0 关闭)")] public float driftBias = 0f; - } - - [Header("干扰表现递进(start_expression 第5参为档位索引,缺省用上方全局参数)")] - [SerializeField] private InterferencePresentationPreset[] interferencePresets = new InterferencePresentationPreset[] - { - new InterferencePresentationPreset(), - new InterferencePresentationPreset - { - shakeIntensity = 0.035f, shakeSpeed = 10f, changeInterval = 0.28f, - floatAmplitude = 0.02f, glitchBaselineMax = 0f, driftBias = 0.25f - }, - new InterferencePresentationPreset - { - shakeIntensity = 0.05f, shakeSpeed = 12f, changeInterval = 0.18f, - floatAmplitude = 0.028f, glitchBaselineMax = 0f, driftBias = 0.4f - }, - }; - [Header("连接线粗细")] [SerializeField] private float targetConnectionThickness = 0.04f; // 目标连线粗细 [SerializeField] private float nonTargetConnectionThickness = 0.02f; // 非目标连线粗细 @@ -165,42 +135,17 @@ namespace AibisDream.MiniGame.Language [SerializeField] private string expressionConfirmTimelineName = "火山表达模块/火山表达确认"; [SerializeField] private float focusZoomScale = 1.6f; [SerializeField] private float focusMoveDuration = 1.2f; - [Tooltip("聚焦阶段 UI/粒子淡出时长 = focusMoveDuration × 此比例,与粒子聚焦移动同节奏")] - [SerializeField, Range(0.3f, 1f)] private float focusFadeRatio = 0.8f; + [SerializeField] private float focusFadeDuration = 1f; [Tooltip("再次 start_expression 时,若上一轮粒子/UI 仍在,先淡出再重开;协程 duration≤0 时用此值(秒)")] [SerializeField] private float restartExpressionFadeDuration = 0.55f; [SerializeField] private float probabilityDuration = 3.5f; [SerializeField] private float predictionSequenceGap = 0.4f; [SerializeField] private float spreadDistance = 0.35f; - [Header("完成释放表现")] - [Tooltip("完成瞬间冲击波扩散时长(秒)")] - [SerializeField] private float completionShockwaveDuration = 0.6f; - [Tooltip("非目标粒子被吹散的径向冲量")] - [SerializeField] private float completionBlastForce = 3.5f; - [Tooltip("吹散后非目标粒子透明度(相对 baseAlpha 的比例)")] - [SerializeField, Range(0f, 1f)] private float completionBlastAlphaRatio = 0.35f; - [Tooltip("吹散后到进入平静态的延迟(秒)")] - [SerializeField] private float completionCalmDelay = 0.55f; - [Tooltip("完成瞬间 glitch 脉冲峰值(0 关闭)")] - [SerializeField, Range(0f, 1f)] private float completionGlitchPulsePeak = 0f; - - [Header("节奏统一")] - [Tooltip("完成时确认按钮淡入/上浮时长(秒)")] - [SerializeField] private float confirmButtonFadeDuration = 0.35f; - [Tooltip("确认按钮上浮距离(Canvas 局部单位)")] - [SerializeField] private float confirmButtonRiseOffset = 0.35f; - [Tooltip("入场 cascade 进行到该比例时并行启动火山表达panel(消除串行空档)")] - [SerializeField, Range(0f, 1f)] private float panelStartAtEntranceProgress = 0.6f; - [Tooltip("状态文字数值滚动速度(百分点/秒)")] - [SerializeField] private float statusCountUpSpeed = 90f; - [Header("聚焦表现")] [SerializeField] private Color focusRingColor = Color.white; [SerializeField, Range(0.01f, 0.3f)] private float focusRingThickness = 0.08f; [SerializeField, Range(0.05f, 1f)] private float focusRingAlpha = 0.85f; - [Tooltip("不稳定期聚焦环虚线旋转速度(DashOffset/秒)")] - [SerializeField] private float focusRingDashRotateSpeed = 0.35f; [SerializeField] private float statusFadeOutDuration = 0.45f; [SerializeField] private float shuffleDistance = 0.35f; [SerializeField] private float shuffleFrequency = 3.5f; @@ -241,6 +186,7 @@ namespace AibisDream.MiniGame.Language // 效果系统(传播效果已移除,保留空列表供渲染器使用) private static readonly List s_emptyPropagations = new List(); + private Dictionary previousConnections = new Dictionary(); private bool initializationComplete = false; private int initializationFrames = 0; @@ -274,7 +220,6 @@ namespace AibisDream.MiniGame.Language public GameObject ringObject; public Disc ringDisc; public TMP_Text probabilityLabel; - public Vector3 labelBasePos; public float probability = 90f; public float targetProbability = 100f; public float startShakeIntensity; @@ -285,50 +230,6 @@ namespace AibisDream.MiniGame.Language public int orderIndex; } - /// 聚焦阶段 UI/粒子统一淡出时长(与聚焦移动同节奏) - private float FocusUiFadeDuration => focusMoveDuration * focusFadeRatio; - - // 入场表现是否结束(panel timeline 并行播放时,两者都完成才 allowInput) - private bool entranceVisualsFinished = true; - - // 确认按钮基准位置(淡入上浮动画的落点,首次完成时捕获) - private Vector2 confirmButtonBaseAnchoredPos; - private bool confirmButtonBasePosCaptured; - - // 状态文字 count-up 显示值与脉冲检测 - private float displayedIntegrationPercent; - private float displayedInterferenceCount; - private int lastInterferenceCount = -1; - private Color interferenceTextBaseColor = Color.white; - private bool interferenceTextBaseColorCaptured; - - // 完成瞬间吹散→延迟平静协程 - private Coroutine completionBurstRoutine; - - // 当前生效的干扰表现参数(InitializeSystem 时由档位或全局参数决定) - private float activeShakeIntensity; - private float activeShakeSpeed; - private float activeChangeInterval; - private float activeFloatAmplitude; - private float activeGlitchBaselineMax; - private float activeDriftBias; - private bool interferenceActiveParamsSet; - private float glitchLinkTimer; - - // 顶层表达管理器(glitch 脉冲/联动用),粒子管理器挂在其子物体上 - private ExpressionManager expressionManagerRef; - private ExpressionManager ExpressionManagerRef - { - get - { - if (expressionManagerRef == null) - { - expressionManagerRef = GetComponentInParent(true); - } - return expressionManagerRef; - } - } - private bool focusTweensCompleted = false; // 边界 @@ -349,9 +250,6 @@ namespace AibisDream.MiniGame.Language // 是否已经初始化 private bool isInitialized = false; - // 鼠标坐标换算用主相机缓存(避免每帧 Camera.main 查找) - private Camera cachedMainCamera; - /// Inspector 中的候选粒子总数,用于未在 Yarn 中指定非目标数量时恢复默认 private int defaultCandidateCountFromInspector; private bool hasCapturedDefaultCandidateCount; @@ -363,43 +261,6 @@ namespace AibisDream.MiniGame.Language hasCapturedDefaultCandidateCount = true; } - /// - /// 选择本轮生效的干扰表现参数:有效档位用预设,否则回落到 Inspector 全局参数(行为与旧版一致) - /// - private void ApplyInterferencePreset(int presetIndex) - { - if (presetIndex >= 0 && interferencePresets != null && - presetIndex < interferencePresets.Length && interferencePresets[presetIndex] != null) - { - var p = interferencePresets[presetIndex]; - activeShakeIntensity = p.shakeIntensity; - activeShakeSpeed = p.shakeSpeed; - activeChangeInterval = p.changeInterval; - activeFloatAmplitude = p.floatAmplitude; - activeGlitchBaselineMax = p.glitchBaselineMax; - activeDriftBias = p.driftBias; - } - else - { - activeShakeIntensity = nonTargetShakeIntensity; - activeShakeSpeed = nonTargetShakeSpeed; - activeChangeInterval = nonTargetChangeInterval; - activeFloatAmplitude = nonTargetFloatAmplitude; - activeGlitchBaselineMax = 0f; - activeDriftBias = 0f; - } - interferenceActiveParamsSet = true; - glitchLinkTimer = 0f; - } - - private void EnsureActiveInterferenceParams() - { - if (!interferenceActiveParamsSet) - { - ApplyInterferencePreset(-1); - } - } - private void Start() { // Start 中不再自动初始化,等待外部调用 InitializeSystem @@ -414,11 +275,9 @@ namespace AibisDream.MiniGame.Language /// /// 非目标候选粒子总数(蓝/干扰侧可交互粒子数量)。≥0 时候选池大小 = 目标句字数 + 该值;<0 时使用 Inspector 的 Candidate Count。 /// - /// 干扰表现档位索引;<0 或越界时使用 Inspector 全局干扰参数 - public void InitializeSystem(List phrases, string sentence, string dialogNode = null, int nonTargetParticleTotal = -1, int interferencePresetIndex = -1) + public void InitializeSystem(List phrases, string sentence, string dialogNode = null, int nonTargetParticleTotal = -1) { CaptureDefaultCandidateCountFromInspector(); - ApplyInterferencePreset(interferencePresetIndex); string useSentence = string.IsNullOrEmpty(sentence) ? "这是一个测试示例" : sentence; @@ -445,8 +304,6 @@ namespace AibisDream.MiniGame.Language if (interferenceCountText != null) { interferenceTextBaseAlpha = interferenceCountText.color.a; - interferenceTextBaseColor = interferenceCountText.color; - interferenceTextBaseColorCaptured = true; } if (focusSequenceButton != null) @@ -513,11 +370,6 @@ namespace AibisDream.MiniGame.Language StopCoroutine(confirmTimelineRoutine); confirmTimelineRoutine = null; } - if (completionBurstRoutine != null) - { - StopCoroutine(completionBurstRoutine); - completionBurstRoutine = null; - } DOTween.Kill(this); foreach (var p in candidateParticles) @@ -643,13 +495,7 @@ namespace AibisDream.MiniGame.Language StopCoroutine(expressionPanelRoutine); expressionPanelRoutine = null; } - - if (completionBurstRoutine != null) - { - StopCoroutine(completionBurstRoutine); - completionBurstRoutine = null; - } - + // 停止所有 DOTween 动画 DOTween.Kill(this); foreach (var particle in candidateParticles) @@ -787,8 +633,7 @@ namespace AibisDream.MiniGame.Language particle.SetFont(chineseFontAsset); particle.SetColors(targetParticleColor, nonTargetParticleColor, nonTargetParticleColor, targetCalmColor); particle.SetFontStyles(targetParticleFontSize, nonTargetParticleFontSize, targetParticleBold, nonTargetParticleBold); - EnsureActiveInterferenceParams(); - particle.SetInterferenceParams(activeShakeIntensity, activeShakeSpeed, activeChangeInterval, activeFloatAmplitude); + particle.SetInterferenceParams(nonTargetShakeIntensity, nonTargetShakeSpeed, nonTargetChangeInterval, nonTargetFloatAmplitude); SetParticleBounds(particle); if (enableBloomGlow) @@ -963,6 +808,11 @@ namespace AibisDream.MiniGame.Language { initializationComplete = true; } + else if (initializationFrames >= 20) + { + // 建立初始连接状态 + BuildConnectionMap(); + } } HandleDebugShortcuts(); UpdateConnections(); @@ -973,6 +823,11 @@ namespace AibisDream.MiniGame.Language if (completionPhase == CompletionPhase.None) { + if (initializationComplete && !interferenceLogicSuspended) + { + DetectConnectionChanges(); + } + if (!isCompleted && CheckRedParticlesConnected()) { isCompleted = true; @@ -987,8 +842,6 @@ namespace AibisDream.MiniGame.Language if (!interferenceLogicSuspended) { ApplySeparationForces(); - ApplyInterferenceDrift(); - UpdateInterferenceGlitchLink(); } } @@ -1115,29 +968,6 @@ namespace AibisDream.MiniGame.Language particle.connections.Clear(); } - // 聚焦及之后阶段非目标粒子已淡出,只需维护目标粒子间连线,跳过全量 O(n²) - if (completionPhase == CompletionPhase.Focusing || - completionPhase == CompletionPhase.FocusHolding || - completionPhase == CompletionPhase.Finished) - { - for (int i = 0; i < targetParticles.Count; i++) - { - for (int j = i + 1; j < targetParticles.Count; j++) - { - float d = Vector3.Distance( - targetParticles[i].transform.position, - targetParticles[j].transform.position - ); - if (d < connectionDistance) - { - targetParticles[i].connections.Add(targetParticles[j]); - targetParticles[j].connections.Add(targetParticles[i]); - } - } - } - return; - } - // 完成目标后,不再创建目标粒子与非目标粒子的新连接 bool suppressTargetNonTargetConnections = isCompleted; @@ -1168,6 +998,51 @@ namespace AibisDream.MiniGame.Language } } + private void BuildConnectionMap() + { + previousConnections.Clear(); + for (int i = 0; i < candidateParticles.Count; i++) + { + for (int j = i + 1; j < candidateParticles.Count; j++) + { + float distance = Vector3.Distance( + candidateParticles[i].transform.position, + candidateParticles[j].transform.position + ); + + if (distance < connectionDistance) + { + string key = $"{Mathf.Min(i, j)}-{Mathf.Max(i, j)}"; + previousConnections[key] = true; + } + } + } + } + + private void DetectConnectionChanges() + { + Dictionary currentConnections = new Dictionary(); + + for (int i = 0; i < candidateParticles.Count; i++) + { + for (int j = i + 1; j < candidateParticles.Count; j++) + { + float distance = Vector3.Distance( + candidateParticles[i].transform.position, + candidateParticles[j].transform.position + ); + + if (distance < connectionDistance) + { + string key = $"{Mathf.Min(i, j)}-{Mathf.Max(i, j)}"; + currentConnections[key] = true; + } + } + } + + previousConnections = currentConnections; + } + private bool CheckRedParticlesConnected() { if (targetParticles.Count == 0) return false; @@ -1263,11 +1138,25 @@ namespace AibisDream.MiniGame.Language } } - // 激活 button 并淡入上浮(不可交互,播放火山表达确认后再解锁),与确认 timeline 并行 + // 首先激活 button 但不可交互,播放火山表达确认后再解锁(若之前被 fade 过,需重置 alpha) + if (focusSequenceButton != null) + { + var btnCg = focusSequenceButton.GetComponent(); + if (btnCg != null) btnCg.alpha = 1f; + focusSequenceButton.gameObject.SetActive(true); + focusSequenceButton.interactable = false; + } + if (confirmTimelineRoutine != null) + { + StopCoroutine(confirmTimelineRoutine); + } + confirmTimelineRoutine = StartCoroutine(PlayConfirmTimelineAndUnlockButton()); + foreach (var particle in candidateParticles) { if (!particle.isRed) { + particle.isCalmed = true; particle.hasEffect = false; particle.isOrange = false; particle.changeSpeedMultiplier = 1f; @@ -1278,224 +1167,6 @@ namespace AibisDream.MiniGame.Language { particle.isCalmed = true; } - - // 释放感:冲击波 + 笑话粒子被吹散 + glitch 短脉冲 - PlayCompletionBurst(); - } - - /// - /// 完成瞬间的释放表现:从目标簇质心放出冲击波环,非目标粒子被径向吹散并渐隐, - /// 延迟后进入平静态;同时打一次 glitch 短脉冲。 - /// - private void PlayCompletionBurst() - { - if (completionBurstRoutine != null) - { - StopCoroutine(completionBurstRoutine); - } - completionBurstRoutine = StartCoroutine(PlayCalmCompletionRoutine()); - } - - private IEnumerator PlayCalmCompletionRoutine() - { - float fadeDuration = Mathf.Max(0.05f, completionCalmDelay); - float moveDuration = Mathf.Max(0.05f, completionShockwaveDuration); - - if (connectionRenderer != null) - { - DOTween.To(() => connectionRenderer.NonTargetAlphaScale, v => connectionRenderer.NonTargetAlphaScale = v, 0f, fadeDuration) - .SetEase(Ease.OutQuad) - .SetTarget(this); - } - - foreach (var particle in candidateParticles) - { - if (particle == null) - continue; - - particle.velocity = Vector2.zero; - if (!particle.isRed) - { - particle.isCalmed = true; - particle.isStatic = true; - FadeOutTextParticle(particle, fadeDuration); - } - else - { - particle.isStatic = true; - particle.isCalmed = true; - particle.changeSpeedMultiplier = 1f; - } - } - - foreach (var particle in floatingParticles) - { - if (particle == null) - continue; - - particle.isCalmed = true; - particle.velocity = Vector2.zero; - FadeOutTextParticle(particle, fadeDuration); - } - - int tweenTargetCount = orderedRedParticles.Count; - int completedTweens = 0; - for (int i = 0; i < orderedRedParticles.Count; i++) - { - var particle = orderedRedParticles[i]; - if (particle == null) - { - completedTweens++; - continue; - } - - if (i < finalTargetCharacters.Count) - { - particle.ForceSetCharacter(finalTargetCharacters[i]); - } - - Vector3 targetPos = finalTargetPositions.Count > i - ? finalTargetPositions[i] - : (worldCanvas != null ? worldCanvas.transform.position : Vector3.zero); - - particle.alpha = particle.baseAlpha; - particle.transform.DOKill(); - particle.transform.DOMove(targetPos, moveDuration) - .SetEase(Ease.InOutQuad) - .OnComplete(() => completedTweens++); - particle.transform.DOScale(Vector3.one * focusZoomScale, moveDuration).SetEase(Ease.OutQuad); - } - - if (tweenTargetCount > 0) - { - while (completedTweens < tweenTargetCount) - { - yield return null; - } - } - else - { - yield return new WaitForSeconds(fadeDuration); - } - - ShowFocusSequenceButton(); - if (confirmTimelineRoutine != null) - { - StopCoroutine(confirmTimelineRoutine); - } - confirmTimelineRoutine = StartCoroutine(PlayConfirmTimelineAndUnlockButton()); - completionBurstRoutine = null; - } - - private void ShowFocusSequenceButton() - { - if (focusSequenceButton == null) - return; - - var btnCg = focusSequenceButton.GetComponent(); - if (btnCg == null) - btnCg = focusSequenceButton.gameObject.AddComponent(); - var btnRect = focusSequenceButton.transform as RectTransform; - if (btnRect != null && !confirmButtonBasePosCaptured) - { - confirmButtonBaseAnchoredPos = btnRect.anchoredPosition; - confirmButtonBasePosCaptured = true; - } - - focusSequenceButton.gameObject.SetActive(true); - focusSequenceButton.interactable = false; - - DOTween.Kill(btnCg); - btnCg.alpha = 0f; - btnCg.DOFade(1f, confirmButtonFadeDuration).SetEase(Ease.OutQuad); - if (btnRect != null) - { - btnRect.DOKill(); - btnRect.anchoredPosition = confirmButtonBaseAnchoredPos + Vector2.down * confirmButtonRiseOffset; - btnRect.DOAnchorPos(confirmButtonBaseAnchoredPos, confirmButtonFadeDuration).SetEase(Ease.OutQuad); - } - } - - private void PlayCompletionBurstLegacy() - { - Vector3 center = ComputeCentroid(targetParticles); - - // 冲击波环(Shapes Ring 扩散淡出) - GameObject waveObj = new GameObject("CompletionShockwave"); - waveObj.layer = renderLayer; - waveObj.transform.SetParent(transform, false); - float z = worldCanvas != null ? worldCanvas.transform.position.z : center.z; - waveObj.transform.position = new Vector3(center.x, center.y, z); - - var wave = waveObj.AddComponent(); - wave.Type = DiscType.Ring; - wave.Radius = 0.2f; - wave.Thickness = 0.12f; - Color waveColor = targetParticleColor; - waveColor.a = 0.9f; - wave.Color = waveColor; - - float maxRadius = layoutShape == LayoutShape.Circle - ? boundsRadius - : Mathf.Max(movementBounds.extents.x, movementBounds.extents.y); - maxRadius *= 1.1f; - Color waveEnd = waveColor; - waveEnd.a = 0f; - - Sequence waveSeq = DOTween.Sequence(); - waveSeq.Append(DOTween.To(() => wave.Radius, r => wave.Radius = r, maxRadius, completionShockwaveDuration).SetEase(Ease.OutCubic)); - waveSeq.Join(DOTween.To(() => wave.Thickness, t => wave.Thickness = t, 0.02f, completionShockwaveDuration).SetEase(Ease.OutQuad)); - waveSeq.Join(DOTween.To(() => wave.Color, c => wave.Color = c, waveEnd, completionShockwaveDuration).SetEase(Ease.OutQuad)); - waveSeq.OnComplete(() => - { - if (waveObj != null) - Destroy(waveObj); - }); - waveSeq.SetLink(waveObj); - - // 非目标粒子径向吹散 + 渐隐 - foreach (var particle in candidateParticles) - { - if (particle == null || particle.isRed) - continue; - - Vector2 dir = (Vector2)(particle.transform.position - center); - dir = dir.sqrMagnitude < 0.0001f ? Random.insideUnitCircle.normalized : dir.normalized; - particle.isCalmed = false; - particle.ApplyForce(dir * completionBlastForce); - - DOTween.To(() => particle.alpha, v => particle.alpha = v, - particle.baseAlpha * completionBlastAlphaRatio, completionCalmDelay) - .SetEase(Ease.OutQuad) - .SetTarget(particle); - } - - if (completionBurstRoutine != null) - { - StopCoroutine(completionBurstRoutine); - } - completionBurstRoutine = StartCoroutine(CalmNonTargetsAfterBlast()); - - // glitch 短脉冲 - if (completionGlitchPulsePeak > 0f && ExpressionManagerRef != null) - { - ExpressionManagerRef.PlayGlitchPulse(completionGlitchPulsePeak, 0.5f); - } - } - - private IEnumerator CalmNonTargetsAfterBlast() - { - yield return new WaitForSeconds(completionCalmDelay); - - foreach (var particle in candidateParticles) - { - if (particle != null && !particle.isRed) - { - particle.isCalmed = true; - particle.velocity = Vector2.zero; - } - } - completionBurstRoutine = null; } private List GetOrderedRedParticles() @@ -1567,15 +1238,14 @@ namespace AibisDream.MiniGame.Language interferenceLogicSuspended = true; SetStatusUIVisibility(false, statusFadeOutDuration); - // 两个 panel、confirm button、火山释放 log 界面在 focus 触发时淡出(与粒子聚焦移动同节奏) - float uiFadeDuration = FocusUiFadeDuration; - FadeOutUIElement(languageDeepPanel1, uiFadeDuration); - FadeOutUIElement(languageDeepPanel2, uiFadeDuration); + // 两个 panel、confirm button、火山释放 log 界面在 focus 触发时淡出 + FadeOutUIElement(languageDeepPanel1, focusFadeDuration); + FadeOutUIElement(languageDeepPanel2, focusFadeDuration); if (focusSequenceButton != null) { - FadeOutUIElement(focusSequenceButton.gameObject, uiFadeDuration); + FadeOutUIElement(focusSequenceButton.gameObject, focusFadeDuration); } - FadeOutSpriteRenderer(releaseLogSpriteRenderer, uiFadeDuration); + FadeOutSpriteRenderer(releaseLogSpriteRenderer, focusFadeDuration); if (connectionRenderer != null) { @@ -1587,7 +1257,7 @@ namespace AibisDream.MiniGame.Language { particle.isCalmed = true; particle.velocity = Vector2.zero; - FadeOutTextParticle(particle, uiFadeDuration); + FadeOutTextParticle(particle, focusFadeDuration); } foreach (var particle in candidateParticles) @@ -1596,7 +1266,7 @@ namespace AibisDream.MiniGame.Language { particle.isCalmed = true; particle.velocity = Vector2.zero; - FadeOutTextParticle(particle, uiFadeDuration); + FadeOutTextParticle(particle, focusFadeDuration); } else { @@ -1621,7 +1291,10 @@ namespace AibisDream.MiniGame.Language for (int i = 0; i < orderedRedParticles.Count; i++) { var particle = orderedRedParticles[i]; - Vector3 focusPosition = particle.transform.position; + Vector3 offset = particle.transform.position - focusCentroid; + float indexCenter = (orderedRedParticles.Count - 1) * 0.5f; + Vector3 spreadOffset = new Vector3((i - indexCenter) * spreadDistance, Mathf.Sin(i * 1.4f) * spreadDistance * 0.6f, 0f); + Vector3 focusPosition = focusTargetCenter + offset * focusZoomScale + spreadOffset; Vector3 finalPosition = finalTargetPositions.Count > i ? finalTargetPositions[i] : focusTargetCenter; focusPosition = ClampPositionToBounds(focusPosition, focusClipBounds); finalPosition = ClampPositionToBounds(finalPosition, focusClipBounds); @@ -1680,8 +1353,6 @@ namespace AibisDream.MiniGame.Language Color ringColor = focusRingColor; ringColor.a = focusRingAlpha; disc.Color = ringColor; - // 不稳定期为旋转虚线环,锁定时收敛为实线(见 PlayFocusLockEffect) - disc.Dashed = true; visual.ringDisc = disc; GameObject labelObj = new GameObject("ProbabilityLabel"); @@ -1691,7 +1362,6 @@ namespace AibisDream.MiniGame.Language rectTransform.localPosition = new Vector3(0f, estimatedRadius + 0.55f, 0f); rectTransform.localScale = Vector3.one; rectTransform.sizeDelta = new Vector2(2f, 0.6f); - visual.labelBasePos = rectTransform.localPosition; var label = labelObj.AddComponent(); if (chineseFontAsset != null) @@ -1700,106 +1370,12 @@ namespace AibisDream.MiniGame.Language } label.fontSize = 2f; label.alignment = TextAlignmentOptions.Center; - Color labelColor = targetParticleColor; - labelColor.a = 0.9f; - label.color = labelColor; - label.text = FormatProbabilityText(visual.probability); + label.text = $"{Mathf.RoundToInt(visual.probability)}%"; visual.probabilityLabel = label; return visual; } - /// 概率标签统一终端风格式 - private static string FormatProbabilityText(float probability) - { - return $"> {Mathf.RoundToInt(probability)}%_"; - } - - /// - /// 不稳定期聚焦环虚线旋转 + 概率标签轻微抖动(每帧调用) - /// - private void UpdateFocusVisualIdle(TargetFocusVisual visual) - { - if (visual == null || visual.stabilized) - return; - - if (visual.ringDisc != null) - { - visual.ringDisc.DashOffset += Time.deltaTime * focusRingDashRotateSpeed; - } - - if (visual.probabilityLabel != null) - { - float jitter = 0.015f; - Vector3 offset = new Vector3( - (Mathf.PerlinNoise(Time.time * 9f, visual.orderIndex * 3.7f) - 0.5f) * jitter * 2f, - (Mathf.PerlinNoise(visual.orderIndex * 5.1f, Time.time * 9f) - 0.5f) * jitter * 2f, - 0f); - visual.probabilityLabel.rectTransform.localPosition = visual.labelBasePos + offset; - } - } - - /// - /// 单字锁定表现:环收敛为实线并闪白后淡出,标签定格 100% 后淡出,字符弹跳强调 - /// - private void PlayFocusLockEffect(TargetFocusVisual visual) - { - if (visual == null) - return; - - if (visual.particle != null) - { - visual.particle.transform.DOPunchScale( - Vector3.one * (focusZoomScale * 0.15f), 0.25f, 6, 0.6f); - } - - if (visual.ringDisc != null) - { - var disc = visual.ringDisc; - var ringObj = visual.ringObject; - disc.Dashed = false; - - Color flashColor = Color.white; - flashColor.a = 1f; - Color fadeColor = focusRingColor; - fadeColor.a = 0f; - float baseRadius = disc.Radius; - - Sequence seq = DOTween.Sequence(); - seq.Append(DOTween.To(() => disc.Color, c => disc.Color = c, flashColor, 0.08f)); - seq.Join(DOTween.To(() => disc.Radius, r => disc.Radius = r, baseRadius * 0.82f, 0.16f).SetEase(Ease.OutQuad)); - seq.Append(DOTween.To(() => disc.Color, c => disc.Color = c, fadeColor, 0.4f).SetEase(Ease.OutQuad)); - seq.OnComplete(() => - { - if (ringObj != null) - Destroy(ringObj); - }); - if (ringObj != null) - seq.SetLink(ringObj); - - visual.ringDisc = null; - visual.ringObject = null; - } - - if (visual.probabilityLabel != null) - { - var label = visual.probabilityLabel; - label.rectTransform.localPosition = visual.labelBasePos; - label.text = FormatProbabilityText(100f); - var labelSeq = DOTween.Sequence(); - labelSeq.AppendInterval(0.3f); - labelSeq.Append(label.DOFade(0f, 0.3f)); - labelSeq.OnComplete(() => - { - if (label != null) - Destroy(label.gameObject); - }); - labelSeq.SetLink(label.gameObject); - - visual.probabilityLabel = null; - } - } - private void EnterFocusHolding() { completionPhase = CompletionPhase.FocusHolding; @@ -1822,7 +1398,6 @@ namespace AibisDream.MiniGame.Language Vector3 shuffleOffset = CalculateShuffleOffset(visual, 0f, focusTimer); visual.particle.transform.position = visual.focusPosition + shuffleOffset; - UpdateFocusVisualIdle(visual); } } @@ -1847,9 +1422,8 @@ namespace AibisDream.MiniGame.Language float probability = Mathf.Lerp(visual.probability, visual.targetProbability, probabilityProgress); if (visual.probabilityLabel != null) { - visual.probabilityLabel.text = FormatProbabilityText(probability); + visual.probabilityLabel.text = $"{Mathf.RoundToInt(probability)}%"; } - UpdateFocusVisualIdle(visual); float interval = Mathf.Lerp(0.05f, 0.8f, probabilityProgress); visual.particle.SetChangeInterval(interval); @@ -1868,7 +1442,10 @@ namespace AibisDream.MiniGame.Language visual.particle.ResetChangeTimer(visual.particle.changeSpeedMultiplier); visual.particle.isStatic = true; visual.stabilized = true; - PlayFocusLockEffect(visual); + if (visual.probabilityLabel != null) + { + visual.probabilityLabel.text = "100%"; + } } } @@ -1903,51 +1480,6 @@ namespace AibisDream.MiniGame.Language } } - /// - /// Returns the combined world-space bounds of the target sentence while it is - /// in the focused completion presentation. Other UI and dialogue text are excluded. - /// - public bool TryGetFocusedTextBounds(out Bounds textBounds) - { - textBounds = default; - if (completionPhase != CompletionPhase.Focusing && - completionPhase != CompletionPhase.FocusHolding && - completionPhase != CompletionPhase.Finished) - { - return false; - } - - bool hasBounds = false; - foreach (var particle in orderedRedParticles) - { - if (particle == null || particle.textMesh == null || - !particle.textMesh.gameObject.activeInHierarchy) - { - continue; - } - - Renderer textRenderer = particle.textMesh.GetComponent(); - if (textRenderer == null || !textRenderer.enabled) - continue; - - Bounds particleBounds = textRenderer.bounds; - if (particleBounds.size.sqrMagnitude <= Mathf.Epsilon) - continue; - - if (!hasBounds) - { - textBounds = particleBounds; - hasBounds = true; - } - else - { - textBounds.Encapsulate(particleBounds); - } - } - - return hasBounds; - } - private void DestroyFocusVisualDecorationsAndClear() { foreach (var visual in focusVisuals.Values) @@ -1973,7 +1505,6 @@ namespace AibisDream.MiniGame.Language DestroyFocusVisualDecorationsAndClear(); foreach (var p in particlesToScale) { - p.transform.DOKill(); p.transform.DOScale(Vector3.one * focusZoomScale, 0.3f).SetEase(Ease.OutQuad); p.isStatic = true; p.changeSpeedMultiplier = 1f; @@ -2178,11 +1709,7 @@ namespace AibisDream.MiniGame.Language private Vector2 GetMouseWorldPosition() { Vector3 mousePos = Input.mousePosition; - if (cachedMainCamera == null) - { - cachedMainCamera = Camera.main; - } - Camera cam = cachedMainCamera; + Camera cam = Camera.main; if (cam != null) { // 对于正交相机,需要使用正确的Z距离 @@ -2205,19 +1732,12 @@ namespace AibisDream.MiniGame.Language DestroyFocusVisualDecorationsAndClear(); finalTargetCharacters.Clear(); finalTargetPositions.Clear(); + previousConnections.Clear(); initializationComplete = false; initializationFrames = 0; focusTweensCompleted = false; interferenceLogicSuspended = false; ClearCompletionClampSmoothVelocity(); - displayedIntegrationPercent = 0f; - displayedInterferenceCount = 0f; - lastInterferenceCount = -1; - if (interferenceCountText != null && interferenceTextBaseColorCaptured) - { - DOTween.Kill(interferenceCountText); - interferenceCountText.color = interferenceTextBaseColor; - } SetStatusUIVisibility(true, 0f); if (entranceRoutine != null) { @@ -2237,12 +1757,6 @@ namespace AibisDream.MiniGame.Language expressionPanelRoutine = null; } - if (completionBurstRoutine != null) - { - StopCoroutine(completionBurstRoutine); - completionBurstRoutine = null; - } - if (focusSequenceButton != null) { focusSequenceButton.onClick.RemoveListener(HandleFocusButtonClicked); @@ -2281,9 +1795,7 @@ namespace AibisDream.MiniGame.Language p.alpha = p.baseAlpha; p.transform.localScale = Vector3.one; p.transform.DOKill(); - DOTween.Kill(p); - EnsureActiveInterferenceParams(); - p.SetInterferenceParams(activeShakeIntensity, activeShakeSpeed, activeChangeInterval, activeFloatAmplitude); + p.SetInterferenceParams(nonTargetShakeIntensity, nonTargetShakeSpeed, nonTargetChangeInterval, nonTargetFloatAmplitude); if (p.glowSprite != null) { @@ -2407,7 +1919,6 @@ namespace AibisDream.MiniGame.Language if (!gameObject.activeInHierarchy || candidateParticles.Count == 0) { Debug.LogWarning($"[LanguageParticleManager] PlayEntranceSequence 跳过: activeInHierarchy={gameObject.activeInHierarchy}, candidateParticles.Count={candidateParticles.Count} (不会播放火山表达panel)"); - entranceVisualsFinished = true; allowInput = true; // 否则 allowInput 会一直为 false return; } @@ -2423,14 +1934,12 @@ namespace AibisDream.MiniGame.Language private IEnumerator EntranceSequenceRoutine() { allowInput = false; - entranceVisualsFinished = false; entranceTargets.Clear(); Vector3 burstOrigin = worldCanvas != null ? worldCanvas.transform.position : transform.position; List particles = candidateParticles.Where(p => p != null).ToList(); if (particles.Count == 0) { - entranceVisualsFinished = true; allowInput = true; entranceRoutine = null; yield break; @@ -2518,15 +2027,7 @@ namespace AibisDream.MiniGame.Language float waitTime = Mathf.Max(maxCoreTime, cascadeDuration); if (waitTime > 0f) { - // cascade 进行到指定比例时并行启动火山表达panel,消除"粒子播完才出 UI"的空档 - float panelDelay = waitTime * Mathf.Clamp01(panelStartAtEntranceProgress); - yield return new WaitForSeconds(panelDelay); - StartExpressionPanelRoutine(); - yield return new WaitForSeconds(waitTime - panelDelay + 0.2f); - } - else - { - StartExpressionPanelRoutine(); + yield return new WaitForSeconds(waitTime + 0.2f); } foreach (var candidate in particles) @@ -2537,12 +2038,9 @@ namespace AibisDream.MiniGame.Language } entranceTargets.Clear(); - entranceVisualsFinished = true; entranceRoutine = null; - } - private void StartExpressionPanelRoutine() - { + // 开场表现完成,启动独立的火山表达panel协程(避免与 Entrance 耦合) if (expressionPanelRoutine != null) { StopCoroutine(expressionPanelRoutine); @@ -2583,12 +2081,6 @@ namespace AibisDream.MiniGame.Language Debug.LogWarning("[LanguageParticleManager] TimelineCenter.Instance 为空,无法播放火山表达panel"); } - // panel 与入场表现并行,两者都完成才解锁输入 - while (!entranceVisualsFinished) - { - yield return null; - } - allowInput = true; expressionPanelRoutine = null; } @@ -2632,63 +2124,6 @@ namespace AibisDream.MiniGame.Language } } - /// - /// 非目标粒子向最近目标粒子的轻微漂移偏置(仅观感"笑话往真话凑",远小于玩家施力) - /// - private void ApplyInterferenceDrift() - { - if (activeDriftBias <= 0f || isCompleted || targetParticles.Count == 0) - return; - - foreach (var particle in candidateParticles) - { - if (particle == null || particle.isRed || particle.isStatic || particle.isCalmed) - continue; - - Vector2 pos = particle.transform.position; - CandidateParticle nearest = null; - float bestSqr = float.MaxValue; - foreach (var t in targetParticles) - { - if (t == null) continue; - float sqr = ((Vector2)t.transform.position - pos).sqrMagnitude; - if (sqr < bestSqr) - { - bestSqr = sqr; - nearest = t; - } - } - - if (nearest == null || bestSqr < 0.01f) - continue; - - Vector2 dir = ((Vector2)nearest.transform.position - pos).normalized; - particle.ApplyForce(dir * (activeDriftBias * Time.deltaTime)); - } - } - - /// - /// 游玩期 glitch 基线随干扰计数轻微浮动(每 0.5s 更新一次;yarn 手动驱动后自动失效) - /// - private void UpdateInterferenceGlitchLink() - { - if (activeGlitchBaselineMax <= 0f || isCompleted || !allowInput) - return; - - glitchLinkTimer += Time.deltaTime; - if (glitchLinkTimer < 0.5f) - return; - glitchLinkTimer = 0f; - - var em = ExpressionManagerRef; - if (em == null) - return; - - // 以 6 处干扰为满强度参照,映射到 0~baselineMax - float normalized = Mathf.Clamp01(CountInterferenceNodes() / 6f); - em.SetGameplayGlitchBaseline(normalized * activeGlitchBaselineMax, 0.6f); - } - private void UpdateStatusUI() { if (statusUIHidden) @@ -2696,48 +2131,18 @@ namespace AibisDream.MiniGame.Language if (integrationProgressText != null) { - float targetPercent = CalculateIntegrationRatio() * 100f; - displayedIntegrationPercent = Mathf.MoveTowards( - displayedIntegrationPercent, targetPercent, statusCountUpSpeed * Time.deltaTime); - integrationProgressText.text = $"{Mathf.RoundToInt(displayedIntegrationPercent)}% 语言整合完成度"; + float ratio = CalculateIntegrationRatio(); + int percent = Mathf.RoundToInt(ratio * 100f); + integrationProgressText.text = $"{percent}% 语言整合完成度"; } if (interferenceCountText != null) { int interferenceCount = CountInterferenceNodes(); - if (lastInterferenceCount >= 0 && interferenceCount < lastInterferenceCount) - { - PulseInterferenceText(); - } - lastInterferenceCount = interferenceCount; - - displayedInterferenceCount = Mathf.MoveTowards( - displayedInterferenceCount, interferenceCount, statusCountUpSpeed * 0.3f * Time.deltaTime); - interferenceCountText.text = $"{Mathf.RoundToInt(displayedInterferenceCount)} 处异常思绪仍在干扰表达"; + interferenceCountText.text = $"{interferenceCount} 处异常思绪仍在干扰表达"; } } - /// - /// 干扰计数下降时闪一下目标色,给玩家正反馈 - /// - private void PulseInterferenceText() - { - if (interferenceCountText == null || statusUIHidden || !interferenceTextBaseColorCaptured) - return; - - DOTween.Kill(interferenceCountText); - Color pulse = targetParticleColor; - pulse.a = interferenceTextBaseAlpha; - Color baseColor = interferenceTextBaseColor; - baseColor.a = interferenceTextBaseAlpha; - - Sequence seq = DOTween.Sequence().SetTarget(interferenceCountText); - seq.Append(DOTween.To( - () => interferenceCountText.color, c => interferenceCountText.color = c, pulse, 0.08f)); - seq.Append(DOTween.To( - () => interferenceCountText.color, c => interferenceCountText.color = c, baseColor, 0.4f)); - } - private float CalculateIntegrationRatio() { if (targetParticles.Count == 0) diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/LanguageYarnCommand.cs b/Assets/Scripts/MiniGame/HuoShan/Language/LanguageYarnCommand.cs index 26da4f431..0b5ed66a9 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/LanguageYarnCommand.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/LanguageYarnCommand.cs @@ -19,32 +19,27 @@ namespace AibisDream.MiniGame.Language /// 注意:Yarn Spinner 2.x 对同名 重载支持不可靠,只保留一个注册入口,用可选参数区分 3/4 参调用。 /// <> /// <> - /// <> /// (第四项为「非目标」候选粒子总数,候选池 = 目标句字数 + 该值;不写第四项则用 Inspector 的 Candidate Count) - /// (第五项为干扰表现档位索引:变字节奏/抖动/glitch联动按档递进,不写则用 Inspector 全局干扰参数) /// /// 干扰短句(笑话等),用 | 分隔,用于非目标粒子字符池,如:"哈哈|嘿嘿|呵呵" /// 目标句子 /// 完成时触发的对话节点(可空字符串) /// 非目标候选粒子总数;<0 表示使用 Inspector 默认 - /// 干扰表现档位索引;<0 表示使用 Inspector 全局干扰参数 [YarnCommand("start_expression")] public static IEnumerator StartExpression( string anxietyPhrasesStr, string targetSentence, string completionNode = "", - float nonTargetParticleTotal = -1f, - float interferencePresetIndex = -1f) + float nonTargetParticleTotal = -1f) { return RunStartExpression( anxietyPhrasesStr, targetSentence, completionNode ?? "", - UnityEngine.Mathf.RoundToInt(nonTargetParticleTotal), - UnityEngine.Mathf.RoundToInt(interferencePresetIndex)); + UnityEngine.Mathf.RoundToInt(nonTargetParticleTotal)); } - private static IEnumerator RunStartExpression(string anxietyPhrasesStr, string targetSentence, string completionNode, int nonTargetParticleTotal, int interferencePresetIndex) + private static IEnumerator RunStartExpression(string anxietyPhrasesStr, string targetSentence, string completionNode, int nonTargetParticleTotal) { if (ExpressionManager == null) { @@ -65,7 +60,7 @@ namespace AibisDream.MiniGame.Language // 先淡出上一轮文字/连线等,再淡入火山释放 log,最后开新一轮粒子(避免 log 与旧字叠在一起) yield return ExpressionManager.FadeOutParticlesBeforeNewRound(0f); yield return ExpressionManager.EnsureReleaseLogFadedIn(1f); - ExpressionManager.StartSystem(phrases, targetSentence, completionNode, nonTargetParticleTotal, interferencePresetIndex); + ExpressionManager.StartSystem(phrases, targetSentence, completionNode, nonTargetParticleTotal); yield return ExpressionManager.WaitUntilExpressionFlowReady(); } diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/README_语言系统使用说明.md b/Assets/Scripts/MiniGame/HuoShan/Language/README_语言系统使用说明.md index fe36dbd3e..947b6cc97 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/README_语言系统使用说明.md +++ b/Assets/Scripts/MiniGame/HuoShan/Language/README_语言系统使用说明.md @@ -34,15 +34,12 @@ **语法:** ```yarn <> -<> ``` **参数说明:** - **焦虑短句**(必填):多个短句用 `|` 分隔,这些短句会显示在非目标字符上,表示焦虑情绪 - **目标句子**(必填):玩家需要完成的目标句子,系统会根据字数设置目标字符数量 - **完成对话节点**(可选):游戏完成时触发的对话节点名称,不填则不触发 -- **非目标粒子总数**(可选,第4参):候选池 = 目标句字数 + 该值;不填用 Inspector 的 Candidate Count -- **干扰表现档位**(可选,第5参):`LanguageParticleManager` Inspector 上 `Interference Presets` 的索引(0/1/2 递进)。只影响非目标粒子的变字节奏、抖动、漂浮、向目标簇的轻微漂移观感和 glitch 联动强度,**不实质增加操作难度**;不填用 Inspector 全局干扰参数 **示例:** ```yarn diff --git a/Assets/Yarn/FP/FP_Huoshan1/Stage6.yarn b/Assets/Yarn/FP/FP_Huoshan1/Stage6.yarn index 7e5e6475c..a7a7d0d3b 100644 --- a/Assets/Yarn/FP/FP_Huoshan1/Stage6.yarn +++ b/Assets/Yarn/FP/FP_Huoshan1/Stage6.yarn @@ -61,11 +61,11 @@ hs: 好…吧…… #line:02115ea // LOG1: 简单 - 4个目标粒子 // 主题:火山的恐惧 // 干扰词:笑话碎片(短小,2字) -// 难度:简单 - 干扰少,粒子少(非目标 10) +// 难度:简单 - 干扰少,粒子少(非目标 16) // ═══════════════════════════════════════════════════════════════ me: 这一条看起来……是一个关于焦虑的记录。 #line:046ab93d //me: NOTE:或许能找合适的方式让这句话的上下文融进来 #line:05217f0 -<> +<> === title: LOG1梳理完成 @@ -116,11 +116,11 @@ hs: 它们都说我是…… #line:0a8663b // LOG2: 中等 - 6个目标粒子 // 主题:关于英里的事 // 干扰词:更多笑话碎片(3字) -// 难度:中等 - 干扰略增,表现更躁(非目标 16,表现档1) +// 难度:中等 - 干扰增加,粒子增加(非目标 28) // ═══════════════════════════════════════════════════════════════ <> me: 这里又有一条阻塞的log,是关于恐惧的。 #line:03bf85f -<> +<> === title: LOG2梳理完成 @@ -167,11 +167,11 @@ me: 对不起…火山。 #line:03686b2a // LOG3: 困难 - 8个目标粒子 // 主题:自责与释放 // 干扰词:更多更长的笑话碎片(4-5字),干扰更强 -// 难度:困难 - 干扰最多靠表现传达(非目标 22,表现档2) +// 难度:困难 - 最多干扰,最多粒子(非目标 38) // ═══════════════════════════════════════════════════════════════ <> me: 最后一个log……这是…关于愤怒的。 #line:052abcd -<> +<> === title: LOG疏通全部完成