using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using DG.Tweening;
///
/// UI管理器 - 负责管理所有UI界面的显示、隐藏和层级
///
public class UIManager : ManagerBase
{
#region 常量和枚举
// 基础排序顺序
private const int BASE_SORTING_ORDER = 10;
#endregion
#region 字段和属性
// UI根节点
private Transform uiRoot;
// 各层级的Transform
private Transform staticTrans;
private Transform bottomTrans;
private Transform midTrans;
private Transform systemTrans;
private Transform loadingTrans;
// UI字典,存储所有已加载的UI,键为UI名称,值为UI实例
private Dictionary> uiDict = new Dictionary>();
// 存储关闭但未销毁的UI,键为UI名称,值为UI实例
private Dictionary> closedUIDict = new Dictionary>();
// UI栈,用于管理UI的显示顺序
private Stack uiStack = new Stack();
// 当前最高的排序顺序
private int currentHighestSortingOrder = 0;
// 当前回合数,用于记录UI的使用情况
private int currentRound = 0;
// 缓存层级对应的排序顺序
private Dictionary layerSortingOrderCache = new Dictionary();
// 缓存层级对应的Transform
private Dictionary layerTransformCache = new Dictionary();
// UI实例计数器,用于为同类型UI生成唯一标识
private Dictionary uiInstanceCounter = new Dictionary();
// 上次检查时间
private float lastCheckTime = 0f;
// 检查间隔(秒)
private const float CHECK_INTERVAL = 5f;
public Action OnOpenWindow;
public Action OnCloseWindow;
#endregion
#region 初始化
///
/// 初始化UI管理器
///
public override void Init()
{
base.Init();
// 初始化UI根节点
InitUIRoot();
// 初始化缓存
layerSortingOrderCache.Clear();
layerTransformCache.Clear();
uiInstanceCounter.Clear();
DOTween.SetTweensCapacity(500, 50);
Debug.Log("UI管理器初始化完成");
}
///
/// 初始化UI根节点
///
private void InitUIRoot()
{
// 查找UI根节点
GameObject root = GameObject.Find("UIRoot");
// 如果场景中没有UI根节点,则创建一个
if (root == null)
{
root = GameObject.Instantiate(BuiltInLoader.LoadPrefab("UIRoot"));
root.name = "UIRoot";
if (root == null)
{
Debug.LogError("无法找到UI根节点");
return;
}
// 添加DontDestroyOnLoad组件,确保UI根节点在场景切换时不被销毁
GameObject.DontDestroyOnLoad(root);
}
uiRoot = root.transform;
uiRoot.position = Vector3.zero;
staticTrans = uiRoot.Find("Static");
bottomTrans = uiRoot.Find("Bottom");
midTrans = uiRoot.Find("Middle");
loadingTrans = uiRoot.Find("Loading");
systemTrans = uiRoot.Find("System");
layerTransformCache.Clear();
layerTransformCache.Add(UILayer.Static, staticTrans);
layerTransformCache.Add(UILayer.Bottom, bottomTrans);
layerTransformCache.Add(UILayer.Mid, midTrans);
layerTransformCache.Add(UILayer.System, systemTrans);
layerTransformCache.Add(UILayer.Loading, loadingTrans);
}
#endregion
#region 辅助方法
// 获取UI层级对应的基础排序顺序
private int GetBaseSortingOrderForLayer(UILayer layer)
{
// 尝试从缓存中获取排序顺序
if (layerSortingOrderCache.TryGetValue(layer, out int order))
return order;
// 如果缓存中没有,使用原来的方法计算并缓存结果
int result;
switch (layer)
{
case UILayer.Static:
result = BASE_SORTING_ORDER;
break;
case UILayer.Bottom:
result = BASE_SORTING_ORDER * 100;
break;
case UILayer.Mid:
result = BASE_SORTING_ORDER * 1000;
break;
case UILayer.System:
result = BASE_SORTING_ORDER * 10000;
break;
case UILayer.Loading:
result = BASE_SORTING_ORDER * 100000;
break;
default:
result = BASE_SORTING_ORDER * 10;
break;
}
// 将结果存入缓存
layerSortingOrderCache[layer] = result;
return result;
}
// 获取层级对应的Transform
private Transform GetTransForLayer(UILayer layer)
{
// 尝试从缓存中获取Transform
if (layerTransformCache.TryGetValue(layer, out Transform trans))
return trans;
// 如果缓存中没有,使用原来的方法获取并缓存结果
Transform result;
switch (layer)
{
case UILayer.Static:
result = staticTrans;
break;
case UILayer.Bottom:
result = bottomTrans;
break;
case UILayer.Mid:
result = midTrans;
break;
case UILayer.System:
result = systemTrans;
break;
case UILayer.Loading:
result = loadingTrans;
break;
default:
result = bottomTrans;
break;
}
// 将结果存入缓存
layerTransformCache[layer] = result;
return result;
}
// 获取UI实例,如果不存在则返回null
public T GetUI() where T : UIBase
{
// 获取UI类型名称
string uiName = typeof(T).Name;
if (string.IsNullOrEmpty(uiName))
{
// 记录错误日志
Debug.LogError("UI名称为空");
return null;
}
// 尝试从字典中获取UI实例列表
if (uiDict.TryGetValue(uiName, out List uiList) && uiList.Count > 0)
{
// 返回第一个实例
return uiList[0] as T;
}
// 如果不存在,返回null
return null;
}
public List GetUIList() where T : UIBase
{
List uiList = new List();
// 获取UI类型名称
string uiName = typeof(T).Name;
if (string.IsNullOrEmpty(uiName))
{
// 记录错误日志
Debug.LogError("UI名称为空");
return uiList;
}
// 尝试从字典中获取UI实例列表
List tempList = null;
uiDict.TryGetValue(uiName, out tempList);
if (tempList != null)
{
for (int i = 0; i < tempList.Count; i++)
{
UIBase ui = tempList[i];
if (null != ui)
{
uiList.Add(ui as T);
}
}
}
return uiList;
}
public bool IsOpenedInList() where T : UIBase
{
List uiList = GetUIList();
foreach (T ui in uiList)
{
if (ui.IsActive())
{
return true;
}
}
return false;
}
public bool IsOpened() where T : UIBase
{
T ui = GetUI();
if (null != ui)
{
return ui.IsActive();
}
return false;
}
// 获取指定类型的所有UI实例
public List GetAllUI() where T : UIBase
{
// 获取UI类型名称
string uiName = typeof(T).Name;
if (string.IsNullOrEmpty(uiName))
{
// 记录错误日志
Debug.LogError("UI名称为空");
return new List();
}
// 尝试从字典中获取UI实例列表
if (uiDict.TryGetValue(uiName, out List uiList) && uiList.Count > 0)
{
// 将列表中的所有实例转换为指定类型并返回
return uiList.Cast().ToList();
}
// 如果不存在,返回空列表
return new List();
}
// 更新父级UI的回合数
private void UpdateParentUIRounds(UIBase ui)
{
// 如果UI为空或不支持父子关系,直接返回
if (ui == null || !ui.supportParentChildRelation)
return;
// 获取父级UI
UIBase parentUI = ui.parentUI;
// 遍历所有父级UI,更新回合数
while (parentUI != null)
{
// 更新父级UI的最后使用回合数
parentUI.lastUsedRound = currentRound;
// 继续向上查找父级UI
parentUI = parentUI.parentUI;
}
}
// 递归收集所有子UI
private void CollectChildrenUI(UIBase ui, List result)
{
// 如果UI为空或没有子UI或不支持父子关系,直接返回
if (ui == null || !ui.supportParentChildRelation || ui.childrenUI == null || ui.childrenUI.Count == 0)
return;
// 遍历所有子UI
foreach (var childUI in ui.childrenUI)
{
// 如果结果列表中不包含当前子UI,则添加
if (!result.Contains(childUI))
{
// 添加到结果列表
result.Add(childUI);
// 递归收集子UI的子UI
CollectChildrenUI(childUI, result);
}
}
}
///
/// 检查并关闭长时间未使用的UI
///
public void CheckAndCloseIdleUI()
{
// 如果没有UI,直接返回
if (uiDict.Count == 0 && closedUIDict.Count == 0)
return;
// 创建需要关闭的UI列表
List uiToClose = new List();
// 遍历所有活跃UI
foreach (var uiList in uiDict.Values)
{
foreach (var ui in uiList)
{
// 如果UI是持久化的,跳过
if (ui.isPersistent)
continue;
if (ui.IsActive())
continue;
// 计算UI空闲的回合数
int idleRounds = currentRound - ui.lastUsedRound;
// 如果空闲回合数超过最大空闲回合数,添加到关闭列表
if (idleRounds > ui.maxIdleRounds)
{
uiToClose.Add(ui);
}
}
}
// 遍历所有关闭的UI
List emptyKeys = new List();
foreach (var kvp in closedUIDict)
{
string uiName = kvp.Key;
List uiList = kvp.Value;
List uiToRemove = new List();
foreach (var ui in uiList)
{
// 计算UI空闲的回合数
int idleRounds = currentRound - ui.lastUsedRound;
// 如果空闲回合数超过最大空闲回合数,添加到关闭列表
if (idleRounds > ui.maxIdleRounds)
{
uiToClose.Add(ui);
uiToRemove.Add(ui);
}
}
// 从关闭列表中移除需要销毁的UI
foreach (var ui in uiToRemove)
{
uiList.Remove(ui);
}
// 如果列表为空,记录需要从字典中移除的键
if (uiList.Count == 0)
{
emptyKeys.Add(uiName);
}
}
// 从字典中移除空列表
foreach (var key in emptyKeys)
{
closedUIDict.Remove(key);
}
// 销毁所有需要关闭的UI
foreach (var ui in uiToClose)
{
// 记录日志
Debug.Log($"销毁长时间未使用的UI: {ui.uiName}, 空闲回合数: {currentRound - ui.lastUsedRound}");
// 销毁UI对象
GameObject.Destroy(ui.gameObject);
}
}
// // 生成UI实例的唯一标识
// private string GenerateUIInstanceID(string uiName)
// {
// // 如果计数器中不存在该UI类型,初始化为0
// if (!uiInstanceCounter.ContainsKey(uiName))
// {
// uiInstanceCounter[uiName] = 0;
// }
// // 递增计数器
// uiInstanceCounter[uiName]++;
// // 返回带有计数的唯一标识
// return $"{uiName}_{uiInstanceCounter[uiName]}";
// }
#endregion
#region UI资源管理
private UIBase LoadUIResource(string uiName)
{
// 从资源管理器加载UI预制体
GameObject prefab = ResManager.Instance.LoadAsset("UI", uiName);
// 检查预制体是否加载成功
if (prefab == null)
{
// 记录错误日志
Debug.LogError($"加载UI预制体失败: {uiName}");
return null;
}
// 实例化UI对象
GameObject uiObject = GameObject.Instantiate(prefab);
// 设置对象名称
uiObject.name = uiName;
// 通过uiName映射Type
Type uiType = Type.GetType(uiName);
if (uiType == null)
{
Debug.LogError($"找不到UI类型: {uiName}");
return null;
}
// 获取UI基类组件
UIBase uiBase = uiObject.GetComponent(uiType) as UIBase;
// 检查UI基类组件是否存在
if (uiBase == null)
{
// 记录错误日志
Debug.LogError($"UI预制体 {uiName} 没有 UIBase 组件或类型不匹配");
return null;
}
// 设置UI名称
uiBase.uiName = uiName;
// 设置父节点为UI根节点
Transform parentTrans = GetTransForLayer(uiBase.uiLayer);
uiObject.transform.SetParent(parentTrans, false);
// 设置排序顺序
int baseSortingOrder = GetBaseSortingOrderForLayer(uiBase.uiLayer);
uiBase.SetSortingOrder(baseSortingOrder);
return uiBase;
}
// 加载UI预制体
private T LoadUIResource(string uiName) where T : UIBase
{
return LoadUIResource(uiName) as T;
}
#endregion
#region UI排序管理
// 更新UI排序顺序
private void UpdateUISortingOrder()
{
// 重置当前最高排序顺序
currentHighestSortingOrder = 0;
// 遍历UI栈,设置排序顺序
UIBase[] uiArray = new UIBase[uiStack.Count];
uiStack.CopyTo(uiArray, 0);
// 先按照UILayer进行排序,然后再按照栈顺序排序
Array.Sort(uiArray, (a, b) => {
// 比较UI层级
int layerCompare = a.uiLayer.CompareTo(b.uiLayer);
if (layerCompare != 0)
return layerCompare;
// 同层级内,按照栈中的顺序排序
return Array.IndexOf(uiArray, b).CompareTo(Array.IndexOf(uiArray, a));
});
// 遍历排序后的UI数组,设置排序顺序
foreach (var ui in uiArray)
{
// 获取基础排序顺序
int baseSortingOrder = GetBaseSortingOrderForLayer(ui.uiLayer);
// 计算当前UI的排序顺序
int sortingOrder = baseSortingOrder + currentHighestSortingOrder;
// 设置UI的排序顺序
ui.SetSortingOrder(sortingOrder);
// 更新当前最高排序顺序
currentHighestSortingOrder += 10;
// Debug.Log(ui.uiName + " order is " + sortingOrder + " " + currentHighestSortingOrder);
}
}
#endregion
#region UI操作
private UIBase GetLastSupportParentChildRelationUI()
{
List tempList = new List();
UIBase target = null;
while (target == null && uiStack.Count > 0)
{
UIBase uiBase = uiStack.Pop();
if (uiBase != null && uiBase.supportParentChildRelation && !uiBase.isMainUI)
{
target = uiBase;
}
tempList.Add(uiBase);
}
for (int i = tempList.Count - 1; i >= 0; i--)
{
uiStack.Push(tempList[i]);
}
return target;
}
public UIBase OpenWindow(string uiName)
{
// 优先从closedUIDict中获取
UIBase parentUI = null;
UIBase returnValue = null;
Debug.Log("OpenWindow " + uiName);
List closedUIList = new List();
if (closedUIDict.TryGetValue(uiName, out closedUIList) && closedUIList.Count > 0)
{
Debug.Log("OpenWindow getFromClosedDict " + uiName);
returnValue = closedUIList[0] as UIBase;
closedUIList.RemoveAt(0);
if (closedUIList.Count == 0)
{
closedUIDict.Remove(uiName);
}
}
else
{
Debug.Log("OpenWindow getNewLoad " + uiName);
returnValue = LoadUIResource(uiName);
if (returnValue == null)
{
// 记录错误日志
Debug.LogError($"打开UI失败: {uiName}");
return null;
}
}
returnValue.gameObject.SetActive(true);
// 自动设置父级UI(如果未指定且支持父子关系)
if (returnValue.supportParentChildRelation && uiStack.Count > 0)
{
// 获取栈顶UI
parentUI = GetLastSupportParentChildRelationUI();
}
// 设置父级UI
if (parentUI != null)
{
// 设置父子关系
returnValue.parentUI = parentUI;
if (parentUI.childrenUI == null)
{
// 初始化父级UI的子UI列表
parentUI.childrenUI = new List();
}
// 添加到父级UI的子UI列表
parentUI.childrenUI.Add(returnValue);
}
// 更新回合数
currentRound++;
// 设置UI的最后使用回合数
returnValue.lastUsedRound = currentRound;
// 更新父级UI的回合数
UpdateParentUIRounds(returnValue);
// 将UI添加到字典中
if (!uiDict.ContainsKey(uiName))
{
// 如果字典中不存在该类型的UI,创建新列表
uiDict[uiName] = new List();
}
// 添加到UI列表
uiDict[uiName].Add(returnValue);
// 将UI添加到栈中
uiStack.Push(returnValue);
// 更新UI排序顺序
UpdateUISortingOrder();
// 打开UI
returnValue.HandleOpen();
OnOpenWindow?.Invoke(returnValue);
// 检查并关闭长时间未使用的UI
CheckAndCloseIdleUI();
return returnValue;
}
///
/// 打开UI
///
public T OpenWindow() where T : UIBase
{
// 获取UI类型名称
string uiName = typeof(T).Name;
return OpenWindow(uiName) as T;
}
///
/// 关闭UI
///
public void CloseWindow(bool destroy = false) where T : UIBase
{
// 获取UI类型名称
string uiName = typeof(T).Name;
CloseWindow(uiName, destroy);
}
public void CloseWindow(string uiName, bool destroy = false)
{
// 检查UI是否存在
if (!uiDict.ContainsKey(uiName) || uiDict[uiName].Count == 0)
{
// 记录警告日志
Debug.LogWarning($"尝试关闭不存在的UI: {uiName}");
return;
}
// 获取第一个UI实例
UIBase ui = uiDict[uiName][0];
// 关闭UI
CloseWindow(ui, destroy);
}
///
/// 关闭指定的UI实例
///
public void CloseWindow(UIBase ui, bool destroy = false)
{
// 检查UI是否为空
if (ui == null)
{
// 记录警告日志
Debug.LogWarning("尝试关闭空UI");
return;
}
if (!ui.IsActive())
{
return;
}
// 获取UI类型名称
string uiName = ui.uiName;
Debug.Log("CloseWindow " + uiName + " destroy : " + destroy.ToString());
// 收集所有子UI
List childrenUI = new List();
if (ui.supportParentChildRelation)
{
CollectChildrenUI(ui, childrenUI);
}
// 先关闭所有子UI
foreach (var childUI in childrenUI)
{
// 关闭子UI
CloseWindow(childUI, destroy);
}
// 从栈中移除UI
Stack tempStack = new Stack();
while (uiStack.Count > 0)
{
// 弹出栈顶UI
UIBase tempUI = uiStack.Pop();
// 如果不是要关闭的UI,则保存到临时栈中
if (tempUI != ui)
{
tempStack.Push(tempUI);
}
}
// 将临时栈中的UI重新压入栈中
while (tempStack.Count > 0)
{
uiStack.Push(tempStack.Pop());
}
// 从字典中移除UI
if (uiDict.ContainsKey(uiName))
{
// 从UI列表中移除
uiDict[uiName].Remove(ui);
// 如果列表为空,从字典中移除该类型
if (uiDict[uiName].Count == 0)
{
uiDict.Remove(uiName);
}
}
// 从父级UI的子UI列表中移除
if (ui.supportParentChildRelation && ui.parentUI != null && ui.parentUI.childrenUI != null)
{
// 从父级UI的子UI列表中移除
ui.parentUI.childrenUI.Remove(ui);
}
// 关闭UI
ui.HandleClose();
OnCloseWindow?.Invoke(ui);
if (destroy)
{
// 销毁UI对象
GameObject.Destroy(ui.gameObject);
}
else
{
// 添加到closedUIDict
if (!closedUIDict.ContainsKey(uiName))
{
closedUIDict[uiName] = new List();
}
closedUIDict[uiName].Add(ui);
Debug.Log("CloseWindow " + uiName + " destroy : " + destroy.ToString() + " push to closedUIDict");
// 隐藏UI (交给handle close内部自己去做)
// ui.gameObject.SetActive(false);
}
// 更新UI排序顺序
UpdateUISortingOrder();
}
///
/// 关闭指定类型的所有UI实例
///
public void CloseAllWindows() where T : UIBase
{
// 获取UI类型名称
string uiName = typeof(T).Name;
// 检查UI是否存在
if (!uiDict.ContainsKey(uiName) || uiDict[uiName].Count == 0)
{
// 记录警告日志
Debug.LogWarning($"尝试关闭不存在的UI: {uiName}");
return;
}
// 创建UI实例列表的副本,因为在关闭过程中会修改原列表
List uiListCopy = new List(uiDict[uiName]);
// 关闭所有UI实例
foreach (var ui in uiListCopy)
{
// 关闭UI实例
CloseWindow(ui, false);
}
}
public void ReturnMainUI()
{
List allUI = new List(uiStack);
for (int i = 0; i < allUI.Count; i++)
{
UIBase uiBase = allUI[i];
if (!uiBase.isMainUI && uiBase.uiLayer < UILayer.System)
{
CloseWindow(uiBase);
}
}
}
///
/// 关闭所有UI
///
public void DestroyAllUI()
{
// 创建UI栈的副本,因为在关闭过程中会修改原栈
UIBase[] uiArray = new UIBase[uiStack.Count];
uiStack.CopyTo(uiArray, 0);
// 关闭所有UI
foreach (var ui in uiArray)
{
// 关闭UI
CloseWindow(ui, true);
}
foreach (var uiList in closedUIDict.Values)
{
foreach (var ui in uiList)
{
GameObject.Destroy(ui.gameObject);
}
}
// 清空UI字典和栈
uiDict.Clear();
uiStack.Clear();
closedUIDict.Clear();
}
///
/// 刷新UI
///
public void RefreshUI() where T : UIBase
{
// 获取UI类型名称
string uiName = typeof(T).Name;
// 检查UI是否存在
if (!uiDict.ContainsKey(uiName) || uiDict[uiName].Count == 0)
{
// 记录警告日志
Debug.LogWarning($"尝试刷新不存在的UI: {uiName}");
return;
}
// 刷新所有该类型的UI实例
foreach (var ui in uiDict[uiName])
{
// 刷新UI
ui.Refresh();
}
}
///
/// 刷新所有UI
///
public void RefreshAllUI()
{
// 遍历所有UI类型
foreach (var uiList in uiDict.Values)
{
// 遍历该类型的所有UI实例
foreach (var ui in uiList)
{
// 刷新UI
ui.Refresh();
}
}
}
private void Update()
{
// 如果距离上次检查的时间超过了检查间隔
if (Time.time - lastCheckTime > CHECK_INTERVAL)
{
// 更新上次检查时间
lastCheckTime = Time.time;
// 检查并关闭长时间未使用的UI
CheckAndCloseIdleUI();
}
}
#endregion
#region 释放资源
///
/// 释放资源
///
public override void Release()
{
// 关闭所有UI
DestroyAllUI();
// 清空缓存
layerSortingOrderCache.Clear();
layerTransformCache.Clear();
uiInstanceCounter.Clear();
Debug.Log("UI管理器资源释放完成");
}
#endregion
}