using System.Collections.Generic; using UnityEngine; /// /// UI Canvas 层级管理器 /// 负责管理 UI 窗口的层级关系、排序和可见性控制 /// public class UICanvasLayerManager { #region Singleton private static UICanvasLayerManager _instance; public static UICanvasLayerManager Instance { get { if (_instance == null) { _instance = new UICanvasLayerManager(); } return _instance; } } private UICanvasLayerManager() { } #endregion #region Fields /// /// UICanvasLayer 到 sortingOrder 的映射 /// private static readonly Dictionary LayerToSortingOrderMap = new Dictionary { { UICanvasLayer.BackGround, 100 }, { UICanvasLayer.Normal, 200 }, { UICanvasLayer.ForeGround, 300 }, { UICanvasLayer.Tips, 400 }, { UICanvasLayer.Guide, 500 }, { UICanvasLayer.System, 600 }, }; /// /// UI层级根节点字典 /// private Dictionary _rootDic = new Dictionary(6); /// /// 每个层级的窗口列表 /// private Dictionary> _layerWindowLists = new Dictionary>(16); #endregion #region Initialization & Lifecycle /// /// 从Transform自动收集层级根节点并初始化 /// /// UIRoot的Transform,包含各层级子节点 public void InitializeFromTransform(Transform uiRootTransform) { if (uiRootTransform == null) { DebugUtil.LogError("[UICanvasLayerManager] InitializeFromTransform failed: uiRootTransform is null"); return; } _rootDic.Clear(); // 查找并注册各层级根节点 TryAddLayerRoot(uiRootTransform, "UIBackGround", UICanvasLayer.BackGround); TryAddLayerRoot(uiRootTransform, "UINormal", UICanvasLayer.Normal); TryAddLayerRoot(uiRootTransform, "UIForeGround", UICanvasLayer.ForeGround); TryAddLayerRoot(uiRootTransform, "UITips", UICanvasLayer.Tips); TryAddLayerRoot(uiRootTransform, "UIGuide", UICanvasLayer.Guide); TryAddLayerRoot(uiRootTransform, "UISystem", UICanvasLayer.System); // 初始化Canvas的sortingOrder InitializeCanvasSorting(); } /// /// 清理所有数据 /// public void Clear() { _rootDic.Clear(); _layerWindowLists.Clear(); } #endregion #region Window Registration /// /// 注册窗口到层级管理(统一入口) /// /// 使用场景: /// - 窗口创建后注册:RegisterWindow(window) 或 RegisterWindow(window, layer) /// - 运行时改变层级:RegisterWindow(window, newLayer) /// /// 特性: /// - 幂等性:可重复调用,自动处理层级变更 /// - 自动去重:同一窗口在同一层级只会存在一次 /// - 数据同步:自动更新 window.uiWindowLayer 和 Transform.parent /// /// 窗口实例 /// 目标层级(null则使用window.uiWindowLayer) public void RegisterWindow(UIWindow window, UICanvasLayer? layer = null) { if (window == null) return; var targetLayer = layer ?? window.uiWindowLayer; var oldLayer = window.uiWindowLayer; // 如果层级改变,先从旧层级移除 if (oldLayer != targetLayer) { RemoveFromLayerList(oldLayer, window); // 更新窗口数据 window.uiWindowLayer = targetLayer; SetObjectLayer(window.gameObject, targetLayer); #if UNITY_EDITOR || DEBUG || DEVELOPMENT_BUILD DebugUtil.Log($"[UICanvasLayerManager] RegisterWindow: Moved '{window.WindowName}' from {oldLayer} to {targetLayer}"); #endif } else { // 层级未变,验证现有设置是否正确 ValidateWindowPlacement(window); } // 添加到目标层级列表 AddToLayerList(targetLayer, window); } /// /// 从层级管理中注销窗口(窗口销毁前调用) /// 带容错机制:如果在预期层级找不到,会搜索所有层级 /// public void UnregisterWindow(UIWindow window) { if (window == null) return; // 尝试从预期层级移除 if (!RemoveFromLayerList(window.uiWindowLayer, window)) { // 容错:搜索所有层级 FallbackRemoveFromAllLayers(window); } } #endregion #region Query Methods /// /// 获取指定层级的所有窗口 /// public LinkedList GetWindowsInLayer(UICanvasLayer layer) { if (_layerWindowLists.TryGetValue((int)layer, out var windowList)) { return windowList; } return new LinkedList(); } /// /// 获取指定层级的最上层窗口 /// public UIWindow GetTopWindowInLayer(UICanvasLayer layer) { if (_layerWindowLists.TryGetValue((int)layer, out var windowList) && windowList.Count > 0) { return windowList.Last.Value; } return null; } /// /// 获取所有层级的顶部窗口(从高到低遍历) /// /// 输出:顶部窗口 /// 输出:窗口所在层级 /// 排除的层级掩码 /// 是否找到窗口 public bool GetTopWindow(out UIWindow window, out UICanvasLayer layer, int banLayerMask = 0) { for (int i = (int)UICanvasLayer.Max - 1; i >= 0; i--) { if (((1 << i) & banLayerMask) == 0) { if (_layerWindowLists.TryGetValue(i, out var windowList) && windowList.Count > 0) { window = windowList.Last.Value; if (window != null) { layer = (UICanvasLayer)i; return true; } } } } window = null; layer = UICanvasLayer.Max; return false; } /// /// 获取所有可见窗口列表(从最上层到最下层) /// public List GetAllVisibleWindows() { var visibleWindows = new List(); // 从最高层级到最低层级遍历 for (int i = (int)UICanvasLayer.Max - 1; i >= 0; i--) { if (_layerWindowLists.TryGetValue(i, out var windowList)) { // 从链表尾到头(最上层到最下层)遍历该层级的窗口 var node = windowList.Last; while (node != null) { var window = node.Value; if (window != null && !window.IsClosing && window.gameObject != null && window.IsShow()) { visibleWindows.Add(window); } node = node.Previous; } } } return visibleWindows; } #endregion #region Layer Operations /// /// 获取指定层级的根节点 /// public GameObject GetLayerRoot(UICanvasLayer layer) { return _rootDic.TryGetValue(layer, out var root) ? root : null; } /// /// 设置UI对象的层级(用于非窗口UI元素,如血条、行动条等动态UI元素) /// public void SetObjectLayer(GameObject go, UICanvasLayer layer) { if (go == null) { DebugUtil.LogWarning("[UICanvasLayerManager] SetObjectLayer: GameObject is null"); return; } if (!_rootDic.TryGetValue(layer, out var layerRoot)) { DebugUtil.LogWarning($"[UICanvasLayerManager] SetObjectLayer: Cannot find root for layer {layer}"); return; } go.transform.SetParent(layerRoot.transform, false); } #endregion #region Utilities /// /// 获取层级掩码 /// public int GetLayerMask(UICanvasLayer layer) { return 1 << ((int)layer); } #endregion #region Internal Helpers /// /// 尝试添加层级根节点 /// private void TryAddLayerRoot(Transform parent, string childName, UICanvasLayer layer) { var child = parent.Find(childName); if (child != null) { _rootDic.Add(layer, child.gameObject); } } /// /// 初始化Canvas的sortingOrder /// private void InitializeCanvasSorting() { foreach (var kv in LayerToSortingOrderMap) { if (_rootDic.TryGetValue(kv.Key, out var rootGo)) { var canvas = rootGo.GetComponent(); if (canvas != null) { canvas.overrideSorting = true; canvas.sortingOrder = kv.Value; DebugUtil.Log($"[UICanvasLayerManager] Set {rootGo.name} sortingOrder = {canvas.sortingOrder}"); } else { DebugUtil.LogWarning($"[UICanvasLayerManager] {rootGo.name} has no Canvas component"); } } else { DebugUtil.LogWarning($"[UICanvasLayerManager] Cannot find root for layer {kv.Key}"); } } } /// /// 添加窗口到指定层级列表 /// private void AddToLayerList(UICanvasLayer layer, UIWindow window) { var layerInt = (int)layer; // 获取或创建窗口列表 if (!_layerWindowLists.TryGetValue(layerInt, out var windowList)) { windowList = new LinkedList(); _layerWindowLists.Add(layerInt, windowList); } // 防御性去重(避免重复添加) windowList.Remove(window); // 添加到列表末尾(最上层) windowList.AddLast(window); // 设置渲染顺序(在同层级内最上层) window.transform.SetAsLastSibling(); } /// /// 从指定层级列表移除窗口 /// /// 是否成功移除 private bool RemoveFromLayerList(UICanvasLayer layer, UIWindow window) { if (_layerWindowLists.TryGetValue((int)layer, out var windowList)) { return windowList.Remove(window); } return false; } /// /// 验证窗口位置是否正确(调试模式) /// private void ValidateWindowPlacement(UIWindow window) { #if UNITY_EDITOR || DEBUG || DEVELOPMENT_BUILD var expectedRoot = GetLayerRoot(window.uiWindowLayer); if (expectedRoot != null && window.transform.parent != expectedRoot.transform) { DebugUtil.LogWarning($"[UICanvasLayerManager] RegisterWindow: '{window.WindowName}' parent mismatch! " + $"Expected {window.uiWindowLayer} ({expectedRoot.name}) but is {window.transform.parent?.name}"); } #endif } /// /// 容错:从所有层级搜索并移除窗口 /// private void FallbackRemoveFromAllLayers(UIWindow window) { #if UNITY_EDITOR || DEBUG || DEVELOPMENT_BUILD DebugUtil.LogWarning($"[UICanvasLayerManager] UnregisterWindow: '{window.WindowName}' not found in layer {window.uiWindowLayer}, searching all layers..."); #endif foreach (var kv in _layerWindowLists) { if (kv.Value.Remove(window)) { #if UNITY_EDITOR || DEBUG || DEVELOPMENT_BUILD DebugUtil.LogWarning($"[UICanvasLayerManager] Found and removed '{window.WindowName}' from layer {(UICanvasLayer)kv.Key}"); #endif break; } } } #endregion }