using AibisDream.Framework; using AibisDream.Kit; using AibisDream.UI; using TMPro; using UnityEngine; namespace AibisDream { public class DialogUIManager : Singleton { [SerializeField] private LineRunner lineView; [SerializeField] private OptionView optionView; [SerializeField] private TMP_Text widthEstimation; private CanvasGroup _canvasGroup; private BubbleSlotGroupData? _pendingBubbleData; private bool _isCanvasOpen = true; private bool _isDialogVisualVisible = true; public override void OnSingletonInit() { _canvasGroup = GetComponent(); if (_canvasGroup == null) { _canvasGroup = gameObject.AddComponent(); } ApplyCanvasGroupState(); EnumEventSystem.Global.Register(DialogEventEnum.LineStart, AddDialogueLine); EnumEventSystem.Global.Register(DialogEventEnum.LineHistoryAppend, AddDialogueLine); EnumEventSystem.Global.Register(DialogEventEnum.OptionSelected, AddDialogueLine); EnumEventSystem.Global.Register(DialogEventEnum.LineEnd, ApplyPendingBubbleLoad); EnumEventSystem.Global.Register(EventEnum.NextYarn, CleanDialogHistory); EnumEventSystem.Global.Register(GameLifecycleEvent.SessionEnded, CleanDialogHistory); EnumEventSystem.Global.Register(GameLifecycleEvent.SessionEnded, ClearPendingBubbleLoad); EnumEventSystem.Global.Register(GameLifecycleEvent.SessionPaused, CloseCanvas); EnumEventSystem.Global.Register(GameLifecycleEvent.SessionResumed, OpenCanvas); } public override void OnSingletonDestroy() { ClearPendingBubbleLoad(); EnumEventSystem.Global.UnRegister(DialogEventEnum.LineStart, AddDialogueLine); EnumEventSystem.Global.UnRegister(DialogEventEnum.LineHistoryAppend, AddDialogueLine); EnumEventSystem.Global.UnRegister(DialogEventEnum.OptionSelected, AddDialogueLine); EnumEventSystem.Global.UnRegister(DialogEventEnum.LineEnd, ApplyPendingBubbleLoad); EnumEventSystem.Global.UnRegister(EventEnum.NextYarn, CleanDialogHistory); EnumEventSystem.Global.UnRegister(GameLifecycleEvent.SessionEnded, CleanDialogHistory); EnumEventSystem.Global.UnRegister(GameLifecycleEvent.SessionEnded, ClearPendingBubbleLoad); EnumEventSystem.Global.UnRegister(GameLifecycleEvent.SessionPaused, CloseCanvas); EnumEventSystem.Global.UnRegister(GameLifecycleEvent.SessionResumed, OpenCanvas); } private void AddDialogueLine(LineInfo line) { if (line.character.dialogViewType == DialogViewType.Task) { return; } var panel = UIManager.Instance.GetPanel(); panel?.AppendDialogLine(line); } private void CleanDialogHistory() { var panel = UIManager.Instance.GetPanel(); panel?.ClearDialog(); } public void CloseCanvas() { _isCanvasOpen = false; ApplyCanvasGroupState(); } public void OpenCanvas() { _isCanvasOpen = true; ApplyCanvasGroupState(); } /// /// 仅隐藏对话视觉,不影响打字机、推进与选项交互。 /// public void SetDialogVisualVisible(bool visible) { _isDialogVisualVisible = visible; ApplyCanvasGroupState(); } private void ApplyCanvasGroupState() { _canvasGroup.alpha = _isCanvasOpen && _isDialogVisualVisible ? 1f : 0f; // SetDialogVisualVisible remains visual-only. Pause-driven canvas state // exclusively controls whether dialogue UI can interact or block raycasts. _canvasGroup.interactable = _isCanvasOpen; _canvasGroup.blocksRaycasts = _isCanvasOpen; } public void HideDialog() { lineView.HideDialog(); optionView.HideDialog(); } public void RegisterScreenView(DialogViewType dialogViewType, ILineView screenView) { lineView.RegisterView(dialogViewType, screenView); } public void UnRegisterScreenView(DialogViewType dialogViewType) { lineView.UnRegisterView(dialogViewType); } public void LoadBubbles(BubbleSlotGroupData data) { var token = DialogController.Instance != null ? DialogController.Instance.lineSyncToken : null; if (token != null && token.state != LineSyncState.Advanced) { // 同一行内的连续请求采用最后一次请求,当前气泡保留到台词推进。 _pendingBubbleData = data; return; } _pendingBubbleData = null; ApplyBubbleLoadImmediately(data); } private void ApplyPendingBubbleLoad() { if (!_pendingBubbleData.HasValue) { return; } var data = _pendingBubbleData.Value; _pendingBubbleData = null; ApplyBubbleLoadImmediately(data); } private void ApplyBubbleLoadImmediately(BubbleSlotGroupData data) { lineView.LoadBubbleData(data); optionView.LoadBubbles(data); } private void ClearPendingBubbleLoad() { _pendingBubbleData = null; } public float EstimateTextWidth(string text, float fontSize) { widthEstimation.fontSize = fontSize; widthEstimation.text = text; widthEstimation.ForceMeshUpdate(); return widthEstimation.preferredWidth; } } public enum DialogViewType { Default, StandardBubble, OnelineBox, Screen, CenterText, CenterOption, Task, Log } }