using System; using System.Collections; using System.Collections.Generic; using AK.Wwise; using Ichni.Menu; using Ichni.Menu.UI; using Ichni.Story; using Ichni.Story.UI; using Ichni.UI; using Sirenix.OdinInspector; using SLSUtilities.WwiseAssistance; using UniRx; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.Serialization; namespace Ichni { public partial class MenuManager : MonoBehaviour { public static MenuManager instance; public StartUIPage startUIPage; public LoginPage loginPage; public ChapterSelectionUIPage chapterSelectionUIPage; public StoryUIPage storyUIPage; public SelectionBoxUIPage selectionBoxUIPage; // dialogUIPage 已随旧对话系统移除;阶段 3 将接入新的 VN 对话页面引用 public SongSelectionUIPage songSelectionUIPage; public TransitionUIPage transitionUIPage; public SettingsUIPage settingsUIPage; public List languageList; public List displayLanguageList; } public partial class MenuManager { AsyncOperation asyncOperation; public bool isEnteringGame; /// /// 当前 MenuScene 是否已经完成 GameScene 预加载且尚未处于切场中。 /// TutorialFlowController 在写入“已处理”剧情变量前使用此状态进行最后一次启动前检查。 /// public bool CanBeginGame => !isEnteringGame && asyncOperation != null; private void Awake() { instance = this; } private void Start() { isEnteringGame = false; InformationTransistor information = InformationTransistor.instance; MenuReturnDestination returnDestination = information != null ? information.menuReturnDestination : MenuReturnDestination.None; if (returnDestination == MenuReturnDestination.SongSelection && information != null && information.chapter != null && information.song != null && information.difficulty != null) { startUIPage.mainCanvasGroup.gameObject.SetActive(false); ChapterSelectionManager.instance.currentChapter = information.chapter; AudioManager.SetSwitch(ChapterSelectionManager.instance.currentChapter.chapterSwitch); MenuInformationRecorder.instance.songSelectionRecords[ ChapterSelectionManager.instance.currentChapter] = new SongSelectionRecord(information.song, information.difficulty); songSelectionUIPage.FadeIn(); } else if (returnDestination == MenuReturnDestination.Story && information != null && information.chapter != null) { startUIPage.mainCanvasGroup.gameObject.SetActive(false); ChapterSelectionManager.instance.currentChapter = information.chapter; AudioManager.SetSwitch(ChapterSelectionManager.instance.currentChapter.chapterSwitch); // MenuScene 会在教程游玩后重新创建,必须显式重建章节故事树, // 不能依赖旧场景中残留的 Block 实例。 StoryManager.instance?.OpenChapter(ChapterSelectionManager.instance.currentChapter.chapterIndex); storyUIPage.FadeIn(); } // 返回目标是一次性运行态信息。无论是否能恢复页面,都不能让它在下次重进菜单时重复生效。 information?.ClearMenuReturnDestination(); Application.targetFrameRate = SettingsManager.instance.gameSettings.targetFrame; asyncOperation = SceneManager.LoadSceneAsync("GameScene"); asyncOperation.allowSceneActivation = false; } } public partial class MenuManager { public void EnterGame() { EnterGame(MenuReturnDestination.SongSelection); } /// /// 激活已预加载的 GameScene,并写入唯一的菜单返回目标。 /// 普通选曲使用 ,教程使用 /// 。 /// public void EnterGame(MenuReturnDestination returnDestination) { InformationTransistor.instance?.SetMenuReturnDestination(returnDestination); EnterGameScene(); } /// /// 为 TutorialBlock 等非选曲入口准备上下文并播放通用转场。 /// 运行成功后会返回 true;返回 false 时不会修改剧情变量或存档,调用方应保留原状。 /// public bool TryBeginGame(ChapterSelectionUnit chapter, SongItemData song, DifficultyData difficulty, MenuReturnDestination returnDestination, UIPageBase sourcePage) { if (!CanBeginGame) { Debug.LogWarning("[MenuManager] GameScene 未完成预加载或菜单已在切换,忽略启动请求。"); return false; } if (InformationTransistor.instance == null || !InformationTransistor.instance.PrepareGameLaunch(chapter, song, difficulty, returnDestination)) { return false; } isEnteringGame = true; sourcePage?.FadeOut(); transitionUIPage?.FadeIn(); AudioManager.Post(AK.EVENTS.ENTERTOGAME); Observable.Timer(TimeSpan.FromSeconds(0.6f)).Subscribe(_ => EnterGameScene()); return true; } public void EnterGameScene() { if (asyncOperation == null) { Debug.LogError("[MenuManager] GameScene 尚未完成预加载,无法进入游戏。"); isEnteringGame = false; return; } MenuInputManager.instance?.gameInput?.Menu.Disable(); asyncOperation.allowSceneActivation = true; } } }