486 lines
13 KiB
C#
486 lines
13 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
/// <summary>
|
||
/// UI面板类型,用于确定面板的层级和行为
|
||
/// </summary>
|
||
public enum UIPanelType
|
||
{
|
||
Base, // 基础面板(主界面等)
|
||
Normal, // 普通面板(功能面板)
|
||
Popup, // 弹出面板(用户管理等)
|
||
Dialog, // 对话框(确认弹窗等)
|
||
System // 系统面板(警报覆盖等),最高优先级
|
||
}
|
||
|
||
/// <summary>
|
||
/// UI面板信息
|
||
/// </summary>
|
||
public class UIPanelInfo
|
||
{
|
||
public BasePanel Panel { get; set; }
|
||
public UIPanelType Type { get; set; }
|
||
public int Layer { get; set; }
|
||
public bool IsModal { get; set; } // 是否为模态面板
|
||
public string PanelName { get; set; }
|
||
|
||
public UIPanelInfo(BasePanel panel, UIPanelType type, bool isModal = false)
|
||
{
|
||
Panel = panel;
|
||
Type = type;
|
||
IsModal = isModal;
|
||
PanelName = panel.GetType().Name;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 增强版UI管理器
|
||
/// 使用栈管理面板导航,支持层级管理和优雅的返回逻辑
|
||
/// </summary>
|
||
public class EnhancedUIManager : MonoBehaviour
|
||
{
|
||
#region 单例
|
||
private static EnhancedUIManager _instance;
|
||
public static EnhancedUIManager Instance
|
||
{
|
||
get
|
||
{
|
||
if (_instance == null)
|
||
{
|
||
var go = new GameObject("EnhancedUIManager");
|
||
_instance = go.AddComponent<EnhancedUIManager>();
|
||
DontDestroyOnLoad(go);
|
||
}
|
||
return _instance;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 字段和属性
|
||
[Header("UI容器")]
|
||
public Transform CanvasRoot;
|
||
public Transform BaseLayer; // 基础层(主界面)
|
||
public Transform NormalLayer; // 普通层(功能面板)
|
||
public Transform PopupLayer; // 弹窗层(用户管理)
|
||
public Transform DialogLayer; // 对话框层(确认弹窗)
|
||
public Transform SystemLayer; // 系统层(警报覆盖)
|
||
|
||
// 面板管理
|
||
private Dictionary<string, UIPanelInfo> _allPanels = new Dictionary<string, UIPanelInfo>();
|
||
private Stack<UIPanelInfo> _navigationStack = new Stack<UIPanelInfo>();
|
||
private List<UIPanelInfo> _activeDialogs = new List<UIPanelInfo>();
|
||
private UIPanelInfo _currentBasePanel;
|
||
|
||
// 层级管理
|
||
private readonly Dictionary<UIPanelType, int> _baseLayerValues = new Dictionary<UIPanelType, int>
|
||
{
|
||
{ UIPanelType.Base, 0 },
|
||
{ UIPanelType.Normal, 100 },
|
||
{ UIPanelType.Popup, 200 },
|
||
{ UIPanelType.Dialog, 300 },
|
||
{ UIPanelType.System, 400 }
|
||
};
|
||
|
||
private int _nextLayerIncrement = 0;
|
||
#endregion
|
||
|
||
#region 初始化
|
||
void Awake()
|
||
{
|
||
if (_instance == null)
|
||
{
|
||
_instance = this;
|
||
DontDestroyOnLoad(gameObject);
|
||
InitializeLayers();
|
||
}
|
||
else if (_instance != this)
|
||
{
|
||
Destroy(gameObject);
|
||
}
|
||
}
|
||
|
||
private void InitializeLayers()
|
||
{
|
||
// 查找或创建Canvas
|
||
if (CanvasRoot == null)
|
||
{
|
||
var canvas = FindObjectOfType<Canvas>();
|
||
if (canvas == null)
|
||
{
|
||
var canvasGO = new GameObject("Canvas");
|
||
canvas = canvasGO.AddComponent<Canvas>();
|
||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||
canvasGO.AddComponent<UnityEngine.UI.CanvasScaler>();
|
||
canvasGO.AddComponent<UnityEngine.UI.GraphicRaycaster>();
|
||
}
|
||
CanvasRoot = canvas.transform;
|
||
}
|
||
|
||
// 创建层级
|
||
BaseLayer = CreateLayer("BaseLayer", 0);
|
||
NormalLayer = CreateLayer("NormalLayer", 100);
|
||
PopupLayer = CreateLayer("PopupLayer", 200);
|
||
DialogLayer = CreateLayer("DialogLayer", 300);
|
||
SystemLayer = CreateLayer("SystemLayer", 400);
|
||
}
|
||
|
||
private Transform CreateLayer(string layerName, int sortingOrder)
|
||
{
|
||
var layerGO = new GameObject(layerName);
|
||
layerGO.transform.SetParent(CanvasRoot, false);
|
||
|
||
var canvas = layerGO.AddComponent<Canvas>();
|
||
canvas.overrideSorting = true;
|
||
canvas.sortingOrder = sortingOrder;
|
||
|
||
layerGO.AddComponent<UnityEngine.UI.GraphicRaycaster>();
|
||
|
||
// 设置为全屏
|
||
var rectTransform = layerGO.GetComponent<RectTransform>();
|
||
rectTransform.anchorMin = Vector2.zero;
|
||
rectTransform.anchorMax = Vector2.one;
|
||
rectTransform.sizeDelta = Vector2.zero;
|
||
rectTransform.anchoredPosition = Vector2.zero;
|
||
|
||
return layerGO.transform;
|
||
}
|
||
#endregion
|
||
|
||
#region 面板显示
|
||
/// <summary>
|
||
/// 显示面板
|
||
/// </summary>
|
||
public T ShowPanel<T>(UIPanelType panelType = UIPanelType.Normal, bool isModal = false, bool isFade = true) where T : BasePanel
|
||
{
|
||
string panelName = typeof(T).Name;
|
||
|
||
// 检查是否已存在
|
||
if (_allPanels.ContainsKey(panelName))
|
||
{
|
||
BringPanelToFront(_allPanels[panelName]);
|
||
return _allPanels[panelName].Panel as T;
|
||
}
|
||
|
||
// 创建新面板
|
||
var panelGO = Instantiate(Resources.Load<GameObject>("UI/" + panelName));
|
||
if (panelGO == null)
|
||
{
|
||
Debug.LogError($"Failed to load UI prefab: {panelName}");
|
||
return null;
|
||
}
|
||
|
||
var panel = panelGO.GetComponent<T>();
|
||
if (panel == null)
|
||
{
|
||
Debug.LogError($"Panel {panelName} does not have component {typeof(T)}");
|
||
Destroy(panelGO);
|
||
return null;
|
||
}
|
||
|
||
// 设置层级
|
||
Transform targetLayer = GetTargetLayer(panelType);
|
||
panelGO.transform.SetParent(targetLayer, false);
|
||
|
||
// 创建面板信息
|
||
var panelInfo = new UIPanelInfo(panel, panelType, isModal);
|
||
panelInfo.Layer = _baseLayerValues[panelType] + _nextLayerIncrement++;
|
||
|
||
// 设置Canvas层级
|
||
SetPanelLayer(panelGO, panelInfo.Layer);
|
||
|
||
// 注册面板
|
||
_allPanels[panelName] = panelInfo;
|
||
|
||
// 处理导航逻辑
|
||
HandlePanelNavigation(panelInfo);
|
||
|
||
// 显示面板
|
||
panel.ShowMe(isFade);
|
||
|
||
// Debug.Log($"Showed panel {panelName} at layer {panelInfo.Layer}");
|
||
return panel;
|
||
}
|
||
|
||
private Transform GetTargetLayer(UIPanelType panelType)
|
||
{
|
||
return panelType switch
|
||
{
|
||
UIPanelType.Base => BaseLayer,
|
||
UIPanelType.Normal => NormalLayer,
|
||
UIPanelType.Popup => PopupLayer,
|
||
UIPanelType.Dialog => DialogLayer,
|
||
UIPanelType.System => SystemLayer,
|
||
_ => NormalLayer
|
||
};
|
||
}
|
||
|
||
private void SetPanelLayer(GameObject panelGO, int layer)
|
||
{
|
||
var canvas = panelGO.GetComponent<Canvas>();
|
||
if (canvas == null)
|
||
{
|
||
canvas = panelGO.AddComponent<Canvas>();
|
||
panelGO.AddComponent<UnityEngine.UI.GraphicRaycaster>();
|
||
}
|
||
canvas.overrideSorting = true;
|
||
canvas.sortingOrder = layer;
|
||
}
|
||
|
||
private void HandlePanelNavigation(UIPanelInfo panelInfo)
|
||
{
|
||
switch (panelInfo.Type)
|
||
{
|
||
case UIPanelType.Base:
|
||
// 基础面板,清空导航栈
|
||
ClearNavigationStack();
|
||
_currentBasePanel = panelInfo;
|
||
break;
|
||
|
||
case UIPanelType.Normal:
|
||
case UIPanelType.Popup:
|
||
// 普通面板,加入导航栈
|
||
_navigationStack.Push(panelInfo);
|
||
break;
|
||
|
||
case UIPanelType.Dialog:
|
||
// 对话框,加入对话框列表
|
||
_activeDialogs.Add(panelInfo);
|
||
break;
|
||
|
||
case UIPanelType.System:
|
||
// 系统面板,不影响导航
|
||
break;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 面板关闭
|
||
/// <summary>
|
||
/// 关闭面板
|
||
/// </summary>
|
||
public void HidePanel<T>(bool isFade = true) where T : BasePanel
|
||
{
|
||
string panelName = typeof(T).Name;
|
||
if (_allPanels.ContainsKey(panelName))
|
||
{
|
||
HidePanel(_allPanels[panelName], isFade);
|
||
}
|
||
}
|
||
|
||
private void HidePanel(UIPanelInfo panelInfo, bool isFade = true)
|
||
{
|
||
if (panelInfo?.Panel == null) return;
|
||
|
||
// 从导航管理中移除
|
||
RemoveFromNavigation(panelInfo);
|
||
|
||
// 隐藏面板
|
||
if (isFade)
|
||
{
|
||
panelInfo.Panel.HideMe(true, () => {
|
||
DestroyPanel(panelInfo);
|
||
});
|
||
}
|
||
else
|
||
{
|
||
DestroyPanel(panelInfo);
|
||
}
|
||
|
||
// Debug.Log($"Hid panel {panelInfo.PanelName}");
|
||
}
|
||
|
||
private void RemoveFromNavigation(UIPanelInfo panelInfo)
|
||
{
|
||
// 从对话框列表移除
|
||
_activeDialogs.Remove(panelInfo);
|
||
|
||
// 从导航栈移除
|
||
if (_navigationStack.Contains(panelInfo))
|
||
{
|
||
var tempStack = new Stack<UIPanelInfo>();
|
||
while (_navigationStack.Count > 0)
|
||
{
|
||
var item = _navigationStack.Pop();
|
||
if (item != panelInfo)
|
||
{
|
||
tempStack.Push(item);
|
||
}
|
||
}
|
||
while (tempStack.Count > 0)
|
||
{
|
||
_navigationStack.Push(tempStack.Pop());
|
||
}
|
||
}
|
||
|
||
// 如果是基础面板
|
||
if (panelInfo == _currentBasePanel)
|
||
{
|
||
_currentBasePanel = null;
|
||
}
|
||
}
|
||
|
||
private void DestroyPanel(UIPanelInfo panelInfo)
|
||
{
|
||
if (panelInfo?.Panel != null)
|
||
{
|
||
Destroy(panelInfo.Panel.gameObject);
|
||
_allPanels.Remove(panelInfo.PanelName);
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 导航功能
|
||
/// <summary>
|
||
/// 返回到主界面
|
||
/// </summary>
|
||
public void ReturnToHome()
|
||
{
|
||
// 关闭所有非基础面板
|
||
var panelsToClose = new List<UIPanelInfo>();
|
||
|
||
foreach (var panel in _allPanels.Values)
|
||
{
|
||
if (panel.Type != UIPanelType.Base)
|
||
{
|
||
panelsToClose.Add(panel);
|
||
}
|
||
}
|
||
|
||
foreach (var panel in panelsToClose)
|
||
{
|
||
HidePanel(panel, false);
|
||
}
|
||
|
||
// 清空导航栈
|
||
ClearNavigationStack();
|
||
|
||
Debug.Log("Returned to home");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 返回上一个面板
|
||
/// </summary>
|
||
public bool GoBack()
|
||
{
|
||
// 优先关闭对话框
|
||
if (_activeDialogs.Count > 0)
|
||
{
|
||
var lastDialog = _activeDialogs[_activeDialogs.Count - 1];
|
||
HidePanel(lastDialog, true);
|
||
return true;
|
||
}
|
||
|
||
// 然后处理导航栈
|
||
if (_navigationStack.Count > 0)
|
||
{
|
||
var currentPanel = _navigationStack.Pop();
|
||
HidePanel(currentPanel, true);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭所有对话框
|
||
/// </summary>
|
||
public void CloseAllDialogs()
|
||
{
|
||
var dialogsToClose = new List<UIPanelInfo>(_activeDialogs);
|
||
foreach (var dialog in dialogsToClose)
|
||
{
|
||
HidePanel(dialog, false);
|
||
}
|
||
}
|
||
|
||
private void ClearNavigationStack()
|
||
{
|
||
_navigationStack.Clear();
|
||
}
|
||
|
||
private void BringPanelToFront(UIPanelInfo panelInfo)
|
||
{
|
||
if (panelInfo?.Panel == null) return;
|
||
|
||
panelInfo.Layer = _baseLayerValues[panelInfo.Type] + _nextLayerIncrement++;
|
||
SetPanelLayer(panelInfo.Panel.gameObject, panelInfo.Layer);
|
||
|
||
// 重新显示
|
||
if (!panelInfo.Panel.gameObject.activeInHierarchy)
|
||
{
|
||
panelInfo.Panel.ShowMe(true);
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 实用功能
|
||
/// <summary>
|
||
/// 获取面板
|
||
/// </summary>
|
||
public T GetPanel<T>() where T : BasePanel
|
||
{
|
||
string panelName = typeof(T).Name;
|
||
if (_allPanels.ContainsKey(panelName))
|
||
{
|
||
return _allPanels[panelName].Panel as T;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查面板是否显示
|
||
/// </summary>
|
||
public bool IsPanelShowing<T>() where T : BasePanel
|
||
{
|
||
return GetPanel<T>() != null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前活动面板数量
|
||
/// </summary>
|
||
public int GetActivePanelCount()
|
||
{
|
||
return _allPanels.Count;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取导航栈深度
|
||
/// </summary>
|
||
public int GetNavigationDepth()
|
||
{
|
||
return _navigationStack.Count;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 调试信息
|
||
/// </summary>
|
||
public void PrintDebugInfo()
|
||
{
|
||
Debug.Log($"=== UI Manager Debug Info ===");
|
||
Debug.Log($"Active Panels: {_allPanels.Count}");
|
||
Debug.Log($"Navigation Stack Depth: {_navigationStack.Count}");
|
||
Debug.Log($"Active Dialogs: {_activeDialogs.Count}");
|
||
|
||
foreach (var panel in _allPanels.Values)
|
||
{
|
||
Debug.Log($"Panel: {panel.PanelName}, Type: {panel.Type}, Layer: {panel.Layer}");
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region Unity事件
|
||
void Update()
|
||
{
|
||
// 处理返回键
|
||
if (Input.GetKeyDown(KeyCode.Escape))
|
||
{
|
||
if (!GoBack())
|
||
{
|
||
// 如果无法返回,可以考虑退出应用或显示退出确认
|
||
Debug.Log("Cannot go back further");
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
} |