UI系统
This commit is contained in:
@@ -53,13 +53,9 @@ namespace BaseGames.UI
|
||||
[SerializeField] private VoidEventChannelSO _onSpellSelectOpen;
|
||||
[Tooltip("打开统一背包菜单(InventoryHub)。对应 EVT_InventoryOpen。")]
|
||||
[SerializeField] private VoidEventChannelSO _onInventoryOpen;
|
||||
[Tooltip("UI 取消操作(ESC / 手柄 B·Circle),全局关闭栈顶面板。对应 EVT_UICancelPressed。")]
|
||||
[SerializeField] private VoidEventChannelSO _onUICancelPressed;
|
||||
|
||||
// ── 面板栈结构 ────────────────────────────────────────────────────────
|
||||
private readonly Stack<GameObject> _panelStack = new();
|
||||
/// <summary>O(1) 成员判断,与 _panelStack 保持同步,替代 Stack.Contains O(n)。</summary>
|
||||
private readonly HashSet<GameObject> _openPanelSet = new();
|
||||
// ── 面板栈:委托给统一的 UINavigator(不再自管栈)─────────────────────
|
||||
private IUINavigator _navigator;
|
||||
private readonly Dictionary<PanelId, GameObject> _panelRegistry = new();
|
||||
private readonly CompositeDisposable _subs = new();
|
||||
|
||||
@@ -114,6 +110,7 @@ namespace BaseGames.UI
|
||||
private void OnEnable()
|
||||
{
|
||||
ServiceLocator.Register<IUIManager>(this);
|
||||
_navigator = ServiceLocator.GetOrDefault<IUINavigator>();
|
||||
_onGameStateChanged?.Subscribe(HandleGameStateChanged).AddTo(_subs);
|
||||
_onPauseRequested?.Subscribe(TogglePause).AddTo(_subs);
|
||||
_onFastTravelOpen?.Subscribe(OpenMap).AddTo(_subs);
|
||||
@@ -122,7 +119,7 @@ namespace BaseGames.UI
|
||||
_onCharmPanelOpen?.Subscribe(OpenCharmPanel).AddTo(_subs);
|
||||
_onSpellSelectOpen?.Subscribe(OpenSpellSelect).AddTo(_subs);
|
||||
_onInventoryOpen?.Subscribe(OpenInventory).AddTo(_subs);
|
||||
_onUICancelPressed?.Subscribe(HandleUICancelPressed).AddTo(_subs);
|
||||
// 取消键(ESC / 手柄 B)由 UINavigator 统一消费,UIManager 不再订阅。
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
@@ -239,46 +236,44 @@ namespace BaseGames.UI
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>打开指定 GameObject 面板并压栈;已在栈中则忽略(O(1) 判断)。</summary>
|
||||
/// <summary>打开指定 GameObject 面板:经统一 <see cref="IUINavigator"/> 压栈。</summary>
|
||||
public void OpenPanel(GameObject panel)
|
||||
{
|
||||
if (panel == null) return;
|
||||
if (!_openPanelSet.Add(panel)) return;
|
||||
if (_panelStack.Count > 0) _panelStack.Peek().SetActive(false);
|
||||
panel.SetActive(true);
|
||||
_panelStack.Push(panel);
|
||||
var uiPanel = EnsurePanel(panel);
|
||||
if (uiPanel != null) Navigator?.Push(uiPanel);
|
||||
}
|
||||
|
||||
/// <summary>关闭栈顶面板并恢复上一层(如有);上一层若实现 IFocusable 则自动恢复焦点。</summary>
|
||||
public void CloseTopPanel()
|
||||
/// <summary>关闭栈顶面板并恢复上一层(委托给导航器)。</summary>
|
||||
public void CloseTopPanel() => Navigator?.Pop();
|
||||
|
||||
/// <summary>
|
||||
/// 适配器:保证面板根挂有 <see cref="UIPanelBase"/>(导航器压栈对象)。
|
||||
/// 既有面板若未挂则运行时补 <see cref="UISimplePanel"/> + CanvasGroup,
|
||||
/// 使所有游戏内面板无需逐个改脚手架即可纳入统一导航栈。
|
||||
/// </summary>
|
||||
private static UIPanelBase EnsurePanel(GameObject go)
|
||||
{
|
||||
if (_panelStack.Count == 0) return;
|
||||
var top = _panelStack.Pop();
|
||||
_openPanelSet.Remove(top);
|
||||
top.SetActive(false);
|
||||
if (_panelStack.Count > 0)
|
||||
var p = go.GetComponent<UIPanelBase>();
|
||||
if (p == null)
|
||||
{
|
||||
var restored = _panelStack.Peek();
|
||||
restored.SetActive(true);
|
||||
restored.GetComponent<IFocusable>()?.OnFocusRestored();
|
||||
if (go.GetComponent<CanvasGroup>() == null) go.AddComponent<CanvasGroup>();
|
||||
p = go.AddComponent<UISimplePanel>();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// ── 快捷事件回调 ──────────────────────────────────────────────────────
|
||||
private void HandleUICancelPressed()
|
||||
{
|
||||
if (_panelStack.Count > 0)
|
||||
CloseTopPanel();
|
||||
}
|
||||
|
||||
private void TogglePause()
|
||||
{
|
||||
if (_panelRegistry.TryGetValue(PanelId.Pause, out var pausePanel)
|
||||
&& _panelStack.Count > 0 && _panelStack.Peek() == pausePanel)
|
||||
CloseTopPanel();
|
||||
&& Navigator?.Top != null && Navigator.Top.gameObject == pausePanel)
|
||||
Navigator.Pop();
|
||||
else
|
||||
OpenPanel(PanelId.Pause);
|
||||
}
|
||||
|
||||
private IUINavigator Navigator => _navigator ??= ServiceLocator.GetOrDefault<IUINavigator>();
|
||||
private void OpenShop(string _) => OpenPanel(PanelId.Shop);
|
||||
private void OpenMap() => OpenPanel(PanelId.Map);
|
||||
private void OpenCharmPanel() => OpenPanel(PanelId.CharmPanel);
|
||||
@@ -335,8 +330,9 @@ namespace BaseGames.UI
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>仅供 UIManagerEditor 实时可视化面板栈(由栈顶到栈底顺序)。</summary>
|
||||
public GameObject[] EditorGetPanelSnapshot() => _panelStack.ToArray();
|
||||
/// <summary>仅供 UIManagerEditor 实时可视化栈顶面板(栈本体已迁移至 UINavigator)。</summary>
|
||||
public GameObject[] EditorGetPanelSnapshot()
|
||||
=> Navigator?.Top != null ? new[] { Navigator.Top.gameObject } : System.Array.Empty<GameObject>();
|
||||
#endif
|
||||
|
||||
[ContextMenu("测试:打开 Pause 面板")]
|
||||
|
||||
Reference in New Issue
Block a user