129 lines
6.0 KiB
C#
129 lines
6.0 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using BaseGames.Core.Assets;
|
||
using BaseGames.Core.Events;
|
||
using BaseGames.UI.MainMenu; // 复用通用菜单按钮视图 MainMenuButtonView
|
||
|
||
namespace BaseGames.UI
|
||
{
|
||
/// <summary>
|
||
/// 数据驱动暂停菜单(仿 <see cref="MainMenu.DataDrivenMainMenuController"/>)。
|
||
/// 据 <see cref="PauseMenuConfigSO"/> 在运行时生成按钮、派发动作;生命周期/焦点/淡入由 <see cref="UIPanelBase"/> 统一处理。
|
||
/// 策划改 UI_PauseMenuConfig 即可增删/重排/改标签/改动作,零代码;样式改 UI_PauseScreen / UI_MainMenu_Button 预制件。
|
||
/// </summary>
|
||
public class DataDrivenPauseMenuController : UIPanelBase
|
||
{
|
||
[Header("数据表 / 按钮列表")]
|
||
[SerializeField] private PauseMenuConfigSO _config;
|
||
[Tooltip("按钮的父节点(通常挂 VerticalLayoutGroup)。")]
|
||
[SerializeField] private Transform _container;
|
||
[SerializeField] private MainMenuButtonView _buttonPrefab;
|
||
|
||
[Header("Event Channels")]
|
||
[SerializeField] private VoidEventChannelSO _onResumeRequested;
|
||
[SerializeField] private SceneLoadRequestEventChannelSO _onSceneLoadRequest;
|
||
|
||
private IUIManager _uiManager;
|
||
private readonly List<MainMenuButtonView> _buttons = new();
|
||
private MainMenuButtonView _firstButton;
|
||
|
||
/// <summary>本面板因某个菜单动作(继续 / 返回主菜单)而关闭——它们已自行处理游戏状态,
|
||
/// OnPanelClose 不应再发恢复事件。ESC / 手柄 B 取消关闭时此标志为 false,需要补发恢复。</summary>
|
||
private bool _resumeHandledByAction;
|
||
|
||
// 暂停面板由 UIManager 开启,此时 ServiceLocator 已就绪
|
||
protected override void OnPanelOpen()
|
||
{
|
||
_uiManager = GetService<IUIManager>();
|
||
BuildMenu();
|
||
}
|
||
|
||
protected override void OnPanelClose()
|
||
{
|
||
_uiManager = null;
|
||
|
||
// 被弹出栈(ESC / 手柄 B 取消)关闭时也要恢复游戏:否则 GameManager 停在 Paused、
|
||
// 输入留在 UI map,Gameplay 的 Pause 键被禁用,导致暂停菜单关闭后再也无法呼出。
|
||
//
|
||
// 仅在「真正出栈」时恢复——被上层面板(设置等 Replace 压栈)覆盖时本面板的 OnDisable
|
||
// 同样触发,但此时本面板仍是栈顶(UINavigator.Push 先停用下层再压入新面板),不应恢复。
|
||
// 区分依据:出栈时 UINavigator.Pop 已先出栈,栈顶不再是本面板;覆盖时栈顶仍是本面板。
|
||
// 「继续」「返回主菜单」已各自处理状态,由 _resumeHandledByAction 抑制重复 / 误恢复。
|
||
var nav = GetService<IUINavigator>();
|
||
bool poppedOff = nav == null || nav.Top != this;
|
||
if (poppedOff && !_resumeHandledByAction)
|
||
_onResumeRequested?.Raise();
|
||
_resumeHandledByAction = false;
|
||
}
|
||
|
||
/// <summary>默认焦点 / 焦点恢复回到首个按钮。</summary>
|
||
protected override GameObject ResolveFirstSelected()
|
||
=> _firstButton != null ? _firstButton.Button.gameObject : null;
|
||
|
||
/// <summary>据配置重建按钮列表(public 以便编辑器预览/测试)。</summary>
|
||
public void BuildMenu()
|
||
{
|
||
ClearMenu();
|
||
if (_config == null || _container == null || _buttonPrefab == null) return;
|
||
|
||
foreach (var item in _config.Items)
|
||
{
|
||
var view = Instantiate(_buttonPrefab, _container);
|
||
view.gameObject.SetActive(true);
|
||
var captured = item;
|
||
view.Bind(item.labelKey, item.icon, () => Dispatch(captured));
|
||
_buttons.Add(view);
|
||
if (_firstButton == null) _firstButton = view;
|
||
}
|
||
}
|
||
|
||
private void ClearMenu()
|
||
{
|
||
_buttons.Clear();
|
||
_firstButton = null;
|
||
if (_container == null) return;
|
||
for (int i = _container.childCount - 1; i >= 0; i--)
|
||
{
|
||
var child = _container.GetChild(i).gameObject;
|
||
if (Application.isPlaying) Destroy(child);
|
||
else DestroyImmediate(child);
|
||
}
|
||
}
|
||
|
||
// ── 动作派发 ──────────────────────────────────────────────────────────
|
||
private void Dispatch(PauseMenuConfigSO.Item item)
|
||
{
|
||
switch (item.action)
|
||
{
|
||
case PauseMenuAction.Resume:
|
||
_resumeHandledByAction = true; // 自行发恢复,避免 OnPanelClose 重复发
|
||
_onResumeRequested?.Raise();
|
||
_uiManager?.CloseTopPanel();
|
||
break;
|
||
case PauseMenuAction.OpenSettings:
|
||
_uiManager?.OpenPanel(PanelId.Settings);
|
||
break;
|
||
case PauseMenuAction.OpenPanel:
|
||
_uiManager?.OpenPanel(item.targetPanel);
|
||
break;
|
||
case PauseMenuAction.ReturnToMainMenu:
|
||
// 不发恢复:须保持 Paused 状态,使 SceneLoaded(MainMenu) 时 Paused→MainMenu 为合法转换。
|
||
_resumeHandledByAction = true;
|
||
_uiManager?.CloseTopPanel();
|
||
_onSceneLoadRequest?.Raise(new SceneLoadRequest
|
||
{
|
||
SceneName = string.IsNullOrEmpty(item.sceneKey) ? AddressKeys.SceneMainMenu : item.sceneKey,
|
||
TransitionType = TransitionType.Scene,
|
||
});
|
||
break;
|
||
case PauseMenuAction.Quit:
|
||
Application.Quit();
|
||
break;
|
||
case PauseMenuAction.RaiseEvent:
|
||
item.eventChannel?.Raise();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|