using System.Collections.Generic; using UnityEngine; namespace MFrame { /// /// 面板创建委托,用于动态创建UI面板实例 /// /// 返回创建的面板实例 public delegate IPanelBase PanelCreate(PanelBaseData data, System.Action completeHandler); /// /// UI面板基类,提供基础UI功能和资源加载管理 /// 继承自UIBase,用于实现具体的UI面板 /// public class PanelBase : UIBase, IPanelBase where V : UI_PanelBase { /// /// UI层级索引,控制面板在UI层级中的显示顺序 /// protected UILayerIdx _layerIdx; /// /// 面板数据,用于存储和传递面板所需的数据 /// protected PanelBaseData _data; /// /// 面板视图组件,负责处理UI的具体显示和交互逻辑 /// protected V _view; /// /// 预制体资源路径 /// protected string _url; protected string _registerModule; // 全屏遮挡以组件形式配置:在面板预制体根节点挂 FullScreenCover 组件即生效(策划可自助添加/移除) /// /// 面板预制体根节点上的 FullScreenCover 组件(未挂则为 null,即非全屏面板) /// private FullScreenCover _fullScreenCover; /// /// 是否正被上层全屏面板隐藏 /// private bool _hiddenByCover; /// /// 被本面板(全屏遮挡)隐藏的下层面板列表 /// private readonly List _panelsHiddenByMe = new List(); /// /// 业务是否已关闭(防止被遮挡隐藏的已关闭面板被“复活”) /// private bool _closed; /// /// 构造函数,初始化面板基本属性 /// /// 是否在关闭时自动销毁 public PanelBase(bool autoDestroy) : base(null) { _autoDestroy = autoDestroy; } /// /// 在关闭界面的时候是否进行销毁 /// /// public bool IsAutoDestroy() { return _autoDestroy; } // —— 全屏遮挡:IPanelBase 接口实现 —— UILayerIdx IPanelBase.panelLayerIdx => _layerIdx; bool IPanelBase.isKeepShowWhenCovered => _fullScreenCover != null && _fullScreenCover.enabled && _fullScreenCover.isKeepShowWhenCovered; bool IPanelBase.isHiddenByCover => _hiddenByCover; bool IPanelBase.isClosed => _closed; /// /// 全屏遮挡优化:由上层全屏面板调用,隐藏本面板(省去被盖住的 DrawCall) /// public void SetCoverHidden() { _hiddenByCover = true; if (_view != null) { _view.gameObject.SetActive(false); } } /// /// 全屏遮挡优化:恢复本面板显示(幂等;面板视图已销毁时自动跳过) /// public void RestoreFromCover() { if (!_hiddenByCover) { return; } _hiddenByCover = false; if (_view != null) { _view.gameObject.SetActive(true); } } /// /// 尝试异步加载UI预制体 /// /// 预制体资源路径 /// 目标UI层级 /// 面板初始化数据 protected virtual void _TryInit(string registerModule, string url, UILayerIdx layerIdx, PanelBaseData data, System.Action completeHandler = null) { _registerModule = registerModule; _url = url; // 初始化并启用资源加载器 _layerIdx = layerIdx; ResLoaderMgr.Instance.TryLoadAsync(_registerModule, _url, (float progress) => { _OnProgress(progress); }, (Object prefab) => { if (prefab != null) { // 加载成功后实例化预制体并设置到指定层级 _TryInitGameObject(prefab as GameObject, layerIdx, data, completeHandler); } else { // 处理加载失败的情况 _OnLoadFail(); } }); } /// /// 初始化 UI预制体 /// /// 目标UI层级 /// 面板初始化数据 protected virtual void _TryInitGameObject(GameObject prefab, UILayerIdx layerIdx, PanelBaseData data, System.Action completeHandler = null) { LayerItem litem = GameGlobalData.GetLayerItem(_layerIdx); GameObject go = UnityUtils.InstantiatePrefab(prefab, litem.transform); transform = go.transform; // 获取视图组件并完成初始化 _view = transform.GetComponent(); // 缓存全屏遮挡组件(策划在预制体根节点上挂载,未挂则为 null) _fullScreenCover = transform.GetComponent(); _view.AddUpdateHandler(this.Update); _OnLoadComplete(); Show(data, completeHandler); } public V view { get { return _view; } } protected virtual void _OnProgress(float progress) { } /// /// 资源加载完成回调,子类可重写此方法实现自定义加载完成逻辑 /// protected virtual void _OnLoadComplete() { foreach (var btn in this._view.btnsClose) { if (btn != null) this.AddBtnOnClick(btn, this.Close); } } /// /// 资源加载失败回调,子类可重写此方法实现自定义失败处理逻辑 /// protected virtual void _OnLoadFail() { } /// /// 打开面板,可传入自定义数据 /// /// 面板初始化数据 public void Show(PanelBaseData data, System.Action completeHandler) { _data = data; _closed = false; if (this._view != null) { // 若此前被上层全屏面板隐藏,主动 Show 视为恢复显示 //(隐藏者列表中的遗留记录由 RestorePanelsHiddenByMe 的 isHiddenByCover 守卫失效,无需清理) if (_hiddenByCover) { RestoreFromCover(); } this._view.gameObject.SetActive(true); } _OnShow(); // 全屏遮挡面板(预制体根节点挂 FullScreenCover 组件且处于启用状态): // 打开时隐藏下层正在显示的面板,省去被盖住的 DrawCall if (_fullScreenCover != null && _fullScreenCover.enabled) { HidePanelsUnderThis(); } _view.Show(() => { completeHandler?.Invoke(this); }); } /// /// 判断面板是否正在显示 /// /// public bool isShowing() { if (_view == null) { return false; } // 被上层全屏面板隐藏时仍视为“打开中”,保持业务语义不变;已关闭的面板不算 return !_closed && (_view.gameObject.activeSelf || _hiddenByCover); } public bool isLoading() { return _view == null; } protected virtual void _OnShow() { } /// /// 关闭面板 /// public void Close() { PanelMgr.Instance.Close(_registerModule); } /// /// 关闭面板(内部使用) /// public void ___CloseForPanelMgr(System.Action complete) { // 标记业务已关闭,防止关闭过程中被遮挡机制“复活” _closed = true; // 若正被上层全屏面板隐藏(inactive 会导致关闭动画不更新而卡死),先恢复再播关闭动画 if (_hiddenByCover) { RestoreFromCover(); } _view.Close(() => { // 关闭完成后恢复被本面板隐藏的下层面板 RestorePanelsHiddenByMe(); _OnClose(); complete?.Invoke(); }); } protected virtual void _OnClose() { } /// /// 全屏遮挡优化:隐藏本面板下方所有层级中正在显示的面板, /// 被完全遮住的 UI 不再提交 DrawCall(关闭/销毁时由 RestorePanelsHiddenByMe 恢复)。 /// private void HidePanelsUnderThis() { Dictionary items = PanelMgr.Instance.items; if (items == null) { return; } foreach (KeyValuePair kvp in items) { PanelMsgItem item = kvp.Value; if (item.panel == null || ReferenceEquals(item.panel, this)) { continue; } IPanelBase panel = item.panel; if (panel.panelLayerIdx < _layerIdx && !panel.isKeepShowWhenCovered && !panel.isHiddenByCover && panel.isShowing()) { _panelsHiddenByMe.Add(panel); panel.SetCoverHidden(); } } } /// /// 恢复被本面板隐藏的下层面板(幂等,可重复调用) /// private void RestorePanelsHiddenByMe() { int len = _panelsHiddenByMe.Count; for (int i = 0; i < len; i++) { IPanelBase panel = _panelsHiddenByMe[i]; // 已关闭的面板不恢复,防止“复活”已关闭的界面 if (panel != null && panel.isHiddenByCover && !panel.isClosed) { panel.RestoreFromCover(); } } _panelsHiddenByMe.Clear(); } /// /// 销毁面板实例,清理资源防止内存泄漏 /// protected override void _Destroy() { // 兼容未走 Close 直接销毁的路径,确保下层面板一定被恢复 RestorePanelsHiddenByMe(); base._Destroy(); // 清理资源加载器 ResLoaderMgr.Instance.TryRemoveLoads(_registerModule); // 清除面板数据 _data = null; _fullScreenCover = null; if (_view != null) { _view.RemoveUpdateHandler(this.Update); GameObject.Destroy(_view.gameObject); _view = null; } } } }