NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Framework/UI/UIWindow.Lifecycle.cs

603 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using Cysharp.Threading.Tasks;
using DG.Tweening;
using Framework;
using PhxhSDK;
using UnityEngine.UI;
public abstract partial class UIWindow
{
/*
╔═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ UIWindow 生命周期 (Lifecycle) ║
╠═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ ║
║ 【生命周期流程】 ║
║ 1. Load → UIManager.CreateWindow() 加载预制体 ║
║ 2. Init → Init() 初始化UI组件 ║
║ 3. Show → ShowWindow(data) 显示窗口 ║
║ 4. Update → Update(dt) 显示的界面每帧更新 ║
║ 5. Refresh → Refresh(data) 刷新数据 ║
║ 6. Close → CloseWindow(false) 关闭窗口(保留内存) ║
║ 7. Destroy → CloseWindow(true) 销毁窗口(释放资源) ║
║ ║
║ 【核心虚方法】(子类可重写) ║
║ • OnInit() - 初始化UI组件绑定事件 ║
║ • OnBeforeShow() - 显示前验证返回false取消显示 ║
║ • OnAfterShow() - 显示后处理,播放动画 ║
║ • OnUpdate(dt) - 显示的界面每帧更新(避免重度计算) ║
║ • OnRefresh() - 刷新显示内容 ║
║ • OnHide() - 隐藏时清理,暂停逻辑 ║
║ • OnRelease() - 销毁时释放资源(重要:防止内存泄漏) ║
║ ║
║ 【注意事项】 ║
║ ⚠️ 不要重写公共接口方法Init, ShowWindow, Update等
║ ⚠️ 不要直接调用OnXxx虚方法必须通过公共接口 ║
║ ⚠️ CloseWindow(true)销毁窗口CloseWindow(false)仅隐藏 ║
║ ║
╚═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
*/
#region Lifecycle Management
/// <summary>
/// 初始化窗口
/// </summary>
public void Init()
{
try
{
if (isInited)
{
DebugUtil.LogWarning("window : {0} already inited", WindowName);
}
else
{
DoInit();
OnInit();
isInited = true;
}
}
catch (System.Exception e)
{
DebugUtil.LogError("window : {0} init window error: {1}\n===Stack===\n{2}\n======", WindowName, e.Message, e.StackTrace);
}
}
/// <summary>
/// 显示窗口
/// </summary>
public void ShowWindow(object data = null)
{
try
{
if (_isShow)
{
DebugUtil.LogWarning("window : {0} is show, can't show again", WindowName);
}
if (!OnBeforeShow(data))
{
DebugUtil.LogError("window : {0} on before show return false", WindowName);
return;
}
DoShow();
OnShow(data);
if (gameObject is null)
{
DebugUtil.Log("UI已被销毁:{0}", WindowName);
}
else
{
gameObject.SetActive(true);
}
_isShow = true;
_isPaused = false; // 新窗口默认是活跃状态(未暂停)
OnAfterShow(data);
bool isShowOpenAnim = IsShowOpenAnim;
if (isShowOpenAnim)
{
AnimShow(() =>
{
//TODO
EventManager.Instance.Send(EventManager.EventName.UIOpen, WindowName);
}).Forget();
}
else
{
//OnAfterShow(data);
EventManager.Instance.Send(EventManager.EventName.UIOpen, WindowName);
}
}
catch (System.Exception e)
{
DebugUtil.LogError("window : {0} show window error: {1}\n===Stack===\n{2}\n======", WindowName, e.Message, e.StackTrace);
}
}
/// <summary>
/// 在显示前截图(如果需要)
/// </summary>
private void CaptureScreenshotIfNeeded()
{
var config = UIManager.Instance.GetWindowInfo(WindowName);
if (config.NeedScreenshot)
{
var oldActive = gameObject.activeSelf;
gameObject.SetActive(false);
ScreenshotManager.Instance.CaptureScreenshot();
gameObject.SetActive(oldActive);
}
}
/// <summary>
/// 自动设置模糊背景(如果需要)
/// </summary>
private void SetupBlurBackgroundIfNeeded()
{
var config = UIManager.Instance.GetWindowInfo(WindowName);
if (!config.NeedScreenshot) return;
// 查找约定的 GaussianBlurMask 节点
var blurTransform = transform.Find("GaussianBlurMask");
if (blurTransform != null)
{
var blurMask = blurTransform.GetComponent<UnityEngine.UI.RawImage>();
if (blurMask != null)
{
blurMask.texture = ScreenshotManager.Instance.GetScreenTexture2D();
blurMask.gameObject.SetActive(true);
}
}
}
/// <summary>
/// 更新入口,界面打开的时候更新
/// </summary>
public void Update(float dt)
{
try
{
_notchScreenAdapter?.UpdateCheck();
OnUpdate(dt);
}
catch (System.Exception e)
{
DebugUtil.LogError("window : {0} update window error: {1}\n===Stack===\n{2}\n======", WindowName, e.Message, e.StackTrace);
}
}
/// <summary>
/// 刷新窗口数据
/// </summary>
public void Refresh(object data)
{
try
{
if (isInited)
{
OnRefresh(data);
}
else
{
DebugUtil.LogError("window : {0} not inited", WindowName);
}
}
catch (System.Exception e)
{
DebugUtil.LogError("window : {0} refresh with data error: {1}\n===Stack===\n{2}\n======", WindowName, e.Message, e.StackTrace);
}
}
/// <summary>
/// 关闭窗口
/// destroy = false: 关闭但保留内存(快速重开)
/// destroy = true: 销毁并释放资源
/// </summary>
public async void CloseWindow(bool destroy)
{
try
{
// 如果窗口已关闭
if (!_isShow)
{
// 如果不需要销毁,直接返回
if (!destroy)
{
DebugUtil.LogWarning("window : {0} is already closed", WindowName);
return;
}
// 如果需要销毁,跳过关闭逻辑,直接执行销毁
DebugUtil.LogG($"窗口已关闭直接销毁UI:{WindowName}");
IsClosing = true;
UIManager.Instance.RemoveWindow(mWindowName);
DoDestroy();
IsClosing = false;
return;
}
IsClosing = true;
// 1. 获取窗口配置
var config = UIManager.Instance.GetWindowInfo(WindowName);
// 2. 可见性管理(由 UIWindowVisibilityManager 根据配置决定是否处理)
UIWindowVisibilityManager.Instance.HandleVisibilityOnClose(this, config.VisibilityStrategy);
// 3. 相机管理(在可见性管理之后处理)
UIWindowCameraManager.Instance.HandleCameraOnClose(this);
// 4. 隐藏逻辑
OnHide();
gameObject?.SetActive(false);
_isShow = false;
_isPaused = false; // 重置暂停状态
EventManager.Instance.Send(EventManager.EventName.UIHide, WindowName);
// 5. 播放关闭动画
bool isShowCloseAnim = IsShowOpenAnim;
if (isShowCloseAnim)
{
UIManager.Instance.BlockUIForCloseOperation();
await AnimHide(() =>
{
// 动画播放完毕后的回调
if (destroy)
{
DoDestroy();
UIManager.Instance.RemoveWindow(mWindowName);
}
IsClosing = false;
});
}
else
{
// 7. 如果需要销毁(无动画情况)
if (destroy)
{
DoDestroy();
UIManager.Instance.RemoveWindow(mWindowName);
}
IsClosing = false;
}
}
catch (System.Exception e)
{
DebugUtil.LogError("window : {0} close window error: {1}\n===Stack===\n{2}\n======", WindowName, e.Message, e.StackTrace);
}
}
/// <summary>
/// 暂停窗口
///
/// 功能:
/// - 标记窗口为暂停状态
/// - 调用 OnPause 回调
/// - 可选:隐藏渲染(通过 hideRendering 参数控制)
///
/// 调用场景:
/// - 自动调用:窗口被遮挡时(由 UIWindowVisibilityManager 触发)
/// - 手动调用:游戏暂停、广告弹出等特殊场景
///
/// 示例:
/// battleUI.PauseWindow(hideRendering: false); // 暂停逻辑但保持显示
/// </summary>
/// <param name="hideRendering">是否同时隐藏渲染SetActive(false)</param>
public void PauseWindow(bool hideRendering = true)
{
// 状态检查
if (!_isShow)
{
DebugUtil.LogWarning($"[UIWindow] PauseWindow failed: window is not showing ({WindowName})");
return;
}
if (_isPaused)
{
DebugUtil.LogWarning($"[UIWindow] PauseWindow ignored: already paused ({WindowName})");
return;
}
_isPaused = true;
// 调用子类的暂停逻辑
OnPause();
// 可选:隐藏渲染
if (hideRendering && gameObject != null && gameObject.activeSelf)
{
gameObject.SetActive(false);
DebugUtil.Log($"[UIWindow] Paused (hidden): {WindowName}");
}
else
{
DebugUtil.Log($"[UIWindow] Paused (logic only): {WindowName}");
}
}
/// <summary>
/// 恢复窗口
///
/// 功能:
/// - 标记窗口为活跃状态
/// - 调用 OnResume 回调
/// - 可选:显示渲染(通过 showRendering 参数控制)
///
/// 调用场景:
/// - 自动调用:窗口重新显示时(由 UIWindowVisibilityManager 触发)
/// - 手动调用:游戏恢复、广告关闭等特殊场景
///
/// 示例:
/// battleUI.ResumeWindow(showRendering: true); // 恢复逻辑并显示
/// </summary>
/// <param name="showRendering">是否同时显示渲染SetActive(true)</param>
public void ResumeWindow(bool showRendering = true)
{
// 状态检查
if (!_isShow)
{
DebugUtil.LogWarning($"[UIWindow] ResumeWindow failed: window is not showing ({WindowName})");
return;
}
if (!_isPaused)
{
DebugUtil.LogWarning($"[UIWindow] ResumeWindow ignored: not paused ({WindowName})");
return;
}
_isPaused = false;
// 可选:显示渲染
if (showRendering && gameObject != null && !gameObject.activeSelf)
{
gameObject.SetActive(true);
DebugUtil.Log($"[UIWindow] Resumed (shown): {WindowName}");
}
else
{
DebugUtil.Log($"[UIWindow] Resumed (logic only): {WindowName}");
}
// 调用子类的恢复逻辑
OnResume();
}
#endregion
#region User Interaction
#endregion
#region Internal Implementation
/// <summary>
/// 恢复主相机状态(已废弃,由 UIWindowCameraManager 统一管理)
/// </summary>
[System.Obsolete("RestoreMainCamera已废弃相机状态由 UIWindowCameraManager 统一管理")]
private void RestoreMainCamera()
{
// 相机管理逻辑已移至 UIWindowCameraManager.HandleCameraOnClose
// 保留此方法以防止破坏性更改
}
/// <summary>
/// 执行基础初始化
/// </summary>
private void DoInit()
{
_isShow = false;
var metaInfo = UIManager.Instance.GetWindowInfo(WindowName);
IsShowOpenAnim = metaInfo.IsAutoAnim;
BackStrategy = UIBackStrategyFactory.CreateStrategy(metaInfo.BackStrategyType);
AutoBindField();
InitScreenAdaption();
BindDefaultCloseButtons();
BindDefaultHomeButtons();
BindDefaultBackButtons();
}
/// <summary>
/// 执行显示逻辑
/// </summary>
private void DoShow()
{
// 🎯 在显示前截图(如果需要)
CaptureScreenshotIfNeeded();
// 🎯 自动设置模糊背景(如果需要)
SetupBlurBackgroundIfNeeded();
// 🎯 相机管理(在窗口显示后处理)
var config = UIManager.Instance.GetWindowInfo(WindowName);
UIWindowVisibilityManager.Instance.HandleVisibilityOnOpen(this, config.VisibilityStrategy);
UIWindowCameraManager.Instance.HandleCameraOnOpen(this);
}
/// <summary>
/// 执行销毁逻辑
/// </summary>
private void DoDestroy()
{
DebugUtil.LogG($"DoDestroy:{WindowName}");
// 如果窗口还在显示状态,先调用 OnHide
if (_isShow)
{
OnHide();
_isShow = false;
}
// 确保 GameObject 隐藏
if (gameObject != null)
{
gameObject.SetActive(false);
}
// 发送隐藏事件
EventManager.Instance.Send(EventManager.EventName.UIHide, WindowName);
// 资源释放
if (gameObject)
{
if (isInited)
{
OnRelease();
DoRelease();
isInited = false;
}
if (UIManager.Instance.UIWindowObjDic.ContainsKey(gameObject))
{
UIManager.Instance.UIWindowObjDic.Remove(gameObject);
}
UnityEngine.Object.Destroy(gameObject);
}
// 卸载资源
AssetManager.Instance.Unload(string.Format(UIManager.UI_PREFAB_PATH, mWindowName), true);
gameObject = null;
}
/// <summary>
/// 基础资源释放
/// </summary>
private void DoRelease()
{
EventManager.Instance.Send(EventManager.EventName.UIDestroy, WindowName);
UnLoad();
_openAnimSeq?.Kill();
_assetReferenceManager?.Dispose();
_assetReferenceManager = null;
_notchScreenAdapter = null;
_graphicRaycasterManager = null;
}
#endregion
#region Virtual Methods
// Init Phase
/// <summary>
/// UI初始化
/// </summary>
protected virtual void OnInit(){}
/// <summary>
/// 自动绑定字段
/// </summary>
protected virtual void AutoBindField(){}
// Show Phase
/// <summary>
/// 显示前检查返回false可取消显示
/// </summary>
protected virtual bool OnBeforeShow(object data)
{
return true;
}
/// <summary>
/// 显示时处理已废弃建议使用OnAfterShow
/// </summary>
[System.Obsolete("OnShow已废弃请使用OnAfterShow方法替代。OnAfterShow在窗口完全显示后调用提供更稳定的显示时机。")]
protected virtual void OnShow(object data){}
/// <summary>
/// 显示后处理
/// </summary>
protected virtual void OnAfterShow(object data){}
/// <summary>
/// IsShow的界面每帧更新
/// </summary>
protected virtual void OnUpdate(float dt){}
// Refresh Phase
/// <summary>
/// 数据刷新
/// </summary>
protected virtual void OnRefresh(object data) {}
// Hide Phase
/// <summary>
/// 隐藏时处理
/// </summary>
protected virtual void OnHide(){}
// Pause/Resume Phase
/// <summary>
/// 窗口暂停回调(子类重写)
///
/// 调用时机:
/// - 自动调用窗口被遮挡时UIWindowVisibilityManager 触发)
/// - 手动调用:调用 PauseWindow() 时
///
/// 建议处理:
/// - 停止播放背景音乐/音效
/// - 暂停动画/粒子特效
/// - 暂停定时器/倒计时
/// - 暂停网络轮询(可选)
///
/// 示例:
/// <code>
/// protected override void OnPause()
/// {
/// if (_bgmHandle != -1)
/// AudioManager.Instance.PauseBGM(_bgmHandle);
/// }
/// </code>
/// </summary>
protected virtual void OnPause() { }
/// <summary>
/// 窗口恢复回调(子类重写)
///
/// 调用时机:
/// - 自动调用窗口重新显示时UIWindowVisibilityManager 触发)
/// - 手动调用:调用 ResumeWindow() 时
///
/// 建议处理:
/// - 恢复播放背景音乐/音效
/// - 恢复动画/粒子特效
/// - 恢复定时器/倒计时
/// - 刷新数据(可选)
///
/// 示例:
/// <code>
/// protected override void OnResume()
/// {
/// if (_bgmHandle != -1)
/// AudioManager.Instance.ResumeBGM(_bgmHandle);
/// }
/// </code>
/// </summary>
protected virtual void OnResume() { }
// Release Phase
/// <summary>
/// 资源释放
/// </summary>
protected virtual void OnRelease(){}
#endregion
}