275 lines
10 KiB
C#
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 未配置!");
|
|
}
|
|
}
|
|
}
|
|
}
|