Files
gold_dolphin/unity/Assets/UI/MainMenuUIController.cs
2026-07-06 20:19:52 +08:00

275 lines
10 KiB
C#

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
namespace GameFramework
{
/// <summary>
/// 主菜单 UI 控制器。
/// 支持开始游戏、设置、制作人员、退出游戏。
/// 修复:场景重载后自动重新查找 UI 引用,避免 DontDestroyOnLoad 导致的空引用问题。
/// 修复:返回主菜单后按钮全部失效(EventSystem/InputModule 状态异常)时主动兜底。
/// </summary>
public class MainMenuUIController : MonoBehaviour
{
[Header("Buttons")]
[SerializeField] Button startButton;
[SerializeField] Button settingsButton;
[SerializeField] Button creditsButton;
[SerializeField] Button quitButton;
[SerializeField] Button storyReplayButton;
[Header("剧情 PV")]
[SerializeField] StoryPVPlayer storyPVPlayer;
[Header("Settings Panel")]
[SerializeField] GameObject settingsPanel;
[SerializeField] Button settingsBackButton;
[Header("Credits Panel")]
[SerializeField] GameObject creditsPanel;
[SerializeField] Button creditsCloseButton;
[Header("新手引导")]
[SerializeField] GameObject guidePanel;
[SerializeField] Button guideStartButton;
void Start()
{
EnsureEventSystem();
// 自动查找(解决场景重载后引用失效问题)
if (startButton == null)
startButton = FindButton("StartButton");
if (settingsButton == null)
settingsButton = FindButton("SettingsButton");
if (creditsButton == null)
creditsButton = FindButton("CreditsButton");
if (quitButton == null)
quitButton = FindButton("QuitButton");
if (storyReplayButton == null)
storyReplayButton = FindButton("StoryReplayButton");
if (storyPVPlayer == null)
{
// 使用 FindObjectsByType 包含 inactive 对象,避免找不到隐藏的 VideoOverlay
var players = FindObjectsByType<StoryPVPlayer>(FindObjectsInactive.Include, FindObjectsSortMode.None);
if (players.Length > 0)
storyPVPlayer = players[0];
}
if (settingsPanel == null)
settingsPanel = GameObject.Find("SettingsPanel");
if (settingsBackButton == null && settingsPanel != null)
settingsBackButton = settingsPanel.GetComponentInChildren<Button>();
if (creditsPanel == null)
creditsPanel = GameObject.Find("CreditsPanel");
if (creditsCloseButton == null && creditsPanel != null)
creditsCloseButton = creditsPanel.GetComponentInChildren<Button>();
if (guidePanel == null)
{
// transform.Find 可以找到非激活的子对象
var guideTr = transform.Find("GuidePanel");
guidePanel = guideTr != null ? guideTr.gameObject : null;
}
if (guideStartButton == null && guidePanel != null)
guideStartButton = guidePanel.GetComponentInChildren<Button>(true);
// 绑定按钮事件
if (startButton != null)
{
startButton.onClick.AddListener(OnStartClick);
Debug.Log($"[MainMenu] 已绑定 startButton = {startButton.name}");
}
else
{
Debug.LogError("[MainMenu] startButton 未找到!开始游戏按钮将不可点。");
}
if (settingsButton != null)
{
settingsButton.onClick.AddListener(OnSettingsClick);
Debug.Log($"[MainMenu] 已绑定 settingsButton = {settingsButton.name}");
}
if (creditsButton != null)
{
creditsButton.onClick.AddListener(OnCreditsClick);
Debug.Log($"[MainMenu] 已绑定 creditsButton = {creditsButton.name}");
}
else
{
Debug.LogError("[MainMenu] creditsButton 未找到!制作人员按钮将不可点。");
}
if (quitButton != null)
{
quitButton.onClick.AddListener(OnQuitClick);
Debug.Log($"[MainMenu] 已绑定 quitButton = {quitButton.name}");
}
if (storyReplayButton != null)
{
// 避免 Inspector 中已绑定的事件导致重复触发
storyReplayButton.onClick.RemoveAllListeners();
storyReplayButton.onClick.AddListener(OnStoryReplayClick);
Debug.Log($"[MainMenu] 已绑定 storyReplayButton = {storyReplayButton.name}");
}
else
{
Debug.LogError("[MainMenu] storyReplayButton 未找到!剧情播放按钮将不可点。");
}
if (settingsBackButton != null)
settingsBackButton.onClick.AddListener(OnSettingsBackClick);
if (creditsCloseButton != null)
creditsCloseButton.onClick.AddListener(OnCreditsCloseClick);
if (guideStartButton != null)
guideStartButton.onClick.AddListener(OnGuideStartClick);
// 确保面板初始关闭
if (settingsPanel != null)
settingsPanel.SetActive(false);
if (creditsPanel != null)
creditsPanel.SetActive(false);
if (guidePanel != null)
guidePanel.SetActive(false);
}
Button FindButton(string name)
{
var btn = GameObject.Find(name);
return btn != null ? btn.GetComponent<Button>() : null;
}
/// <summary>
/// 兜底:确保场景里有一个可用的 EventSystem + StandaloneInputModule。
/// 当 activeInputHandler 配置错误(比如选了未安装的 Input System 包)或
/// EventSystem 上挂了丢失脚本的组件时,重载后所有按钮会同时失效。
/// 这里主动清理缺失脚本并补齐 InputModule。
/// </summary>
private void EnsureEventSystem()
{
var es = EventSystem.current;
if (es == null)
{
Debug.LogWarning("[MainMenu] EventSystem.current 为空!主动创建一个兜底。");
var go = new GameObject("EventSystem");
es = go.AddComponent<EventSystem>();
go.AddComponent<StandaloneInputModule>();
return;
}
// 删除 EventSystem 上所有"脚本丢失"的组件(Missing Script),
// 避免它们被 Unity 当作 InputSystemUIInputModule 处理而导致按钮不响应
var components = es.GetComponents<Component>();
foreach (var c in components)
{
if (c == null)
{
// c == null 说明是 missing script,需要通过序列化接口删除
// 这里只能用 DestroyImmediate(编辑器/启动阶段调用安全)
// 由于无法从 null 引用直接 Destroy,采用遍历 SerializedObject 的方式太重,
// 改为:如果发现任何 missing,直接重建一个干净的 EventSystem
Debug.LogWarning("[MainMenu] 发现 EventSystem 上有丢失脚本的组件,重建 EventSystem 以保证按钮可点。");
var old = es.gameObject;
var go = new GameObject("EventSystem_Fallback");
var newEs = go.AddComponent<EventSystem>();
go.AddComponent<StandaloneInputModule>();
Destroy(old);
return;
}
}
// 确保有 StandaloneInputModule(如果误用 InputSystemUIInputModule 但包未装,也会点不动)
if (es.GetComponent<StandaloneInputModule>() == null)
{
Debug.LogWarning("[MainMenu] EventSystem 缺少 StandaloneInputModule,已自动添加。");
es.gameObject.AddComponent<StandaloneInputModule>();
}
}
void OnStartClick()
{
Debug.Log("[MainMenu] OnStartClick 触发");
// 显示新手引导页,而非直接进入游戏
if (guidePanel != null)
{
guidePanel.SetActive(true);
Debug.Log("[MainMenu] 显示新手引导页");
}
else if (SceneLoader.Instance != null)
{
// 没有引导页则直接进入游戏
SceneLoader.Instance.LoadGameplayScene();
}
}
void OnGuideStartClick()
{
if (guidePanel != null)
guidePanel.SetActive(false);
if (SceneLoader.Instance != null)
SceneLoader.Instance.LoadGameplayScene();
}
void OnSettingsClick()
{
if (settingsPanel != null)
settingsPanel.SetActive(true);
}
void OnSettingsBackClick()
{
if (settingsPanel != null)
settingsPanel.SetActive(false);
}
void OnCreditsClick()
{
Debug.Log($"[MainMenu] OnCreditsClick 触发, creditsPanel={(creditsPanel != null ? creditsPanel.name : "null")}");
if (creditsPanel != null)
{
creditsPanel.SetActive(true);
Debug.Log($"[MainMenu] Showing credits panel: {creditsPanel.name}");
}
else
{
Debug.LogWarning("[MainMenu] creditsPanel not found! Make sure a GameObject named 'CreditsPanel' exists in the scene.");
}
}
void OnCreditsCloseClick()
{
if (creditsPanel != null)
creditsPanel.SetActive(false);
}
void OnQuitClick()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
void OnStoryReplayClick()
{
Debug.Log($"[MainMenu] OnStoryReplayClick 触发, storyPVPlayer={(storyPVPlayer != null ? storyPVPlayer.gameObject.name : "null")}");
if (storyPVPlayer != null)
{
Debug.Log($"[MainMenu] 播放 PV, StoryPVPlayer on: {storyPVPlayer.gameObject.name}");
storyPVPlayer.Play();
}
else
{
Debug.LogWarning("[MainMenu] StoryPVPlayer 未配置!");
}
}
}
}