123 lines
4.5 KiB
C#
123 lines
4.5 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using Yarn.Unity;
|
|
|
|
namespace AibisDream.SaveSystem
|
|
{
|
|
/// <summary>
|
|
/// 从运行时组装 <see cref="SaveSnapshot"/>(纯内存,不写盘)。
|
|
/// <para>
|
|
/// 流程:元数据 → 场景 → 锚点(含宏观章节)→ Yarn 变量 → 各 Provider.Capture。
|
|
/// scene / anchor 由框架直管;其余子系统通过 Provider 写入 sections。
|
|
/// </para>
|
|
/// </summary>
|
|
public static class SnapshotCapture
|
|
{
|
|
/// <summary>捕获当前完整快照。调用前需已注册全部 provider。</summary>
|
|
/// <param name="triggerNodeName">
|
|
/// 触发存档时的 Yarn 节点名(通常为 <c>OnNodeStart</c> 传入值)。
|
|
/// 非 null 时作为 anchor,并与 Capture 时刻的当前节点比较;不一致时打 warning,不阻断写盘。
|
|
/// </param>
|
|
/// <param name="omitAnchor">
|
|
/// 为 true 时写入空 <see cref="AnchorSnapshot.nodeName"/>(显式 <c><<save>></c> 默认行为)。
|
|
/// </param>
|
|
public static SaveSnapshot Capture(
|
|
YarnVariableStorage storage,
|
|
string triggerNodeName = null,
|
|
bool omitAnchor = false)
|
|
{
|
|
SnapshotRegistry.ValidateRequiredProviders();
|
|
|
|
var snapshot = new SaveSnapshot
|
|
{
|
|
gameVersion = Application.version,
|
|
savedAt = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
|
|
};
|
|
|
|
CaptureScene(snapshot);
|
|
CaptureAnchor(snapshot, triggerNodeName, omitAnchor);
|
|
CaptureYarnVariables(storage, snapshot);
|
|
|
|
foreach (var provider in SnapshotRegistry.GetOrderedProviders())
|
|
{
|
|
var dto = provider.Capture();
|
|
if (dto == null) continue;
|
|
snapshot.sections[provider.SaveId] = dto;
|
|
}
|
|
|
|
return snapshot;
|
|
}
|
|
|
|
private static void CaptureScene(SaveSnapshot snapshot)
|
|
{
|
|
var sceneLoader = SceneLoader.Instance;
|
|
snapshot.scene = new SceneSnapshotDto
|
|
{
|
|
sceneName = sceneLoader != null ? sceneLoader.CurrentSceneName : null
|
|
};
|
|
}
|
|
|
|
private static void CaptureAnchor(SaveSnapshot snapshot, string triggerNodeName, bool omitAnchor)
|
|
{
|
|
var dialog = DialogController.Instance;
|
|
if (dialog == null) return;
|
|
|
|
string anchorNodeName;
|
|
if (omitAnchor)
|
|
{
|
|
anchorNodeName = string.Empty;
|
|
}
|
|
else
|
|
{
|
|
var (currentNodeName, _) = dialog.GetCurrentNodeContext();
|
|
anchorNodeName = triggerNodeName ?? currentNodeName ?? string.Empty;
|
|
|
|
if (triggerNodeName != null
|
|
&& !string.Equals(
|
|
NormalizeNodeName(triggerNodeName),
|
|
NormalizeNodeName(currentNodeName),
|
|
StringComparison.Ordinal))
|
|
{
|
|
Debug.LogWarning(
|
|
$"[SnapshotCapture] 存档锚点漂移:触发时「{FormatNodeName(triggerNodeName)}」," +
|
|
$"Capture 时「{FormatNodeName(currentNodeName)}」。anchor 使用触发节点。");
|
|
}
|
|
}
|
|
|
|
var yarnProject = dialog.DialogueRunner?.YarnProject;
|
|
var projectId = yarnProject != null ? yarnProject.name : string.Empty;
|
|
|
|
var gameManager = GameManager.Instance;
|
|
var sceneSoName = gameManager != null ? gameManager.GetSceneSoName() : null;
|
|
|
|
snapshot.anchor = new AnchorSnapshot
|
|
{
|
|
sceneSoName = sceneSoName,
|
|
yarnProjectId = projectId,
|
|
nodeName = anchorNodeName
|
|
};
|
|
}
|
|
|
|
private static string NormalizeNodeName(string nodeName)
|
|
{
|
|
return string.IsNullOrEmpty(nodeName) ? string.Empty : nodeName;
|
|
}
|
|
|
|
private static string FormatNodeName(string nodeName)
|
|
{
|
|
return string.IsNullOrEmpty(nodeName) ? "(无节点)" : nodeName;
|
|
}
|
|
|
|
private static void CaptureYarnVariables(YarnVariableStorage storage, SaveSnapshot snapshot)
|
|
{
|
|
var (floats, strings, bools) = storage.GetAllVariables();
|
|
snapshot.yarnVariables = new YarnVariablesSnapshot
|
|
{
|
|
floats = floats ?? new(),
|
|
strings = strings ?? new(),
|
|
bools = bools ?? new()
|
|
};
|
|
}
|
|
}
|
|
}
|