using System; using System.Collections.Generic; using SLSUtilities.General; using UnityEngine; namespace SLSUtilities.UI { /// /// 栈式 UI 页面管理器。 /// 管理所有打开的 UIPageBase 页面,提供输入阻塞事件。 /// ESC 关闭栈顶由游戏层负责调用 CloseTopPage()。 /// 需要放置在场景中的 GameObject 上(Singleton)。 /// public class UIPageManager : Singleton { // ──────────────────── 动态页面 ──────────────────── [Header("Dynamic Pages")] [Tooltip("确认 / 提示弹窗的 Prefab(需挂载 ConfirmUIPage 组件)。")] [SerializeField] private GameObject confirmPagePrefab; [Tooltip("动态页面实例化的父节点(通常是 Canvas 下的某个 Transform)。")] [SerializeField] private Transform dynamicPageParent; // ──────────────────── 页面栈 ──────────────────── private readonly List openPages = new List(); /// /// 输入阻塞状态变化时触发。 /// true = 首个页面打开(应阻塞游戏输入),false = 所有页面已关闭(应恢复输入)。 /// 游戏层通过订阅此事件联动 isCursorLocked 等状态。 /// public event Action OnInputBlockChanged; /// 当前是否有打开的页面。 public bool HasOpenPages => openPages.Count > 0; /// 当前栈顶页面,若无则返回 null。 public UIPageBase TopPage => openPages.Count > 0 ? openPages[^1] : null; /// 确认页面的 Prefab 引用(以 GameObject 形式暴露,避免跨 Assembly 类型依赖)。 public GameObject ConfirmPagePrefab => confirmPagePrefab; // ──────────────────── 页面注册 ──────────────────── /// /// 将页面注册到栈顶。若为首个页面则触发 OnInputBlockChanged(true)。 /// 由 UIPageBase.Open() 内部调用,不要手动调用。 /// public void RegisterPage(UIPageBase page) { if (openPages.Contains(page)) return; bool wasEmpty = openPages.Count == 0; openPages.Add(page); if (wasEmpty) { OnInputBlockChanged?.Invoke(true); } } /// /// 将页面从栈中移除。若栈清空则触发 OnInputBlockChanged(false)。 /// 由 UIPageBase.Close() 内部调用,不要手动调用。 /// public void UnregisterPage(UIPageBase page) { if (!openPages.Remove(page)) return; if (openPages.Count == 0) { OnInputBlockChanged?.Invoke(false); } } // ──────────────────── 页面关闭 ──────────────────── /// /// 关闭栈顶页面(若其允许 ESC 关闭)。由游戏层的 ESC 处理逻辑调用。 /// /// true 表示成功关闭了一个页面。 public bool CloseTopPage() { if (openPages.Count == 0) return false; UIPageBase top = openPages[^1]; if (top.CloseOnEsc) { top.Close(); return true; } return false; } /// 关闭所有打开的页面。 public void CloseAllPages() { for (int i = openPages.Count - 1; i >= 0; i--) { openPages[i].Close(); } } // ──────────────────── 动态页面实例化 ──────────────────── /// /// 在 下实例化一个动态页面 Prefab。 /// 用于创建运行时弹窗(确认框、提示框等)。 /// /// 要实例化的 Prefab。 /// 实例化后的 GameObject。 public GameObject InstantiateDynamicPage(GameObject prefab) { if (prefab == null) { Debug.LogError("[UIPageManager] Cannot instantiate a null prefab."); return null; } Transform parent = dynamicPageParent != null ? dynamicPageParent : transform; return Instantiate(prefab, parent); } } }