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