存档重构,游戏内容解锁机制;教程完善(未完成)

This commit is contained in:
SoulliesOfficial
2026-07-18 16:51:18 -04:00
parent d48ef1e65e
commit dda354ebb9
123 changed files with 4032 additions and 558 deletions

View File

@@ -3,10 +3,13 @@ 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;
@@ -21,6 +24,7 @@ namespace Ichni
public LoginPage loginPage;
public ChapterSelectionUIPage chapterSelectionUIPage;
public StoryUIPage storyUIPage;
public SelectionBoxUIPage selectionBoxUIPage;
// dialogUIPage 已随旧对话系统移除;阶段 3 将接入新的 VN 对话页面引用
public SongSelectionUIPage songSelectionUIPage;
public TransitionUIPage transitionUIPage;
@@ -34,6 +38,12 @@ namespace Ichni
{
AsyncOperation asyncOperation;
public bool isEnteringGame;
/// <summary>
/// 当前 MenuScene 是否已经完成 GameScene 预加载且尚未处于切场中。
/// TutorialFlowController 在写入“已处理”剧情变量前使用此状态进行最后一次启动前检查。
/// </summary>
public bool CanBeginGame => !isEnteringGame && asyncOperation != null;
private void Awake()
{
@@ -44,24 +54,39 @@ namespace Ichni
{
isEnteringGame = false;
if (InformationTransistor.instance.isReturnedFromGame)
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 =
InformationTransistor.instance.chapter;
information.chapter;
AudioManager.SetSwitch(ChapterSelectionManager.instance.currentChapter.chapterSwitch);
MenuInformationRecorder.instance.songSelectionRecords.Add(
ChapterSelectionManager.instance.currentChapter,
new SongSelectionRecord(InformationTransistor.instance.song, InformationTransistor.instance.difficulty));
MenuInformationRecorder.instance.songSelectionRecords[
ChapterSelectionManager.instance.currentChapter] =
new SongSelectionRecord(information.song, information.difficulty);
songSelectionUIPage.FadeIn();
}
else if (InformationTransistor.instance.isReturnedFromTutorial)
else if (returnDestination == MenuReturnDestination.Story &&
information != null && information.chapter != null)
{
startUIPage.mainCanvasGroup.gameObject.SetActive(false);
ChapterSelectionManager.instance.currentChapter = InformationTransistor.instance.chapter;
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");
@@ -73,15 +98,59 @@ namespace Ichni
{
public void EnterGame()
{
InformationTransistor.instance.isReturnedFromTutorial = false;
InformationTransistor.instance.isReturnedFromGame = true;
EnterGame(MenuReturnDestination.SongSelection);
}
/// <summary>
/// 激活已预加载的 GameScene并写入唯一的菜单返回目标。
/// 普通选曲使用 <see cref="MenuReturnDestination.SongSelection"/>,教程使用
/// <see cref="MenuReturnDestination.Story"/>。
/// </summary>
public void EnterGame(MenuReturnDestination returnDestination)
{
InformationTransistor.instance?.SetMenuReturnDestination(returnDestination);
EnterGameScene();
}
/// <summary>
/// 为 TutorialBlock 等非选曲入口准备上下文并播放通用转场。
/// 运行成功后会返回 true返回 false 时不会修改剧情变量或存档,调用方应保留原状。
/// </summary>
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()
{
MenuInputManager.instance.gameInput.Menu.Disable();
if (asyncOperation == null)
{
Debug.LogError("[MenuManager] GameScene 尚未完成预加载,无法进入游戏。");
isEnteringGame = false;
return;
}
MenuInputManager.instance?.gameInput?.Menu.Disable();
asyncOperation.allowSceneActivation = true;
}
}
}
}