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; [SerializeField] private LogReleasePresentationController logReleasePresentation; [Header("默认配置")] [SerializeField] private List defaultAnxietyPhrases = new List { "哈哈", "嘿嘿", "呵呵" }; [SerializeField] private string defaultTargetSentence = "这是一个测试示例"; [Header("Sprite 淡入淡出")] [SerializeField] private SpriteRenderer screenOverlayRenderer; [SerializeField] private SpriteRenderer volcanoOverlayRenderer; [SerializeField, Range(0f, 1f)] private float spriteOverlayFadeInAlpha = 1f; [Tooltip("屏幕强制保持该 RGB(只动 alpha)")] [SerializeField] private Color lockedScreenRgb = Color.black; [Header("Glitch 噪波效果")] [Tooltip("噪波故障效果控制器,可选;置于 Expression View 子物体上")] [SerializeField] private SpriteNoiseGlitchController glitchController; public LogReleasePresentationController LogReleasePresentation => logReleasePresentation; 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); } if (logReleasePresentation == null) { logReleasePresentation = GetComponent(); } if (particleManager != null) { particleManager.SetPresentationController(logReleasePresentation); } } /// /// 打开表达视图(只显示美术背景,不启动粒子系统) /// 先设置所有初始状态,再打开视图 /// public void OpenView() { // 先设置所有初始状态再打开 if (particleManager != null) { particleManager.SetInitialStatesBeforeOpen(); } if (expressionView != null) { expressionView.SetActive(true); } } /// /// 启动粒子系统(在打开视图后调用,通常通过 Yarn 对话控制) /// /// 干扰短句列表(笑话等,非目标粒子从中随机取字符) /// 目标句子 /// 完成时触发的对话节点名称(可选) /// 非目标候选粒子总数;<0 时使用 Inspector 默认候选池大小 public void StartSystem(List anxietyPhrases, string targetSentence, string completionNodeName = null, int nonTargetParticleTotal = -1) { if (particleManager != null) { particleManager.SetPresentationController(logReleasePresentation); // 使用传入的配置,如果没有则使用默认配置 var phrases = anxietyPhrases ?? defaultAnxietyPhrases; var sentence = targetSentence ?? defaultTargetSentence; particleManager.InitializeSystem(phrases, sentence, completionNodeName, nonTargetParticleTotal); } } /// /// 启动粒子系统(字符串数组版本,方便 Yarn 调用) /// 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); } /// /// 若火山释放 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) { if (particleManager == null) yield break; yield return StartCoroutine(particleManager.FadeOutBeforeNewExpressionRound(duration)); } public IEnumerator PreparePresentationForNextRound() { if (logReleasePresentation == null) yield break; yield return logReleasePresentation.PrepareForNextRound(); } /// /// 等待表达流程就绪(开场表现 + 火山表达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 IEnumerator PlayFocusInterferenceAndWait( string direction, float duration, string characterPool, string colorHex, float shakeAmplitude, float shakeSpeed, float changeInterval, float transitionRatio) { if (particleManager == null) yield break; yield return particleManager.PlayFocusInterferenceAndWait( direction, duration, characterPool, colorHex, shakeAmplitude, shakeSpeed, changeInterval, transitionRatio); } /// /// 开发预览:跳过玩法,直接进入 FocusHolding(目标字已就位)。 /// public IEnumerator SkipToFocusHoldingPreview() { if (particleManager == null) yield break; if (!particleManager.IsInitialized) yield break; yield return particleManager.WaitUntilExpressionFlowReady(); particleManager.DebugCompleteTargets(); float timeout = 8f; float elapsed = 0f; while (elapsed < timeout && particleManager.CurrentCompletionPhase != CompletionPhase.FocusHolding && particleManager.CurrentCompletionPhase != CompletionPhase.Focusing) { elapsed += Time.deltaTime; yield return null; } while (particleManager.CurrentCompletionPhase == CompletionPhase.Focusing) yield return null; } /// /// 关闭表达系统 /// public void CloseView() { if (logReleasePresentation != null) { logReleasePresentation.ForceCleanupImmediate(true); } if (expressionView != null) { expressionView.SetActive(false); } if (particleManager != null) { particleManager.StopSystem(); } } /// /// 重置系统(保持当前配置) /// public void ResetSystem(bool playEntranceEffect = true) { if (logReleasePresentation != null) { logReleasePresentation.ForceCleanupImmediate(true); } if (particleManager != null) { particleManager.ResetGame(playEntranceEffect); } } /// /// 淡入 Sprite 图片(屏幕、火山同步) /// /// 淡入时长(秒) public IEnumerator FadeInSpriteOverlay(float duration = 1f) { List renderers = CollectSpriteOverlayRenderers(); if (renderers.Count == 0) yield break; float fadeDuration = Mathf.Max(0.01f, duration); float targetAlpha = Mathf.Clamp01(spriteOverlayFadeInAlpha); Sequence sequence = DOTween.Sequence().SetTarget(this); for (int i = 0; i < renderers.Count; i++) { SpriteRenderer sr = renderers[i]; DOTween.Kill(sr); PrepareOverlayRendererForFade(sr, 0f); sr.enabled = true; sr.gameObject.SetActive(true); sequence.Join(sr.DOFade(targetAlpha, fadeDuration).SetEase(Ease.OutQuad)); } yield return sequence.WaitForCompletion(); for (int i = 0; i < renderers.Count; i++) LockScreenOverlayRgb(renderers[i], renderers[i] != null ? renderers[i].color.a : 0f); } /// /// 淡出 Sprite 图片(屏幕、火山同步) /// /// 淡出时长(秒) public IEnumerator FadeOutSpriteOverlay(float duration = 1f) { List renderers = CollectSpriteOverlayRenderers(); if (renderers.Count == 0) yield break; float fadeDuration = Mathf.Max(0.01f, duration); Sequence sequence = DOTween.Sequence().SetTarget(this); for (int i = 0; i < renderers.Count; i++) { SpriteRenderer sr = renderers[i]; DOTween.Kill(sr); LockScreenOverlayRgb(sr, sr.color.a); sr.enabled = true; sr.gameObject.SetActive(true); sequence.Join(sr.DOFade(0f, fadeDuration).SetEase(Ease.InQuad)); } yield return sequence.WaitForCompletion(); for (int i = 0; i < renderers.Count; i++) { SpriteRenderer sr = renderers[i]; if (sr == null) continue; LockScreenOverlayRgb(sr, 0f); sr.enabled = false; sr.gameObject.SetActive(false); } } private List CollectSpriteOverlayRenderers() { var result = new List(); if (screenOverlayRenderer != null) result.Add(screenOverlayRenderer); if (volcanoOverlayRenderer != null && volcanoOverlayRenderer != screenOverlayRenderer) result.Add(volcanoOverlayRenderer); return result; } private bool IsScreenOverlayRenderer(SpriteRenderer sr) { return sr != null && sr == screenOverlayRenderer; } private void PrepareOverlayRendererForFade(SpriteRenderer sr, float alpha) { if (sr == null) return; if (IsScreenOverlayRenderer(sr)) { SuppressScreenMaterialGlow(sr); LockScreenOverlayRgb(sr, alpha); return; } Color color = sr.color; color.a = alpha; sr.color = color; } private void LockScreenOverlayRgb(SpriteRenderer sr, float alpha) { if (!IsScreenOverlayRenderer(sr)) return; Color locked = lockedScreenRgb; locked.a = Mathf.Clamp01(alpha); sr.color = locked; } private static void SuppressScreenMaterialGlow(SpriteRenderer sr) { if (sr == null) return; Material mat = sr.material; if (mat == null) return; if (mat.HasProperty("_Glow")) mat.SetFloat("_Glow", 0f); if (mat.HasProperty("_GlowGlobal")) mat.SetFloat("_GlowGlobal", 1f); } /// /// Glitch 噪波过渡:从当前强度平滑过渡到 to,不瞬移(类似 DOTween)。 /// public void TransitionGlitch(float to, float duration = 0f) { if (glitchController == null) return; glitchController.TransitionTo(to, duration); } /// /// Glitch 噪波过渡并等待完成:从当前强度平滑过渡到 to。 /// public IEnumerator TransitionGlitchAndWait(float to, float duration = 0f) { if (glitchController == null) yield break; 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; yield return glitchController.StartCoroutine(glitchController.TransitionToAndWait(to, duration)); } public IEnumerator BeginExpressionMemory( string memoryKey, float fadeDuration = -1f, float pushSpeed = -1f, float horizontalSpeed = -1f, bool preserveGlitch = false, float targetOpacity = 1f) { if (logReleasePresentation == null) { Debug.LogError("[ExpressionManager] LogReleasePresentationController 未配置;记忆命令安全结束。"); yield break; } yield return logReleasePresentation.BeginMemory( memoryKey, fadeDuration, pushSpeed, horizontalSpeed, preserveGlitch, targetOpacity); } public IEnumerator BeginExpressionLie(string keyword, string preset) { if (logReleasePresentation == null) { Debug.LogError("[ExpressionManager] LogReleasePresentationController 未配置;谎话命令安全结束。"); yield break; } yield return logReleasePresentation.BeginLie(keyword, preset); } public IEnumerator BreakExpressionLie() { if (logReleasePresentation == null) yield break; yield return logReleasePresentation.BreakLie(); } public IEnumerator RevealExpressionTruth() { if (logReleasePresentation == null) yield break; yield return logReleasePresentation.RevealTruth(); } public IEnumerator EndExpressionMemory() { if (logReleasePresentation == null) yield break; yield return logReleasePresentation.EndMemory(); } public void ImpactExpressionTruth() { logReleasePresentation?.TruthImpact(); } private void OnDisable() { if (logReleasePresentation != null) logReleasePresentation.ForceCleanupImmediate(true); } } }