NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Framework/UI/UIWindowVisibilityManager.cs

215 lines
8.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System.Collections.Generic;
/// <summary>
/// UI窗口可见性管理器纯性能优化模块
///
/// 核心理念:
/// - 只控制 GameObject.SetActive(),仅影响渲染层面
/// - 不触发 UIWindow 生命周期(不调用 CloseWindow/ShowWindow
/// - 不改变窗口逻辑状态_isShow 保持不变)
///
/// 优化目标:
/// - 当全屏窗口打开时,自动隐藏被遮挡的下层窗口,减少 Overdraw
/// - 当全屏窗口关闭时,自动恢复被遮挡的窗口渲染
/// - 节省 GPU 绘制开销和 CPU UI 遍历开销
/// </summary>
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
/// <summary>
/// 复用的窗口列表,避免 GC
/// </summary>
private readonly List<UIWindow> _tempWindowList = new List<UIWindow>(32);
#endregion
#region Visibility Operations
/// <summary>
/// 处理窗口打开时的渲染优化
/// HideBelow 策略:隐藏所有被遮挡的下层窗口
///
/// 性能优化:只控制 GameObject.SetActive(false),不影响窗口逻辑状态
/// </summary>
/// <param name="newWindow">新打开的窗口</param>
/// <param name="visibilityStrategy">可见性策略</param>
public void HandleVisibilityOnOpen(UIWindow newWindow, UIVisibilityStrategy visibilityStrategy)
{
if (newWindow == null || visibilityStrategy == UIVisibilityStrategy.None)
return;
// HideBelow 策略:基于渲染层级隐藏
if (visibilityStrategy == UIVisibilityStrategy.Occlusion)
{
var newWindowLayer = newWindow.uiWindowLayer;
var newWindowSiblingIndex = newWindow.transform.GetSiblingIndex();
var layerManager = UICanvasLayerManager.Instance;
// 从底层到当前层,隐藏所有应该被遮挡的窗口(只控制 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 != newWindow && window.IsShow() && window.gameObject.activeSelf)
{
bool shouldHide = false;
if (layer < (int)newWindowLayer)
{
// 更低层:全部隐藏
shouldHide = true;
}
else // layer == (int)newWindowLayer
{
// 同层:只隐藏 SiblingIndex 更小(在下面)的窗口
var windowSiblingIndex = window.transform.GetSiblingIndex();
shouldHide = windowSiblingIndex < newWindowSiblingIndex;
}
if (shouldHide)
{
window.gameObject.SetActive(false);
}
}
}
}
}
}
/// <summary>
/// 处理窗口关闭时的渲染恢复
/// HideBelow 策略:恢复被遮挡窗口的渲染,直到遇到另一个 HideBelow 窗口
///
/// 性能优化:只控制 GameObject.SetActive(true),不影响窗口逻辑状态
/// </summary>
/// <param name="closingWindow">正在关闭的窗口</param>
/// <param name="visibilityStrategy">可见性策略</param>
public void HandleVisibilityOnClose(UIWindow closingWindow, UIVisibilityStrategy visibilityStrategy)
{
if (closingWindow == null || visibilityStrategy == UIVisibilityStrategy.None)
return;
// HideBelow 策略:基于渲染层级显示
if (visibilityStrategy == UIVisibilityStrategy.Occlusion)
{
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)
{
// ✅ 预加载的窗口IsShow=false不参与遮挡管理
if (window != null && window != closingWindow && window.IsShow())
{
// 同层:只检查在关闭窗口下面的窗口
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);
// 显示被隐藏的窗口(只控制 GameObject.active
if (!window.gameObject.activeSelf)
{
// // 🎯 如果窗口需要截图,在恢复前重新截图
// if (config.NeedScreenshot)
// {
// ScreenshotManager.Instance.CaptureScreenshot();
//
// // 🎯 刷新模糊背景 texture
// RefreshBlurBackground(window);
// }
// else
// {
// ScreenshotManager.Instance.ResetScreenshot();
// }
window.gameObject.SetActive(true);
}
// 如果遇到另一个 HideBelow 窗口,停止继续向下恢复
// (因为这个窗口会负责隐藏它下面的窗口)
if (config.VisibilityStrategy == UIVisibilityStrategy.Occlusion)
{
foundBlocker = true;
break;
}
}
if (foundBlocker)
{
break;
}
}
}
}
#region Helper Methods
/// <summary>
/// 刷新窗口的模糊背景 texture
/// </summary>
private void RefreshBlurBackground(UIWindow window)
{
var blurTransform = window.transform.Find("GaussianBlurMask");
if (blurTransform != null)
{
var blurMask = blurTransform.GetComponent<UnityEngine.UI.RawImage>();
if (blurMask != null)
{
blurMask.texture = ScreenshotManager.Instance.GetScreenTexture2D();
}
}
}
#endregion
#endregion
}