using System.Collections.Generic; using Ichni.Story.Dialogue; using UnityEngine; using UnityEngine.UI; namespace Ichni.Story.UI { /// /// 对话记录覆盖面板。 /// 它不是独立 UIPage:打开时覆盖在 DialogUIPage 上方,并由全屏 Raycast Target 阻断底层输入; /// 关闭后仅恢复对话画面,不推进 Yarn 对话。 /// public class DialogueHistoryPage : MonoBehaviour { [Header("References")] [Tooltip("覆盖全屏的 CanvasGroup。根物体应位于 DialogCanvas 顶层,并包含可接收 Raycast 的全屏透明 Image。")] [SerializeField] private CanvasGroup canvasGroup; [SerializeField] private ScrollRect scrollRect; [SerializeField] private RectTransform entryContainer; [SerializeField] private DialogueHistoryEntryView entryPrefab; [SerializeField] private Button closeButton; /// 当前覆盖层是否正在显示并拦截 DialogUIPage 输入。 public bool IsOpen => _isOpen; private readonly List _spawnedEntries = new(); private bool _isOpen; private void Awake() { if (closeButton != null) closeButton.onClick.AddListener(Close); SetVisible(false); } private void OnDestroy() { if (closeButton != null) closeButton.onClick.RemoveListener(Close); } /// 以当前一次 TextBlock 对话的内存记录刷新并打开覆盖面板。 public void Open(IReadOnlyList entries) { RebuildEntries(entries); SetVisible(true); Canvas.ForceUpdateCanvases(); // 记录按时间从上到下排列;打开后停在最早一条,玩家可向下浏览最新内容。 if (scrollRect != null) scrollRect.verticalNormalizedPosition = 1f; } /// 关闭覆盖面板,不修改当前 Yarn 进度或选项状态。 public void Close() => SetVisible(false); private void RebuildEntries(IReadOnlyList entries) { foreach (DialogueHistoryEntryView view in _spawnedEntries) if (view != null) Destroy(view.gameObject); _spawnedEntries.Clear(); if (entries == null || entryPrefab == null || entryContainer == null) return; foreach (DialogueHistoryEntry entry in entries) { DialogueHistoryEntryView view = Instantiate(entryPrefab, entryContainer); view.SetUp(entry); _spawnedEntries.Add(view); } } private void SetVisible(bool visible) { _isOpen = visible; if (canvasGroup == null) return; canvasGroup.alpha = visible ? 1f : 0f; canvasGroup.interactable = visible; canvasGroup.blocksRaycasts = visible; } } }