2025-12-19 16:15:25 +08:00
using System.Collections.Generic ;
using UnityEngine ;
/// <summary>
/// UI Canvas 层级管理器
/// 负责管理 UI 窗口的层级关系、排序和可见性控制
/// </summary>
public class UICanvasLayerManager
{
#region Singleton
2026-01-08 19:43:14 +08:00
2025-12-19 16:15:25 +08:00
private static UICanvasLayerManager _instance ;
public static UICanvasLayerManager Instance
{
get
{
if ( _instance = = null )
{
_instance = new UICanvasLayerManager ( ) ;
}
return _instance ;
}
}
private UICanvasLayerManager ( ) { }
2026-01-08 19:43:14 +08:00
2025-12-19 16:15:25 +08:00
# endregion
#region Fields
2026-01-08 19:43:14 +08:00
2025-12-19 16:15:25 +08:00
/// <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 } ,
} ;
2026-01-08 19:43:14 +08:00
/// <summary>
/// UI层级根节点字典
/// </summary>
private Dictionary < UICanvasLayer , GameObject > _rootDic = new Dictionary < UICanvasLayer , GameObject > ( 6 ) ;
2025-12-19 16:15:25 +08:00
/// <summary>
/// 每个层级的窗口列表
/// </summary>
private Dictionary < int , LinkedList < UIWindow > > _layerWindowLists = new Dictionary < int , LinkedList < UIWindow > > ( 16 ) ;
2026-01-08 19:43:14 +08:00
2025-12-19 16:15:25 +08:00
# endregion
2026-01-08 19:43:14 +08:00
#region Initialization & Lifecycle
2025-12-19 16:15:25 +08:00
/// <summary>
2026-01-08 19:43:14 +08:00
/// 从Transform自动收集层级根节点并初始化
2025-12-19 16:15:25 +08:00
/// </summary>
2026-01-08 19:43:14 +08:00
/// <param name="uiRootTransform">UIRoot的Transform, 包含各层级子节点</param>
public void InitializeFromTransform ( Transform uiRootTransform )
2025-12-19 16:15:25 +08:00
{
2026-01-08 19:43:14 +08:00
if ( uiRootTransform = = null )
2025-12-19 16:15:25 +08:00
{
2026-01-08 19:43:14 +08:00
DebugUtil . LogError ( "[UICanvasLayerManager] InitializeFromTransform failed: uiRootTransform is null" ) ;
2025-12-19 16:15:25 +08:00
return ;
}
2026-01-08 19:43:14 +08:00
_rootDic . Clear ( ) ;
2025-12-19 16:15:25 +08:00
2026-01-08 19:43:14 +08:00
// 查找并注册各层级根节点
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 ( ) ;
2025-12-19 16:15:25 +08:00
}
/// <summary>
/// 清理所有数据
/// </summary>
public void Clear ( )
{
2026-01-08 19:43:14 +08:00
_rootDic . Clear ( ) ;
2025-12-19 16:15:25 +08:00
_layerWindowLists . Clear ( ) ;
}
2026-01-08 19:43:14 +08:00
2025-12-19 16:15:25 +08:00
# endregion
2026-01-08 19:43:14 +08:00
#region Window Registration
2025-12-19 16:15:25 +08:00
/// <summary>
2026-01-08 19:43:14 +08:00
/// 注册窗口到层级管理(统一入口)
///
/// 使用场景:
/// - 窗口创建后注册: RegisterWindow(window) 或 RegisterWindow(window, layer)
/// - 运行时改变层级: RegisterWindow(window, newLayer)
///
/// 特性:
/// - 幂等性:可重复调用,自动处理层级变更
/// - 自动去重:同一窗口在同一层级只会存在一次
/// - 数据同步:自动更新 window.uiWindowLayer 和 Transform.parent
2025-12-19 16:15:25 +08:00
/// </summary>
2026-01-08 19:43:14 +08:00
/// <param name="window">窗口实例</param>
/// <param name="layer">目标层级( null则使用window.uiWindowLayer) </param>
public void RegisterWindow ( UIWindow window , UICanvasLayer ? layer = null )
2025-12-19 16:15:25 +08:00
{
if ( window = = null ) return ;
2026-01-08 19:43:14 +08:00
var targetLayer = layer ? ? window . uiWindowLayer ;
var oldLayer = window . uiWindowLayer ;
2025-12-19 16:15:25 +08:00
2026-01-08 19:43:14 +08:00
// 如果层级改变,先从旧层级移除
if ( oldLayer ! = targetLayer )
2025-12-19 16:15:25 +08:00
{
2026-01-08 19:43:14 +08:00
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 ) ;
2025-12-19 16:15:25 +08:00
}
2026-01-08 19:43:14 +08:00
// 添加到目标层级列表
AddToLayerList ( targetLayer , window ) ;
2025-12-19 16:15:25 +08:00
}
/// <summary>
2026-01-08 19:43:14 +08:00
/// 从层级管理中注销窗口(窗口销毁前调用)
/// 带容错机制:如果在预期层级找不到,会搜索所有层级
2025-12-19 16:15:25 +08:00
/// </summary>
2026-01-08 19:43:14 +08:00
public void UnregisterWindow ( UIWindow window )
2025-12-19 16:15:25 +08:00
{
if ( window = = null ) return ;
2026-01-08 19:43:14 +08:00
// 尝试从预期层级移除
if ( ! RemoveFromLayerList ( window . uiWindowLayer , window ) )
2025-12-19 16:15:25 +08:00
{
2026-01-08 19:43:14 +08:00
// 容错:搜索所有层级
FallbackRemoveFromAllLayers ( window ) ;
2025-12-19 16:15:25 +08:00
}
}
2026-01-08 19:43:14 +08:00
2025-12-19 16:15:25 +08:00
# endregion
2026-01-08 19:43:14 +08:00
#region Query Methods
2025-12-19 16:15:25 +08:00
/// <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>
2026-01-08 19:43:14 +08:00
/// <param name="window">输出:顶部窗口</param>
/// <param name="layer">输出:窗口所在层级</param>
/// <param name="banLayerMask">排除的层级掩码</param>
/// <returns>是否找到窗口</returns>
2025-12-19 16:15:25 +08:00
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>
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 ;
}
2026-01-08 19:43:14 +08:00
2025-12-19 16:15:25 +08:00
# endregion
2026-01-08 19:43:14 +08:00
#region Layer Operations
2025-12-19 16:15:25 +08:00
/// <summary>
2026-01-08 19:43:14 +08:00
/// 获取指定层级的根节点
2025-12-19 16:15:25 +08:00
/// </summary>
2026-01-08 19:43:14 +08:00
public GameObject GetLayerRoot ( UICanvasLayer layer )
2025-12-19 16:15:25 +08:00
{
2026-01-08 19:43:14 +08:00
return _rootDic . TryGetValue ( layer , out var root ) ? root : null ;
}
/// <summary>
/// 设置UI对象的层级( 用于非窗口UI元素, 如血条、行动条等动态UI元素)
/// </summary>
public void SetObjectLayer ( GameObject go , UICanvasLayer layer )
{
if ( go = = null )
2025-12-19 16:15:25 +08:00
{
2026-01-08 19:43:14 +08:00
DebugUtil . LogWarning ( "[UICanvasLayerManager] SetObjectLayer: GameObject is null" ) ;
return ;
2025-12-19 16:15:25 +08:00
}
2026-01-08 19:43:14 +08:00
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
/// <summary>
/// 获取层级掩码
/// </summary>
public int GetLayerMask ( UICanvasLayer layer )
{
return 1 < < ( ( int ) layer ) ;
2025-12-19 16:15:25 +08:00
}
2026-01-08 19:43:14 +08:00
# endregion
2025-12-19 16:15:25 +08:00
2026-01-08 19:43:14 +08:00
#region Internal Helpers
2025-12-19 16:15:25 +08:00
/// <summary>
2026-01-08 19:43:14 +08:00
/// 尝试添加层级根节点
2025-12-19 16:15:25 +08:00
/// </summary>
2026-01-08 19:43:14 +08:00
private void TryAddLayerRoot ( Transform parent , string childName , UICanvasLayer layer )
2025-12-19 16:15:25 +08:00
{
2026-01-08 19:43:14 +08:00
var child = parent . Find ( childName ) ;
if ( child ! = null )
2025-12-19 16:15:25 +08:00
{
2026-01-08 19:43:14 +08:00
_rootDic . Add ( layer , child . gameObject ) ;
2025-12-22 12:51:26 +08:00
}
}
/// <summary>
2026-01-08 19:43:14 +08:00
/// 初始化Canvas的sortingOrder
2025-12-22 12:51:26 +08:00
/// </summary>
2026-01-08 19:43:14 +08:00
private void InitializeCanvasSorting ( )
2025-12-22 12:51:26 +08:00
{
2026-01-08 19:43:14 +08:00
foreach ( var kv in LayerToSortingOrderMap )
2025-12-22 12:51:26 +08:00
{
2026-01-08 19:43:14 +08:00
if ( _rootDic . TryGetValue ( kv . Key , out var rootGo ) )
2025-12-19 16:15:25 +08:00
{
2026-01-08 19:43:14 +08:00
var canvas = rootGo . GetComponent < Canvas > ( ) ;
if ( canvas ! = null )
{
canvas . overrideSorting = true ;
canvas . sortingOrder = kv . Value ;
DebugUtil . Log ( $"[UICanvasLayerManager] Set {rootGo.name} sortingOrder = {canvas.sortingOrder}" ) ;
}
else
2025-12-19 16:15:25 +08:00
{
2026-01-08 19:43:14 +08:00
DebugUtil . LogWarning ( $"[UICanvasLayerManager] {rootGo.name} has no Canvas component" ) ;
2025-12-19 16:15:25 +08:00
}
}
2026-01-08 19:43:14 +08:00
else
{
DebugUtil . LogWarning ( $"[UICanvasLayerManager] Cannot find root for layer {kv.Key}" ) ;
}
2025-12-19 16:15:25 +08:00
}
2025-12-22 12:51:26 +08:00
}
/// <summary>
2026-01-08 19:43:14 +08:00
/// 添加窗口到指定层级列表
2025-12-22 12:51:26 +08:00
/// </summary>
2026-01-08 19:43:14 +08:00
private void AddToLayerList ( UICanvasLayer layer , UIWindow window )
2025-12-22 12:51:26 +08:00
{
2026-01-08 19:43:14 +08:00
var layerInt = ( int ) layer ;
2025-12-19 16:15:25 +08:00
2026-01-08 19:43:14 +08:00
// 获取或创建窗口列表
if ( ! _layerWindowLists . TryGetValue ( layerInt , out var windowList ) )
2025-12-22 12:51:26 +08:00
{
2026-01-08 19:43:14 +08:00
windowList = new LinkedList < UIWindow > ( ) ;
_layerWindowLists . Add ( layerInt , windowList ) ;
}
// 防御性去重(避免重复添加)
windowList . Remove ( window ) ;
// 添加到列表末尾(最上层)
windowList . AddLast ( window ) ;
// 设置渲染顺序(在同层级内最上层)
window . transform . SetAsLastSibling ( ) ;
}
/// <summary>
/// 从指定层级列表移除窗口
/// </summary>
/// <returns>是否成功移除</returns>
private bool RemoveFromLayerList ( UICanvasLayer layer , UIWindow window )
{
if ( _layerWindowLists . TryGetValue ( ( int ) layer , out var windowList ) )
{
return windowList . Remove ( window ) ;
2025-12-22 12:51:26 +08:00
}
return false ;
2025-12-19 16:15:25 +08:00
}
/// <summary>
2026-01-08 19:43:14 +08:00
/// 验证窗口位置是否正确(调试模式)
2025-12-19 16:15:25 +08:00
/// </summary>
2026-01-08 19:43:14 +08:00
private void ValidateWindowPlacement ( UIWindow window )
2025-12-19 16:15:25 +08:00
{
2026-01-08 19:43:14 +08:00
#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
2025-12-19 16:15:25 +08:00
}
2026-01-08 19:43:14 +08:00
/// <summary>
/// 容错:从所有层级搜索并移除窗口
/// </summary>
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 ;
}
}
}
2025-12-19 16:15:25 +08:00
# endregion
}