2025-12-19 16:15:25 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// UI Canvas 层级管理器
|
|
|
|
|
|
/// 负责管理 UI 窗口的层级关系、排序和可见性控制
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
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
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// UICanvasLayer 到 sortingOrder 的映射
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static readonly Dictionary<UICanvasLayer, int> LayerToSortingOrderMap = new Dictionary<UICanvasLayer, int>
|
|
|
|
|
|
{
|
|
|
|
|
|
{ UICanvasLayer.BackGround, 100 },
|
|
|
|
|
|
{ UICanvasLayer.Normal, 200 },
|
|
|
|
|
|
{ UICanvasLayer.ForeGround, 300 },
|
|
|
|
|
|
{ UICanvasLayer.Tips, 400 },
|
|
|
|
|
|
{ UICanvasLayer.Guide, 500 },
|
|
|
|
|
|
{ UICanvasLayer.System, 600 },
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 每个层级的窗口列表
|
|
|
|
|
|
/// Key: UICanvasLayer 的整数值
|
|
|
|
|
|
/// Value: 该层级的窗口链表
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Dictionary<int, LinkedList<UIWindow>> _layerWindowLists = new Dictionary<int, LinkedList<UIWindow>>(16);
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Initialization
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 初始化:为每个 Canvas 设置 sortingOrder
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="rootDic">UI层级根节点字典</param>
|
|
|
|
|
|
public void Initialize(IReadOnlyDictionary<UICanvasLayer, GameObject> rootDic)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (rootDic == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("[UICanvasLayerManager] Initialize failed: rootDic is null");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var kv in LayerToSortingOrderMap)
|
|
|
|
|
|
{
|
|
|
|
|
|
var layer = kv.Key;
|
|
|
|
|
|
|
|
|
|
|
|
if (rootDic.TryGetValue(layer, out var rootGo))
|
|
|
|
|
|
{
|
|
|
|
|
|
var canvas = rootGo.GetComponent<Canvas>();
|
|
|
|
|
|
if (canvas != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
canvas.overrideSorting = true;
|
|
|
|
|
|
canvas.sortingOrder = kv.Value;
|
|
|
|
|
|
|
|
|
|
|
|
DebugUtil.Log($"[UICanvasLayerManager] Set {rootGo.name} sortingOrder = {canvas.sortingOrder} for layer {layer}");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogWarning($"[UICanvasLayerManager] {rootGo.name} has no Canvas component!");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogWarning($"[UICanvasLayerManager] Cannot find Canvas for layer: {layer}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 清理所有数据
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Clear()
|
|
|
|
|
|
{
|
|
|
|
|
|
_layerWindowLists.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Window Management
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 将窗口添加到指定层级
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void AddWindow(UIWindow window)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (window == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
var layer = (int)window.uiWindowLayer;
|
|
|
|
|
|
|
|
|
|
|
|
if (!_layerWindowLists.TryGetValue(layer, out var windowList))
|
|
|
|
|
|
{
|
|
|
|
|
|
windowList = new LinkedList<UIWindow>();
|
|
|
|
|
|
_layerWindowLists.Add(layer, windowList);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 移除已存在的(避免重复)
|
|
|
|
|
|
windowList.Remove(window);
|
|
|
|
|
|
|
|
|
|
|
|
// 添加到列表末尾(最上层)
|
|
|
|
|
|
windowList.AddLast(window);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置 SiblingIndex(只在同层级内排序)
|
|
|
|
|
|
window.transform.SetAsLastSibling();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 从层级中移除窗口
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void RemoveWindow(UIWindow window)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (window == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
var layer = (int)window.uiWindowLayer;
|
|
|
|
|
|
|
|
|
|
|
|
if (_layerWindowLists.TryGetValue(layer, out var windowList))
|
|
|
|
|
|
{
|
|
|
|
|
|
windowList.Remove(window);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Query
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取指定层级的所有窗口
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public LinkedList<UIWindow> GetWindowsInLayer(UICanvasLayer layer)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_layerWindowLists.TryGetValue((int)layer, out var windowList))
|
|
|
|
|
|
{
|
|
|
|
|
|
return windowList;
|
|
|
|
|
|
}
|
|
|
|
|
|
return new LinkedList<UIWindow>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-25 20:26:05 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取指定层级的最上层窗口
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public UIWindow GetTopWindowInLayer(UICanvasLayer layer)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_layerWindowLists.TryGetValue((int)layer, out var windowList) && windowList.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return windowList.Last.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 16:15:25 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取所有层级的顶部窗口(从高到低遍历)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-12-25 20:26:05 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取所有可见窗口列表(从最上层到最下层)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>按显示优先级排序的可见窗口列表</returns>
|
|
|
|
|
|
public List<UIWindow> GetAllVisibleWindows()
|
|
|
|
|
|
{
|
|
|
|
|
|
var visibleWindows = new List<UIWindow>();
|
|
|
|
|
|
|
|
|
|
|
|
// 从最高层级到最低层级遍历
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-12-19 16:15:25 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Visibility Control
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 隐藏指定层级及以下的所有窗口
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public List<UIWindow> HideWindowsUpToLayer(UICanvasLayer maxLayer, params string[] exceptions)
|
|
|
|
|
|
{
|
|
|
|
|
|
var hiddenWindows = new List<UIWindow>();
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i <= (int)maxLayer; i++)
|
|
|
|
|
|
{
|
2025-12-22 12:51:26 +08:00
|
|
|
|
HideWindowsInLayerInternal((UICanvasLayer)i, hiddenWindows, exceptions);
|
2025-12-19 16:15:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return hiddenWindows;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 隐藏指定层级列表中的所有窗口
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public List<UIWindow> HideWindowsInLayers(UICanvasLayer[] layers, params string[] exceptions)
|
|
|
|
|
|
{
|
|
|
|
|
|
var hiddenWindows = new List<UIWindow>();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var layer in layers)
|
|
|
|
|
|
{
|
2025-12-22 12:51:26 +08:00
|
|
|
|
HideWindowsInLayerInternal(layer, hiddenWindows, exceptions);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return hiddenWindows;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 隐藏单个层级的所有窗口(内部方法)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void HideWindowsInLayerInternal(UICanvasLayer layer, List<UIWindow> hiddenWindows, string[] exceptions)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_layerWindowLists.TryGetValue((int)layer, out var windowList))
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var window in windowList)
|
2025-12-19 16:15:25 +08:00
|
|
|
|
{
|
2025-12-22 12:51:26 +08:00
|
|
|
|
if (window.gameObject.activeInHierarchy && !IsException(window.WindowName, exceptions))
|
2025-12-19 16:15:25 +08:00
|
|
|
|
{
|
2025-12-22 12:51:26 +08:00
|
|
|
|
hiddenWindows.Add(window);
|
2025-12-23 19:28:45 +08:00
|
|
|
|
window.CloseWindow(destroy:false);
|
2025-12-19 16:15:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-22 12:51:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 检查窗口是否在例外列表中
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool IsException(string windowName, string[] exceptions)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (exceptions == null || exceptions.Length == 0) return false;
|
2025-12-19 16:15:25 +08:00
|
|
|
|
|
2025-12-22 12:51:26 +08:00
|
|
|
|
foreach (var exception in exceptions)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (exception == windowName) return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
2025-12-19 16:15:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Utilities
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取层级掩码
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int GetLayerMask(UICanvasLayer layer)
|
|
|
|
|
|
{
|
|
|
|
|
|
return 1 << ((int)layer);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
}
|