using System.Collections; using DG.Tweening; using UnityEngine; using AibisDream.FixSystem; using AibisDream.MiniGame.Language; using System.Collections.Generic; namespace AibisDream { /// /// 表达系统顶层管理器(原 ExpressionManager,现改为语言粒子系统) /// public class ExpressionManager : MonoBehaviour { [Header("系统组件")] [SerializeField] private LanguageParticleManager particleManager; [SerializeField] private GameObject expressionView; [Header("默认配置")] [SerializeField] private List defaultAnxietyPhrases = new List { "哈哈", "嘿嘿", "呵呵" }; [SerializeField] private string defaultTargetSentence = "这是一个测试示例"; [Header("Sprite 淡入淡出")] [SerializeField] private SpriteRenderer spriteRenderer; [Header("Glitch 噪波效果")] [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() { // 注册到系统字典 FixSystemCenter.SystemDic.Register(this); // 查找并初始化表达视图 if (expressionView == null) { expressionView = transform.Find("Expression View")?.gameObject; } if (expressionView != null) { expressionView.SetActive(false); } // 查找粒子管理器 if (particleManager == null) { particleManager = GetComponentInChildren(true); } if (glitchController == null) { 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(); } } /// /// 打开表达视图(只显示美术背景,不启动粒子系统) /// 先设置所有初始状态,再打开视图 /// public void OpenView() { glitchController?.ClearCalmField(true); // 先设置所有初始状态再打开 if (particleManager != null) { particleManager.SetInitialStatesBeforeOpen(); } if (expressionView != null) { expressionView.SetActive(true); } } /// /// 启动粒子系统(在打开视图后调用,通常通过 Yarn 对话控制) /// /// 干扰短句列表(笑话等,非目标粒子从中随机取字符) /// 目标句子 /// 完成时触发的对话节点名称(可选) /// 非目标候选粒子总数;<0 时使用 Inspector 默认候选池大小 /// 干扰表现档位索引;<0 时使用粒子管理器的全局干扰参数 public void StartSystem(List anxietyPhrases, string targetSentence, string completionNodeName = null, int nonTargetParticleTotal = -1, int interferencePresetIndex = -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); } } /// /// 启动粒子系统(字符串数组版本,方便 Yarn 调用) /// public void StartSystem(string[] anxietyPhrases, string targetSentence, string completionNodeName = null, int nonTargetParticleTotal = -1, int interferencePresetIndex = -1) { List phrasesList = anxietyPhrases != null ? new List(anxietyPhrases) : null; StartSystem(phrasesList, targetSentence, completionNodeName, nonTargetParticleTotal, interferencePresetIndex); } /// /// 若火山释放 log 界面已淡出,则先淡入再返回(start_expression 时调用) /// /// 淡入时长(秒) public IEnumerator EnsureReleaseLogFadedIn(float duration = 1f) { if (particleManager == null) yield break; yield return particleManager.FadeInReleaseLogIfNeeded(duration); } /// /// 再次 start_expression 前淡出上一轮粒子与连线(若尚未初始化则立即结束)。 /// duration≤0 时使用 LanguageParticleManager 上的默认时长。 /// 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)); } /// /// 等待表达流程就绪(开场表现 + 火山表达panel 播完) /// public IEnumerator WaitUntilExpressionFlowReady() { if (particleManager == null) yield break; yield return particleManager.WaitUntilExpressionFlowReady(); } /// /// 从聚焦保持态触发稳定化(概率递增 → 文字锁定 → 排列) /// public void ResolveFocusSequence() { if (particleManager != null) { particleManager.ResolveFocusSequence(); } } /// /// 触发聚焦稳定化并等待全流程完成 /// public IEnumerator ResolveFocusAndWait() { if (particleManager == null) yield break; particleManager.ResolveFocusSequence(); yield return particleManager.WaitUntilFocusFinished(); } /// /// 关闭表达系统 /// public void CloseView() { glitchController?.ClearCalmField(true); glitchController?.TransitionTo(0f, 0f); if (expressionView != null) { expressionView.SetActive(false); } if (particleManager != null) { particleManager.StopSystem(); } } /// /// 重置系统(保持当前配置) /// public void ResetSystem(bool playEntranceEffect = true) { glitchController?.ClearCalmField(true); glitchController?.TransitionTo(0f, 0.25f); if (particleManager != null) { particleManager.ResetGame(playEntranceEffect); } } /// /// 淡入 Sprite 图片 /// /// 淡入时长(秒) public IEnumerator FadeInSpriteOverlay(float duration = 1f) { if (spriteRenderer == null) yield break; spriteRenderer.color = new Color(1f, 1f, 1f, 0f); spriteRenderer.enabled = true; spriteRenderer.gameObject.SetActive(true); yield return spriteRenderer.DOFade(0.8f, duration).WaitForCompletion(); } /// /// 淡出 Sprite 图片 /// /// 淡出时长(秒) public IEnumerator FadeOutSpriteOverlay(float duration = 1f) { if (spriteRenderer == null) yield break; yield return spriteRenderer.DOFade(0f, duration).WaitForCompletion(); spriteRenderer.enabled = false; 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)); } /// /// Glitch 噪波过渡并等待完成:从当前强度平滑过渡到 to(from 忽略,保持丝滑连续)。 /// public IEnumerator TransitionGlitchAndWait(float from, float to, float duration) { if (glitchController == null) yield break; MarkYarnGlitchOverride(); yield return glitchController.StartCoroutine(glitchController.TransitionToAndWait(to, duration)); } } }