using System.Collections.Generic;
///
/// UI窗口可见性管理器(纯性能优化模块)
///
/// 核心理念:
/// - 只控制 GameObject.SetActive(),仅影响渲染层面
/// - 不触发 UIWindow 生命周期(不调用 HideWindow/ShowWindow)
/// - 不改变窗口逻辑状态(_isShow 保持不变)
///
/// 优化目标:
/// - 当全屏窗口打开时,自动隐藏被遮挡的下层窗口,减少 Overdraw
/// - 当全屏窗口关闭时,自动恢复被遮挡的窗口渲染
/// - 节省 GPU 绘制开销和 CPU UI 遍历开销
///
public class UIWindowVisibilityManager
{
#region Singleton
private static UIWindowVisibilityManager _instance;
public static UIWindowVisibilityManager Instance
{
get
{
if (_instance == null)
{
_instance = new UIWindowVisibilityManager();
}
return _instance;
}
}
private UIWindowVisibilityManager() { }
#endregion
#region Fields
///
/// 复用的窗口列表,避免 GC
///
private readonly List _tempWindowList = new List(32);
#endregion
#region Visibility Operations
///
/// 处理窗口打开时的渲染优化
/// HideBelow 策略:隐藏所有被遮挡的下层窗口
///
/// 性能优化:只控制 GameObject.SetActive(false),不影响窗口逻辑状态
///
/// 新打开的窗口
/// 可见性策略
public void HandleVisibilityOnOpen(UIWindow newWindow, UIVisibilityStrategy visibilityStrategy)
{
if (newWindow == null || visibilityStrategy == UIVisibilityStrategy.None)
return;
// HideBelow 策略:基于渲染层级隐藏
if (visibilityStrategy == UIVisibilityStrategy.HideBelow)
{
var newWindowLayer = newWindow.uiWindowLayer;
var newWindowSiblingIndex = newWindow.transform.GetSiblingIndex();
var layerManager = UICanvasLayerManager.Instance;
// 1. 隐藏所有更低 Layer 的窗口(只控制 GameObject.active)
for (int layer = 0; layer < (int)newWindowLayer; layer++)
{
var windowList = layerManager.GetWindowsInLayer((UICanvasLayer)layer);
foreach (var window in windowList)
{
if (window != null && window.gameObject.activeSelf)
{
window.gameObject.SetActive(false);
}
}
}
// 2. 隐藏同层但 SiblingIndex 更小(在下面)的窗口
var sameLayerWindows = layerManager.GetWindowsInLayer(newWindowLayer);
foreach (var window in sameLayerWindows)
{
if (window != null && window != newWindow && window.gameObject.activeSelf)
{
var windowSiblingIndex = window.transform.GetSiblingIndex();
if (windowSiblingIndex < newWindowSiblingIndex)
{
window.gameObject.SetActive(false);
}
}
}
}
}
///
/// 处理窗口关闭时的渲染恢复
/// HideBelow 策略:恢复被遮挡窗口的渲染,直到遇到另一个 HideBelow 窗口
///
/// 性能优化:只控制 GameObject.SetActive(true),不影响窗口逻辑状态
///
/// 正在关闭的窗口
/// 可见性策略
public void HandleVisibilityOnClose(UIWindow closingWindow, UIVisibilityStrategy visibilityStrategy)
{
if (closingWindow == null || visibilityStrategy == UIVisibilityStrategy.None)
return;
// HideBelow 策略:基于渲染层级显示
if (visibilityStrategy == UIVisibilityStrategy.HideBelow)
{
var closingWindowLayer = closingWindow.uiWindowLayer;
var closingWindowSiblingIndex = closingWindow.transform.GetSiblingIndex();
var layerManager = UICanvasLayerManager.Instance;
// 从当前层开始,向下遍历所有层级,直到遇到 HideBelow 窗口
bool foundBlocker = false;
for (int layer = (int)closingWindowLayer; layer >= 0; layer--)
{
var windowList = layerManager.GetWindowsInLayer((UICanvasLayer)layer);
// 清空并复用列表,避免 GC
_tempWindowList.Clear();
// 收集需要检查的窗口
foreach (var window in windowList)
{
if (window != null && window != closingWindow)
{
// 同层:只检查在关闭窗口下面的窗口
if (layer == (int)closingWindowLayer)
{
var windowSiblingIndex = window.transform.GetSiblingIndex();
if (windowSiblingIndex < closingWindowSiblingIndex)
{
_tempWindowList.Add(window);
}
}
// 其他层:检查所有窗口
else
{
_tempWindowList.Add(window);
}
}
}
// 按 SiblingIndex 倒序排序(从上往下检查)
_tempWindowList.Sort((a, b) => b.transform.GetSiblingIndex().CompareTo(a.transform.GetSiblingIndex()));
// 遍历并处理窗口
foreach (var window in _tempWindowList)
{
var config = UIManager.Instance.GetWindowInfo(window.WindowName);
if (config.VisibilityStrategy == UIVisibilityStrategy.HideBelow)
{
// 遇到另一个 HideBelow 窗口,停止
foundBlocker = true;
break;
}
else
{
// 显示 None 策略的窗口(只控制 GameObject.active)
// ✅ 只恢复被遮挡的窗口,不恢复预加载的窗口
if (!window.gameObject.activeSelf && window.IsShow())
{
window.gameObject.SetActive(true);
}
}
}
if (foundBlocker)
{
break;
}
}
}
}
///
/// 获取栈顶的 HideBelow 策略窗口
///
public UIWindow GetTopHideBelowWindow()
{
var navigationStack = UINavigationManager.Instance.NavigationStack;
for (int i = navigationStack.Count - 1; i >= 0; i--)
{
var window = navigationStack[i];
var config = UIManager.Instance.GetWindowInfo(window.WindowName);
if (config.VisibilityStrategy == UIVisibilityStrategy.HideBelow)
{
return window;
}
}
return null;
}
#endregion
}