feat(save): 新增梦境演出图与物品图的存档快照

新增 showcase(order 69) 与 playTool(order 70) 两个快照 section,
分别持久化 SpriteShowcase 小图/大图与 PlayToolPanel 物品图/全屏图
的终态,读档时直接还原静止画面。SpriteShowcase 与 PlayToolPanel
补充快照捕获/还原与立即清理逻辑,GameManager 在卸载场景前清理
演出效果。screen 还原顺序顺延至 80。
This commit is contained in:
2026-07-18 23:59:52 +08:00
parent 2eea496c4b
commit a5e417eeae
13 changed files with 597 additions and 49 deletions
@@ -5,7 +5,7 @@ namespace AibisDream.SaveSystem
{
/// <summary>
/// sections["fix"]Fix 场景宏观模式(FixSceneDirector)。
/// RestoreOrder=65,在 timeline(60) 之后、screen(70) 之前。
/// RestoreOrder=65,在 timeline(60) 之后、showcase(69) 之前。
/// </summary>
public class FixSnapshotProvider : IAsyncSnapshotProvider
{
@@ -0,0 +1,36 @@
using System.Collections;
using AibisDream.UI;
namespace AibisDream.SaveSystem
{
/// <summary>sections["playTool"]:物品图与全屏图。RestoreOrder=70。</summary>
public sealed class PlayToolSnapshotProvider : IAsyncSnapshotProvider
{
public string SaveId => SnapshotProviderIds.PlayTool;
public int RestoreOrder => 70;
public object Capture()
{
var panel = UIManager.Instance?.GetPanel<PlayToolPanel>();
return panel != null ? panel.CaptureSnapshot() : null;
}
public IEnumerator RestoreAsync(object dto, SnapshotRestoreContext context)
{
if (dto is not PlayToolSnapshotDto snapshot)
{
context.Warn("[PlayToolSnapshotProvider] DTO 类型不匹配,跳过还原。");
yield break;
}
var panel = UIManager.Instance?.GetPanel<PlayToolPanel>();
if (panel == null)
{
context.Warn("[PlayToolSnapshotProvider] PlayToolPanel 未初始化,跳过还原。");
yield break;
}
yield return panel.RestoreSnapshotAsync(snapshot, context.Warn);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6c8b98755ccb44089e8ac66b30c02c68
@@ -2,12 +2,12 @@ namespace AibisDream.SaveSystem
{
/// <summary>
/// sections["screen"]:屏幕饱和度与演出淡入淡出遮罩。
/// 委托 <see cref="ScreenSnapshotHelper"/>RestoreOrder=70通常在表现类最后还原。
/// 委托 <see cref="ScreenSnapshotHelper"/>RestoreOrder=80,在表现类最后还原。
/// </summary>
public class ScreenSnapshotProvider : ISyncSnapshotProvider
{
public string SaveId => SnapshotProviderIds.Screen;
public int RestoreOrder => 70;
public int RestoreOrder => 80;
public object Capture()
{
@@ -0,0 +1,36 @@
using System.Collections;
namespace AibisDream.SaveSystem
{
/// <summary>sections["showcase"]:梦境普通小图或大图。RestoreOrder=69。</summary>
public sealed class ShowcaseSnapshotProvider : IAsyncSnapshotProvider
{
public string SaveId => SnapshotProviderIds.Showcase;
public int RestoreOrder => 69;
public object Capture()
{
return SpriteShowcase.Instance != null
? SpriteShowcase.Instance.CaptureSnapshot()
: null;
}
public IEnumerator RestoreAsync(object dto, SnapshotRestoreContext context)
{
if (dto is not ShowcaseSnapshotDto snapshot)
{
context.Warn("[ShowcaseSnapshotProvider] DTO 类型不匹配,跳过还原。");
yield break;
}
var showcase = SpriteShowcase.Instance;
if (showcase == null)
{
context.Warn("[ShowcaseSnapshotProvider] SpriteShowcase 未初始化,跳过还原。");
yield break;
}
yield return showcase.RestoreSnapshotAsync(snapshot, context.Warn);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 83cccdfaca2a42a89ed515c16c6adfad
+5 -3
View File
@@ -100,7 +100,9 @@ SaveSystem/
├── TimelineSnapshotProvider.cs order 60
├── FixSnapshotProvider.cs order 65
├── FixPanelSnapshotProvider.cs order 66
── ScreenSnapshotProvider.cs order 70
── ShowcaseSnapshotProvider.cs order 69
├── PlayToolSnapshotProvider.cs order 70
└── ScreenSnapshotProvider.cs order 80
```
## 存盘 / 读盘流程
@@ -133,7 +135,7 @@ Phase 0 Yarn 变量(sync
Phase 1 场景加载(BarrierLoadSceneAsync)← 框架直管,不走 Provider
Phase 2 env / actor / audio / timeline / fix / screenProvider 按 RestoreOrder
Phase 2 env / actor / audio / timeline / fix / showcase / playTool / screenProvider 按 RestoreOrder
└─ timeline`Stopped` = untouched(从未 Evaluate),读档仅还原 `isActive`,不 Reset/Evaluate
Phase 2 可选 Barrier(如 Timeline Addressable 须显式等待)
@@ -200,7 +202,7 @@ Fix 场景各 State 的 `Enter()` 通常包含相机过渡、Timeline 播放、F
| `bodyModule` | `BodyModuleSnapshotDto` | 67 | 插线模块物理态 |
| `eye` | `EyeSnapshotDto` | 68 | Eye 叙事阶段 |
**还原顺序**punchTape → fixcue)→ fixPanel → bodyModule → eye → screen。`fixPanel` 须在 `fix` 之后,以覆盖 Cue `EnterImmediate` 中的 `ResetPlug`
**还原顺序**punchTape → fixcue)→ fixPanel → bodyModule → eye → showcase → playTool → screen。`fixPanel` 须在 `fix` 之后,以覆盖 Cue `EnterImmediate` 中的 `ResetPlug`
**维修场景门控**`FixSceneSnapshotHelper`):上述 section 仅在 `FixSystemCenter.Instance != null` 时 Capture/Restore。
+30
View File
@@ -180,6 +180,36 @@ namespace AibisDream.SaveSystem
public bool isFadeScreenCovered;
}
/// <summary>sections["playTool"]:常驻演出 UI 中的物品图与全屏图终态。</summary>
[Serializable]
public class PlayToolSnapshotDto
{
public bool isObjVisible;
public string objPicName;
public bool isFullScreenVisible;
public string fullScreenPicName;
}
/// <summary>Showcase 当前持久化的图片类型;不包含风筝及其他瞬时演出。</summary>
public enum ShowcaseDisplayMode
{
None,
Small,
Large
}
/// <summary>sections["showcase"]:梦境 Showcase 普通小图或大图终态。</summary>
[Serializable]
public class ShowcaseSnapshotDto
{
/// <summary><see cref="ShowcaseDisplayMode"/> 枚举名。</summary>
public string displayMode;
public string picName;
/// <summary>小图是否由分层资源组成;只恢复静止画面,不恢复运动。</summary>
public bool usesSplitLayers;
}
/// <summary>sections["subway"]:地铁场景宏观状态(SubwayCenter / OutSideState)。</summary>
[Serializable]
public class SubwaySnapshotDto
@@ -15,6 +15,8 @@ namespace AibisDream.SaveSystem
public const string Audio = "audio";
public const string Timeline = "timeline";
public const string Screen = "screen";
public const string PlayTool = "playTool";
public const string Showcase = "showcase";
public const string Subway = "subway";
public const string PunchTape = "punchTape";
public const string Fix = "fix";
@@ -29,7 +31,7 @@ namespace AibisDream.SaveSystem
/// </summary>
public static readonly string[] RequiredForCapture =
{
Environment, Actor, Audio, Timeline, Subway, Fix, Screen
Environment, Actor, Audio, Timeline, Subway, Fix, Showcase, PlayTool, Screen
};
}
@@ -59,6 +61,8 @@ namespace AibisDream.SaveSystem
Register(new FixPanelSnapshotProvider());
Register(new BodyModuleSnapshotProvider());
Register(new EyeSnapshotProvider());
Register(new ShowcaseSnapshotProvider());
Register(new PlayToolSnapshotProvider());
Register(new ScreenSnapshotProvider());
}
@@ -257,6 +257,8 @@ namespace AibisDream.SaveSystem
SnapshotProviderIds.FixPanel => jobj.ToObject<FixPanelSnapshotDto>(),
SnapshotProviderIds.BodyModule => jobj.ToObject<BodyModuleSnapshotDto>(),
SnapshotProviderIds.Eye => jobj.ToObject<EyeSnapshotDto>(),
SnapshotProviderIds.Showcase => jobj.ToObject<ShowcaseSnapshotDto>(),
SnapshotProviderIds.PlayTool => jobj.ToObject<PlayToolSnapshotDto>(),
SnapshotProviderIds.Screen => jobj.ToObject<ScreenSnapshotDto>(),
_ => jobj
};