UI系统组件
This commit is contained in:
91
Assets/_Game/Scripts/UI/Base/UIPanelBase.cs
Normal file
91
Assets/_Game/Scripts/UI/Base/UIPanelBase.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// UI 面板基类:统一面板生命周期、焦点恢复、进场淡入与事件订阅清理。
|
||||
///
|
||||
/// 消除各面板控制器(PauseMenu / Settings / InventoryHub 等)重复的样板:
|
||||
/// - OnEnable:取服务 / 订阅 / 设默认选中 / 播淡入
|
||||
/// - OnDisable:清理订阅(<see cref="_subs"/>)/ 停动画
|
||||
/// - <see cref="IFocusable.OnFocusRestored"/>:关闭子面板回到栈顶时聚焦首项
|
||||
///
|
||||
/// 与 <see cref="UIManager"/> 面板栈完全兼容:UIManager 仍以 SetActive 驱动开关,
|
||||
/// 本基类只在 OnEnable/OnDisable 加钩子,不改变栈语义。
|
||||
///
|
||||
/// 子类用法:重写 <see cref="OnPanelOpen"/>(订阅/刷新)与可选 <see cref="OnPanelClose"/>;
|
||||
/// 在 Inspector 指定 <see cref="_firstSelected"/>(手柄/键盘默认焦点);可选挂 CanvasGroup 做淡入。
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public abstract class UIPanelBase : MonoBehaviour, IFocusable
|
||||
{
|
||||
[Header("Panel Base")]
|
||||
[Tooltip("打开 / 焦点恢复时默认选中的控件(手柄/键盘导航起点)。可空。")]
|
||||
[SerializeField] protected Selectable _firstSelected;
|
||||
|
||||
[Tooltip("进场淡入用的 CanvasGroup(可空;为空则不淡入)。")]
|
||||
[SerializeField] protected CanvasGroup _canvasGroup;
|
||||
|
||||
[Tooltip("进场淡入时长(秒)。0 表示无淡入。")]
|
||||
[SerializeField] protected float _fadeInDuration = 0.15f;
|
||||
|
||||
[Tooltip("打开时是否自动把焦点设到首项。")]
|
||||
[SerializeField] protected bool _selectFirstOnEnable = true;
|
||||
|
||||
/// <summary>事件订阅容器,OnDisable 自动清理。子类订阅用 <c>channel.Subscribe(..).AddTo(_subs)</c>。</summary>
|
||||
protected readonly CompositeDisposable _subs = new();
|
||||
|
||||
private Coroutine _fadeRoutine;
|
||||
|
||||
// ── 生命周期 ──────────────────────────────────────────────────────────
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
OnPanelOpen();
|
||||
if (_canvasGroup != null && _fadeInDuration > 0f) PlayFadeIn();
|
||||
if (_selectFirstOnEnable) FocusFirst();
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
_subs.Clear();
|
||||
if (_fadeRoutine != null) { StopCoroutine(_fadeRoutine); _fadeRoutine = null; }
|
||||
OnPanelClose();
|
||||
}
|
||||
|
||||
/// <summary>面板打开时调用(OnEnable)。子类在此取服务、订阅事件、刷新内容。</summary>
|
||||
protected virtual void OnPanelOpen() { }
|
||||
|
||||
/// <summary>面板关闭时调用(OnDisable,_subs 已清理之后)。子类在此释放非 _subs 资源。</summary>
|
||||
protected virtual void OnPanelClose() { }
|
||||
|
||||
// ── 焦点 ──────────────────────────────────────────────────────────────
|
||||
/// <summary>关闭子面板、本面板恢复为栈顶时调用(<see cref="IUIManager.CloseTopPanel"/> 触发)。</summary>
|
||||
public virtual void OnFocusRestored() => FocusFirst();
|
||||
|
||||
/// <summary>将 EventSystem 焦点设到首项(优先 <see cref="_firstSelected"/>,否则 <see cref="ResolveFirstSelected"/>)。</summary>
|
||||
protected void FocusFirst()
|
||||
{
|
||||
var go = _firstSelected != null ? _firstSelected.gameObject : ResolveFirstSelected();
|
||||
if (go != null && EventSystem.current != null)
|
||||
EventSystem.current.SetSelectedGameObject(go);
|
||||
}
|
||||
|
||||
/// <summary>子类可重写:动态决定首个选中项(如列表第一行),当 <see cref="_firstSelected"/> 未指定时使用。</summary>
|
||||
protected virtual GameObject ResolveFirstSelected() => null;
|
||||
|
||||
// ── 工具 ──────────────────────────────────────────────────────────────
|
||||
/// <summary>从 ServiceLocator 取服务(未注册返回 null)。</summary>
|
||||
protected static T GetService<T>() where T : class => ServiceLocator.GetOrDefault<T>();
|
||||
|
||||
private void PlayFadeIn()
|
||||
{
|
||||
if (_fadeRoutine != null) StopCoroutine(_fadeRoutine);
|
||||
_canvasGroup.alpha = 0f;
|
||||
_fadeRoutine = StartCoroutine(UITween.FadeCanvasGroup(_canvasGroup, 1f, _fadeInDuration));
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/UI/Base/UIPanelBase.cs.meta
Normal file
11
Assets/_Game/Scripts/UI/Base/UIPanelBase.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0b3d5f5b7a324d47930ee0ccd5c6ae3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
11
Assets/_Game/Scripts/UI/Base/UISimplePanel.cs
Normal file
11
Assets/_Game/Scripts/UI/Base/UISimplePanel.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace BaseGames.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 通用面板:<see cref="UIPanelBase"/> 的最简具体实现,无额外逻辑。
|
||||
/// 用作脚手架生成的 themed 面板根(带 CanvasGroup 淡入 + 默认焦点 + 订阅清理),
|
||||
/// 适合不需要自定义控制器的简单弹窗 / 容器。需要业务逻辑时改挂自定义 <see cref="UIPanelBase"/> 子类。
|
||||
/// </summary>
|
||||
public sealed class UISimplePanel : UIPanelBase
|
||||
{
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/UI/Base/UISimplePanel.cs.meta
Normal file
11
Assets/_Game/Scripts/UI/Base/UISimplePanel.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6fa4cf81d2fd2e46b0d922f7b55097c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user