Files
zeling_v2/Assets/_Game/Scripts/UI/Menus/DataDrivenPauseMenuController.cs
2026-06-08 16:05:00 +08:00

129 lines
6.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 mapGameplay 的 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;
}
}
}
}