将以下小文件合并到相关核心类中,减少文件数量并保持职责清晰: - SaveSnapshotSummary -> SaveSnapshot - SnapshotProviderIds + SnapshotBootstrap -> SnapshotRegistry - SnapshotSectionDeserializer -> SnapshotRestore - SnapshotSerializer -> SnapshotPersistence 同步更新 SnapshotService 与 YarnVariableStorage 的调用点。
158 lines
5.9 KiB
C#
158 lines
5.9 KiB
C#
using System.Collections;
|
||
using Newtonsoft.Json.Linq;
|
||
using UnityEngine;
|
||
|
||
namespace AibisDream.SaveSystem
|
||
{
|
||
/// <summary>
|
||
/// 将 <see cref="SaveSnapshot"/> 逐项写回运行时(纯内存,不读盘)。
|
||
/// <para>
|
||
/// 顺序:Yarn 变量 → 场景加载 → 设置章节 → 各 Provider(按 RestoreOrder)→ 锚点加载对话并重进节点。
|
||
/// 完整淡入淡出时序由 P4 在 <see cref="SaveRestoreOrchestrator"/> 扩展。
|
||
/// </para>
|
||
/// </summary>
|
||
public static class SnapshotRestore
|
||
{
|
||
/// <summary>
|
||
/// 还原 Yarn 变量、场景、章节与各 section;不包含锚点重进(由 <see cref="RestoreAnchor"/> 或上层编排)。
|
||
/// </summary>
|
||
public static IEnumerator Restore(YarnVariableStorage storage, SaveSnapshot snapshot)
|
||
{
|
||
if (snapshot == null)
|
||
{
|
||
Debug.LogError("[SnapshotRestore] snapshot 为 null");
|
||
yield break;
|
||
}
|
||
|
||
RestoreYarnVariables(storage, snapshot);
|
||
yield return RestoreScene(snapshot);
|
||
RestoreSceneSo(snapshot);
|
||
|
||
foreach (var provider in SnapshotRegistry.GetOrderedProviders())
|
||
{
|
||
if (!snapshot.sections.TryGetValue(provider.SaveId, out var dto) || dto == null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
dto = CoerceSectionDto(provider.SaveId, dto);
|
||
yield return provider.Restore(dto);
|
||
}
|
||
|
||
if (snapshot.deepRepair != null && snapshot.deepRepair.sections != null
|
||
&& snapshot.deepRepair.sections.Count > 0)
|
||
{
|
||
Debug.LogWarning("[SnapshotRestore] DeepRepair 段尚未实现,已跳过(P5)。");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 按 <see cref="AnchorSnapshot"/> 加载对话工程并重新进入 Yarn 节点,实现「所见即所存」。
|
||
/// 调用前应已完成场景加载与各 provider 还原。
|
||
/// </summary>
|
||
public static IEnumerator RestoreAnchor(SaveSnapshot snapshot)
|
||
{
|
||
if (snapshot?.anchor == null || string.IsNullOrEmpty(snapshot.anchor.nodeName))
|
||
{
|
||
yield break;
|
||
}
|
||
|
||
var dialog = DialogController.Instance;
|
||
if (dialog == null) yield break;
|
||
|
||
var runner = dialog.DialogueRunner;
|
||
if (runner == null) yield break;
|
||
|
||
// 若当前 Runner 未加载对应 YarnProject,则先加载
|
||
if (!string.IsNullOrEmpty(snapshot.anchor.yarnProjectId))
|
||
{
|
||
var sceneSo = GameManager.Instance?.GetCurrentTalkSceneSo();
|
||
if (sceneSo?.yarnProject != null && sceneSo.yarnProject.name == snapshot.anchor.yarnProjectId)
|
||
{
|
||
if (runner.YarnProject == null || runner.YarnProject.name != snapshot.anchor.yarnProjectId)
|
||
{
|
||
dialog.LoadDialog(sceneSo.yarnProject);
|
||
}
|
||
}
|
||
else if (runner.YarnProject != null && runner.YarnProject.name != snapshot.anchor.yarnProjectId)
|
||
{
|
||
Debug.LogWarning(
|
||
$"[SnapshotRestore] YarnProject 不匹配:当前 {runner.YarnProject.name},存档 {snapshot.anchor.yarnProjectId}");
|
||
}
|
||
}
|
||
|
||
if (runner.IsDialogueRunning)
|
||
{
|
||
yield return runner.Stop();
|
||
}
|
||
|
||
yield return runner.StartDialogue(snapshot.anchor.nodeName);
|
||
}
|
||
|
||
private static void RestoreYarnVariables(YarnVariableStorage storage, SaveSnapshot snapshot)
|
||
{
|
||
var vars = snapshot.yarnVariables;
|
||
if (vars == null) return;
|
||
|
||
storage.SetAllVariables(vars.floats, vars.strings, vars.bools);
|
||
}
|
||
|
||
private static IEnumerator RestoreScene(SaveSnapshot snapshot)
|
||
{
|
||
if (snapshot.scene == null || string.IsNullOrEmpty(snapshot.scene.sceneName))
|
||
{
|
||
yield break;
|
||
}
|
||
|
||
var sceneLoader = SceneLoader.Instance;
|
||
if (sceneLoader == null)
|
||
{
|
||
Debug.LogError("[SnapshotRestore] SceneLoader 未初始化,无法加载场景");
|
||
yield break;
|
||
}
|
||
|
||
yield return sceneLoader.LoadSceneAsync(snapshot.scene.sceneName);
|
||
}
|
||
|
||
private static void RestoreSceneSo(SaveSnapshot snapshot)
|
||
{
|
||
if (snapshot.anchor == null || string.IsNullOrEmpty(snapshot.anchor.sceneSoName))
|
||
{
|
||
return;
|
||
}
|
||
|
||
var gameManager = GameManager.Instance;
|
||
if (gameManager == null)
|
||
{
|
||
Debug.LogWarning("[SnapshotRestore] GameManager 未初始化,无法设置章节 SO");
|
||
return;
|
||
}
|
||
|
||
gameManager.SetSceneSoByName(snapshot.anchor.sceneSoName);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读盘后 <see cref="SaveSnapshot.sections"/> 中的值常为 <see cref="JObject"/>;
|
||
/// 按 SaveId 转回各 Provider 所需的强类型 DTO。
|
||
/// </summary>
|
||
private static object CoerceSectionDto(string saveId, object dto)
|
||
{
|
||
if (dto is not JObject jobj)
|
||
{
|
||
return dto;
|
||
}
|
||
|
||
return saveId switch
|
||
{
|
||
SnapshotProviderIds.Environment => jobj.ToObject<EnvironmentSnapshotDto>(),
|
||
SnapshotProviderIds.Actor => jobj.ToObject<ActorSnapshotDto>(),
|
||
SnapshotProviderIds.Audio => jobj.ToObject<AudioSnapshotDto>(),
|
||
SnapshotProviderIds.Timeline => jobj.ToObject<TimelineSnapshotDto>(),
|
||
SnapshotProviderIds.Fix => jobj.ToObject<FixSnapshotDto>(),
|
||
SnapshotProviderIds.Screen => jobj.ToObject<ScreenSnapshotDto>(),
|
||
_ => jobj
|
||
};
|
||
}
|
||
}
|
||
}
|