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
|
|||
|
|
}
|