using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using Cysharp.Threading.Tasks; using Framework; using Obfuz; using PhxhSDK; using PhxhSDK.AOT.Aspect; using UnityEngine; using Object = UnityEngine.Object; [ObfuzIgnore] public partial class UIManagerBase: IInitable, IUpdatable { public const string UI_PREFAB_PATH = "Assets/Art_Out/UI/Prefab/{0}.prefab"; protected Dictionary mMemoryWindows = new Dictionary(); // ✅ 使用独立的层级管理器 protected UICanvasLayerManager LayerManager => UICanvasLayerManager.Instance; /// /// 窗口工厂(负责创建窗口控制器实例) /// protected IUIWindowFactory _windowFactory; protected ScreenOrientation _currentOrientation; protected Dictionary _openingWindows = new Dictionary(); protected UIWindow _curFullScreen; protected UIWindowGroup _tempHideGroup; protected Dictionary _uiWindowObjDic = new Dictionary(); public Dictionary UIWindowObjDic => _uiWindowObjDic; public Vector2 Size; /// /// UI和主相机屏幕缩放 /// public Vector2 UIAndMainScreenScale; /// /// UI和UI屏幕缩放 /// public Vector2 UIAndUIScreenScale; /// /// 禁用返回键计数 /// protected int disableBackCount; /// /// 是否响应返回键 /// public bool IsEnableBack { get { return disableBackCount <= 0; } set { if (value) { if (disableBackCount > 0) --disableBackCount; } else ++disableBackCount; } } public void Init() { // ✅ 设置默认工厂(项目特定实现) _windowFactory = new UIWindowReflectionFactory(); // ✅ 初始化截图管理器 ScreenshotManager.Instance.Initialize(new ScreenshotConfig { BlurMaterialPath = Framework.Constants.BLUR_MAT_PATH, EnableBlur = true, ResolutionScale = 0.5f }); // 初始化层级管理器(从当前transform收集层级根节点) LayerManager.InitializeFromTransform(UIRoot.Instance.transform); IsEnableBack = true; _AllWindowMeta(); if (DeviceHelper.IsFullScreenIOS()) { SetFullScreenIOS(); } EventManager.Instance.Register(EventManager.EventName.Android_Click_Back, _OnAndroidClickBack); OnScreenSizeChange(); AspectManager.Instance.ScreenSizeChange += OnScreenSizeChange; } /// /// 设置窗口工厂(可选,用于测试或替换工厂) /// public void SetWindowFactory(IUIWindowFactory factory) { _windowFactory = factory ?? throw new ArgumentNullException(nameof(factory)); } 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 /// /// 创建窗口实例(从UIRoot迁移过来,职责更清晰) /// /// 窗口名称(路径) /// 目标层级 /// 创建的UIWindow实例,失败返回null private async UniTask CreateWindow(string windowName, UICanvasLayer layer) { // 1. 加载预制体 GameObject uiPrefab = await LoadPrefab(windowName); if (uiPrefab is null) { DebugUtil.LogError("[UIManager] CreateWindow: cannot find window resource: {0}", windowName); return null; } // 2. 获取目标层级根节点 var layerRoot = LayerManager.GetLayerRoot(layer); if (layerRoot == null) { DebugUtil.LogError("[UIManager] CreateWindow: cannot find layer root for layer: {0}", layer); return null; } // 3. 实例化UI对象到目标层级 GameObject obj = Object.Instantiate(uiPrefab, layerRoot.transform, false); // 4. 使用工厂创建Controller实例 UIWindow window = _windowFactory.CreateWindow(windowName); if (window == null) { DebugUtil.LogError("[UIManager] CreateWindow: factory failed to create controller for: {0}", windowName); Object.Destroy(obj); // 创建失败,销毁GameObject return null; } // 5. 绑定GameObject和UIWindow window.gameObject = obj.gameObject; window.transform = obj.transform; window.name = obj.name; return window; } /// /// 加载UI预制体 /// private async UniTask LoadPrefab(string windowName) { var path = string.Format(UI_PREFAB_PATH, windowName); GameObject uiPrefab = await AssetManager.Instance.LoadAssetAsync(path); // 如果AssetBundle加载失败,尝试从Resources加载(兼容方案) if (uiPrefab is null) { uiPrefab = Resources.Load(Path.Combine("Loading", windowName)); } return uiPrefab; } #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.UnregisterWindow(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); } /// /// 加载窗口(不显示) /// 用于预加载场景 /// 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 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.RegisterWindow(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 IEnumerable GetAllWindows() { return mMemoryWindows.Values; } 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.UnregisterWindow(window); } } } #endregion #region Show And Hide /// /// 隐藏指定层级及以下的所有UI /// 使用 UIWindowGroup 实现 /// /// 使用场景: /// - 进入全屏场景(如战斗、过场动画)时隐藏UI /// - 显示模态对话框时隐藏背景UI /// /// 注意:必须与 ShowAllUI 成对调用以恢复UI /// /// 最大层级(包含该层级及以下的所有UI都会被隐藏) /// 不隐藏的UI窗口名称列表 public void HideAllUI(UICanvasLayer maxLayer, params string[] exceptions) { // 收集需要隐藏的窗口名称 var windowNames = new List(); for (int i = 0; i <= (int)maxLayer; i++) { var layer = (UICanvasLayer)i; var windowsInLayer = LayerManager.GetWindowsInLayer(layer); foreach (var window in windowsInLayer) { if (window != null && window.gameObject.activeInHierarchy && !IsException(window.WindowName, exceptions)) { windowNames.Add(window.WindowName); } } } // 创建窗口组并隐藏 if (_tempHideGroup == null) { _tempHideGroup = new UIWindowGroup(windowNames); } _tempHideGroup.Close(false); } /// /// 显示所有被 HideAllUI 隐藏的UI /// public void ShowAllUI() { if (_tempHideGroup != null) { _tempHideGroup.Show(); _tempHideGroup = null; } } /// /// 检查窗口是否在例外列表中 /// private bool IsException(string windowName, string[] exceptions) { if (exceptions == null || exceptions.Length == 0) return false; foreach (var exception in exceptions) { if (exception == windowName) return true; } return false; } #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.UINormalRoot.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); } }