using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Cysharp.Threading.Tasks; using Framework; using Obfuz; using PhxhSDK; using PhxhSDK.AOT.Aspect; using UnityEngine; [ObfuzIgnore] public partial class UIManager : Singlenton, IInitable, IUpdatable { public const string UI_PREFAB_PATH = "Assets/Art_Out/UI/Prefab/{0}.prefab"; public Dictionary mMemoryWindows = new Dictionary(); // ✅ 使用独立的层级管理器 private UICanvasLayerManager LayerManager => UICanvasLayerManager.Instance; private ScreenOrientation _currentOrientation; private Dictionary _openingWindows = new Dictionary(); private List _hideCache = new List(); private UIWindow _curFullScreen; private Dictionary _uiWindowObjDic = new Dictionary(); public Dictionary UIWindowObjDic => _uiWindowObjDic; public Vector2 Size; /// /// UI和主相机屏幕缩放 /// public Vector2 UIAndMainScreenScale; /// /// UI和UI屏幕缩放 /// public Vector2 UIAndUIScreenScale; /// /// 禁用返回键计数 /// private int disableBackCount; /// /// 是否响应返回键 /// public bool IsEnableBack { get { return disableBackCount <= 0; } set { if (value) { if (disableBackCount > 0) --disableBackCount; } else ++disableBackCount; } } public void Init() { IsEnableBack = true; _AllWindowMeta(); // 初始化层级管理器 LayerManager.Initialize(UIRoot.Instance.GetAllLayerRoots()); if (DeviceHelper.IsFullScreenIOS()) { SetFullScreenIOS(); } EventManager.Instance.Register(EventManager.EventName.Android_Click_Back, _OnAndroidClickBack); OnScreenSizeChange(); AspectManager.Instance.ScreenSizeChange += OnScreenSizeChange; } public void Release() { LayerManager.Clear(); AspectManager.Instance.ScreenSizeChange -= OnScreenSizeChange; EventManager.Instance.Unregister(EventManager.EventName.Android_Click_Back, _OnAndroidClickBack); } public bool disableUILogic = false; public void Update(float dt) { if (disableUILogic) return; foreach (var winKv in mMemoryWindows) { var window = winKv.Value; if (window != null && window.gameObject && window.IsCreated() && window.IsActive() // 改为 IsActive():只更新活跃窗口(显示且未暂停) && !window.IsClosing) { window.Update(dt); } } _UpdateInputBlockTimer(dt); } #region UIWindow Manager public static void BindWindowObj(UIWindow window, GameObject uiObj) { if (window == null || uiObj == null) { if (window == null) DebugUtil.LogError("window is null"); if (uiObj == null) DebugUtil.LogError("uiObj is null"); return; } window.gameObject = uiObj.gameObject; window.transform = uiObj.transform; window.name = uiObj.name; } #endregion #region OpenWindowAPI /// /// 打开一个UI /// UIManager.Instance.OpenWindow("Common/GameUI") /// /// /// 创建并打开窗口 /// /// 窗口路径 /// 传递的数据 public async UniTask CreateAndOpenWindow(string path, object data = null) { DebugUtil.LogP($"打开窗口:{path}"); if (mMemoryWindows.ContainsKey(path)) { DebugUtil.LogG($"窗口已打开!!!{path}"); //return null; } var window = await _LoadWindow(path, false); if (window != null) { window.ShowWindow(data); } return window; } public bool Back() { return false; } public bool RemoveWindow(string wName) { string windowName = wName; bool result = false; UIWindow window; if (mMemoryWindows.TryGetValue(windowName, out window)) { LayerManager.RemoveWindow(window); mMemoryWindows.Remove(windowName); result = true; } return result; } /// /// 关闭窗口 /// /// 窗口名称 /// 是否销毁窗口 public void CloseWindow(string wName, bool destroy) { var window = GetLoadedWindowByPath(wName); if (window != null) { window.CloseWindow(destroy); } } /// /// 打开已加载的窗口(通过路径) /// public void OpenLoadedWindow(string path, object data = null) { var window = GetLoadedWindowByPath(path); if (window != null) { window.ShowWindow(data); } else { DebugUtil.LogError($"OpenLoadedWindow: window not found: {path}"); } } public T GetLoadedWindowByPath(string uipath) where T : UIWindow { var window = GetLoadedWindowByPath(uipath) as T; //return window.gameObject.activeSelf ? window : null; return window; } public UIWindow GetLoadedWindowByPath(string uipath) { if (string.IsNullOrEmpty(uipath)) { DebugUtil.LogError("UIManager.GetOpenedWindowByPath, uipath is null or empty"); return null; } if (mMemoryWindows.ContainsKey(uipath)) { //return window.gameObject.activeSelf ? window : null; return mMemoryWindows[uipath]; } return null; } public bool GetTopWindow(out UIWindow window, out UICanvasLayer layer, int banLayerMask) { return LayerManager.GetTopWindow(out window, out layer, banLayerMask); } public int LayerMask(UICanvasLayer layer) { return LayerManager.GetLayerMask(layer); } async void SetFullScreenIOS() { await UniTask.Delay(670); // while (true) // { // var screenOrientation = DragonNativeBridge.GetScreenOrientation(); // if (screenOrientation != _currentOrientation) // { // _currentOrientation = screenOrientation; // EventDispatcher.Instance.DispatchEvent(new ScreenOrientationChangedEvent(_currentOrientation)); // } // yield return wait; // } } /// /// 加载窗口(不显示) /// 用于预加载场景 /// public async UniTask LoadWindow(string path) { return await _LoadWindow(path, true); } private async UniTask _LoadWindow(string path, bool forceHide) { string uipath = path; UIWindow window = null; WindowInfo wInfo; if (!_windowsInfo.ContainsKey(uipath)) { //if (_windowsInfo.TryGetValue(uipath, out wInfo)) { DebugUtil.LogError("Create ui fail: {0}, no windows info inited", uipath); return window; } } wInfo = _windowsInfo[uipath]; var uiWindowLayer = wInfo.windowLayer; if (!mMemoryWindows.TryGetValue(uipath, out window)) { if (_openingWindows.ContainsKey(uipath)) { return window; } _openingWindows.Add(uipath, uipath); window = await UIRoot.Instance.CreateWindow(uipath, uiWindowLayer); _openingWindows.Remove(uipath); if (window != null) { mMemoryWindows[uipath] = window; window.WindowName = uipath; window.uiWindowLayer = uiWindowLayer; window.Init(); } else { DebugUtil.LogError("Create ui fail: {0}", uipath); return window; } } LayerManager.AddWindow(window); if (window == null) { DebugUtil.LogError("Create ui fail: {0}", uipath); return null; } if (forceHide) { window.gameObject.SetActive(false); } _uiWindowObjDic.TryAdd(window.gameObject, window); return window; } public void ForeachUI(Action action) { var windowList = mMemoryWindows.ToList(); foreach (var kv in windowList) { var window = kv.Value; if (window != null) { action?.Invoke(kv.Value); } } } public void CloseAllUIExcept(string path) { var windowList = mMemoryWindows.ToList(); foreach (var kv in windowList) { var window = kv.Value; if(window.WindowName != path) { window.CloseWindow(destroy: true); mMemoryWindows.Remove(window.WindowName); LayerManager.RemoveWindow(window); } } } #endregion #region ShowAndHide /// /// 隐藏指定层级及以下的所有UI /// 和 ShowAllUI 成对调用 /// /// 最大层级(包含该层级及以下的所有UI都会被隐藏) /// 不隐藏的UI窗口名称列表 public void HideAllUI(UICanvasLayer maxLayer, params string[] exceptions) { var hiddenWindows = LayerManager.HideWindowsUpToLayer(maxLayer, exceptions); _hideCache.AddRange(hiddenWindows); } /// /// 和HideAllUI成对调用 /// public void ShowAllUI() { foreach (var v in _hideCache) { var window = v; if (window != null) window.ShowWindow(); } _hideCache.Clear(); } #endregion //根据路径判断UI界面是否打开 public bool IsWindowOpening(string uiPath) { string windowName; if (_openingWindows.TryGetValue(uiPath, out windowName)) { if (windowName != null) { return true; } } return false; } /// /// 关闭指定层级的所有窗口(支持单个或多个层级) /// /// 要关闭的层级列表 public void CloseAllInLayer(params UICanvasLayer[] layers) { CloseAllInLayer(layers, null); } /// /// 关闭指定层级的所有窗口,可排除指定窗口(支持单个或多个层级) /// /// 要关闭的层级列表 /// 不关闭的窗口名称列表 public void CloseAllInLayer(UICanvasLayer[] layers, params string[] exceptions) { if (layers == null || layers.Length == 0) { DebugUtil.LogWarning("[CloseAllInLayer] No layers specified"); return; } int totalClosed = 0; foreach (var layer in layers) { var windowsInLayer = LayerManager.GetWindowsInLayer(layer); if (windowsInLayer.Count == 0) { DebugUtil.Log($"[CloseAllInLayer] No windows in layer: {layer}"); continue; } List toBeRemoved = new List(); var node = windowsInLayer.First; while (node != null) { var window = node.Value; bool shouldClose = true; // 检查是否在例外列表中 if (exceptions != null && exceptions.Length > 0) { foreach (var exception in exceptions) { if (window.WindowName == exception) { shouldClose = false; DebugUtil.Log($"[CloseAllInLayer] Skipping window: {window.WindowName} (in exception list)"); break; } } } if (shouldClose) { toBeRemoved.Add(window); } if (node == windowsInLayer.Last) { break; } node = node.Next; } // 关闭窗口 foreach (var window in toBeRemoved) { DebugUtil.Log($"[CloseAllInLayer] Closing window: {window.WindowName} in layer {layer}"); window.CloseWindow(destroy: true); } totalClosed += toBeRemoved.Count; } if (layers.Length == 1) DebugUtil.Log($"[CloseAllInLayer] Closed {totalClosed} windows in layer {layers[0]}"); else DebugUtil.Log($"[CloseAllInLayer] Closed {totalClosed} windows in {layers.Length} layers"); } private async void _OnAndroidClickBack() { if (!IsEnableBack) return; // 获取所有可见窗口(从上到下) var visibleWindows = LayerManager.GetAllVisibleWindows(); DebugUtil.Log($"[AndroidBack] Found {visibleWindows.Count} visible windows"); // 从上到下遍历,找到第一个处理返回键的窗口 foreach (var window in visibleWindows) { DebugUtil.Log($"[AndroidBack] Trying window: {window.WindowName} (Layer: {window.uiWindowLayer})"); if (await window.Back()) { // 窗口处理了返回键,结束遍历 DebugUtil.Log($"[AndroidBack] Handled by: {window.WindowName}"); return; } } // 没有窗口处理返回键,显示退出确认 DebugUtil.Log("[AndroidBack] No window handled, showing exit confirmation"); if (!CheckWindowCreated(UINameConst.UIBackReturn)) { await CreateAndOpenWindow(UINameConst.UIBackReturn); } } public bool CheckWindowCreated(string uiPath) { if (mMemoryWindows.TryGetValue(uiPath, out var window)) { return window.IsCreated(); } return false; } private async void OnScreenSizeChange() { await UniTask.NextFrame(); // 必须等待一帧,等RectTransform大小刷新后获取 RectTransform rectTrans = UIRoot.Instance.Root.GetComponent(); Size = new(rectTrans.sizeDelta.x, rectTrans.sizeDelta.y); UIAndMainScreenScale = new Vector3(Size.x / Screen.width, Size.y / Screen.height); UIAndUIScreenScale = new Vector3(Size.x / AspectManager.Instance.ScreenSize.x, Size.y / AspectManager.Instance.ScreenSize.y); } }