fix: 任务面板重构

This commit is contained in:
2026-07-26 18:07:34 +08:00
parent 0a1adf1558
commit 3cdedb155c
10 changed files with 386 additions and 94 deletions
@@ -2827,6 +2827,8 @@ Transform:
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: m_Children:
- {fileID: 895638590458225172} - {fileID: 895638590458225172}
- {fileID: 910000000000000002}
- {fileID: 910000000000000004}
- {fileID: 2161354415544340870} - {fileID: 2161354415544340870}
- {fileID: 930245164819552547} - {fileID: 930245164819552547}
- {fileID: 1768036090393189231} - {fileID: 1768036090393189231}
@@ -2845,7 +2847,74 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
taskRoot: {fileID: 895638590458225172} taskRoot: {fileID: 895638590458225172}
hiddenAnchor: {fileID: 910000000000000002}
shownAnchor: {fileID: 910000000000000004}
taskListParent: {fileID: 2078109273994026669} taskListParent: {fileID: 2078109273994026669}
taskItemPrefab: {fileID: 910000000000000005, guid: 63913c6ee20b38644bf16a505dd300f2, type: 3}
animationDuration: 1
animationEase: 6
--- !u!1 &910000000000000001
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 910000000000000002}
m_Layer: 0
m_Name: TaskPanelHiddenAnchor
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &910000000000000002
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 910000000000000001}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -10.32, y: 3.6511626, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4912059846133439537}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &910000000000000003
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 910000000000000004}
m_Layer: 0
m_Name: TaskPanelShownAnchor
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &910000000000000004
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 910000000000000003}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -7.12, y: 3.6511626, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4912059846133439537}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &2080789105181032567 --- !u!1 &2080789105181032567
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
+14
View File
@@ -11,6 +11,7 @@ GameObject:
- component: {fileID: 4156240678385147197} - component: {fileID: 4156240678385147197}
- component: {fileID: 3152720343440710739} - component: {fileID: 3152720343440710739}
- component: {fileID: 351969066121265812} - component: {fileID: 351969066121265812}
- component: {fileID: 910000000000000005}
m_Layer: 0 m_Layer: 0
m_Name: TaskItem m_Name: TaskItem
m_TagString: Untagged m_TagString: Untagged
@@ -171,3 +172,16 @@ MonoBehaviour:
m_hasFontAssetChanged: 0 m_hasFontAssetChanged: 0
m_renderer: {fileID: 3152720343440710739} m_renderer: {fileID: 3152720343440710739}
m_maskType: 0 m_maskType: 0
--- !u!114 &910000000000000005
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5370176113195622641}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4fe106c891d8f8b448b72b805e707075, type: 3}
m_Name:
m_EditorClassIdentifier:
label: {fileID: 351969066121265812}
-1
View File
@@ -56,7 +56,6 @@ namespace AibisDream.Utility
public const string CutLinePrefabName = "Prefab/CutLine"; public const string CutLinePrefabName = "Prefab/CutLine";
public const string TaskItemName = "Prefab/TaskItem";
public const string ActorPrefabName = "Prefab/ActorAnima"; public const string ActorPrefabName = "Prefab/ActorAnima";
public const string SpriteActorPrefabName = "Prefab/SpriteActor"; public const string SpriteActorPrefabName = "Prefab/SpriteActor";
@@ -102,7 +102,7 @@ namespace AibisDream.FixSystem
isStartupPrepared = true; isStartupPrepared = true;
targetLightState = LightState.Normal; targetLightState = LightState.Normal;
ApplySystemOnImmediate(false); ApplySystemOnImmediate(false);
taskPanel?.HidePanelImmediate(); taskPanel?.SetVisible(false, immediate: true);
} }
/// <summary> /// <summary>
@@ -205,7 +205,7 @@ namespace AibisDream.FixSystem
if (taskPanel != null) if (taskPanel != null)
{ {
taskPanel.CaptureSnapshot(out dto.isTaskPanelVisible, dto.tasks); taskPanel.CaptureSnapshot(out dto.isTaskPanelRequestedVisible, dto.tasks);
} }
return dto; return dto;
@@ -235,7 +235,7 @@ namespace AibisDream.FixSystem
GetRepairPanel<CablePanel>()?.ApplyCableSnapshot(dto.isCableRetracted, dto.pluggedModuleName); GetRepairPanel<CablePanel>()?.ApplyCableSnapshot(dto.isCableRetracted, dto.pluggedModuleName);
taskPanel?.ApplySnapshot(dto.isTaskPanelVisible, dto.tasks, immediate: true); taskPanel?.ApplySnapshot(dto.isTaskPanelRequestedVisible, dto.tasks);
} }
/// <summary>读档终态:跳过 StartAllSystems 动画,直接写面板壳层状态。</summary> /// <summary>读档终态:跳过 StartAllSystems 动画,直接写面板壳层状态。</summary>
@@ -1,121 +1,153 @@
using System.Collections.Generic; using System.Collections.Generic;
using AibisDream.Framework;
using AibisDream.SaveSystem; using AibisDream.SaveSystem;
using AibisDream.Utility; using DG.Tweening;
using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using DG.Tweening;
namespace AibisDream.FixSystem namespace AibisDream.FixSystem
{ {
public class TaskPanel : MonoBehaviour, ILineView public class TaskPanel : MonoBehaviour, ILineView
{ {
private const float HiddenLocalX = -10.32f; [Header("面板位置")]
private const float ShownLocalX = -7.12f;
#region
[SerializeField] private Transform taskRoot; [SerializeField] private Transform taskRoot;
[SerializeField] private Transform taskListParent; [SerializeField] private Transform hiddenAnchor;
private GameObject _taskItemPrefab; [SerializeField] private Transform shownAnchor;
private readonly Dictionary<string, GameObject> _taskDict = new();
#endregion [Header("任务列表")]
[SerializeField] private RectTransform taskListParent;
[SerializeField] private TaskItem taskItemPrefab;
[Header("动画")]
[SerializeField, Min(0f)] private float animationDuration = 1f;
[SerializeField] private Ease animationEase = Ease.OutQuad;
private readonly List<TaskEntry> _tasks = new();
private Tween _moveTween;
private bool _requestedVisible;
private bool _temporarilyHidden;
public bool RequestedVisible => _requestedVisible;
public bool EffectiveVisible => _requestedVisible && !_temporarilyHidden;
private sealed class TaskEntry
{
public string LineId;
public string Text;
public TaskItem View;
}
private void Awake() private void Awake()
{ {
_taskItemPrefab = ResourceKit.LoadAssetSync<GameObject>(ConstRef.TaskItemName); ValidateConfiguration();
} }
public bool IsPanelVisible => Mathf.Approximately(taskRoot.localPosition.x, ShownLocalX); private void OnEnable()
public void ShowPanel()
{ {
taskRoot.DOLocalMoveX(ShownLocalX, 1f); RefreshVisual(immediate: true);
} }
/// <summary>动画收起面板(与 ShowPanel 对称)。</summary> private void OnDisable()
public void HidePanel()
{ {
taskRoot.DOKill(); KillMoveTween();
taskRoot.DOLocalMoveX(HiddenLocalX, 1f);
} }
public void HidePanelImmediate() public void SetVisible(bool visible, bool immediate = false)
{ {
taskRoot.DOKill(); if (_requestedVisible == visible)
SetPanelVisibleImmediate(false); {
if (immediate)
{
RefreshVisual(immediate: true);
}
return;
}
var wasEffectivelyVisible = EffectiveVisible;
_requestedVisible = visible;
if (wasEffectivelyVisible != EffectiveVisible || immediate)
{
RefreshVisual(immediate);
}
} }
private bool _restoreVisibleAfterCollapse; public void SetTemporarilyHidden(bool hidden, bool immediate = false)
/// <summary>临时收起面板(如进入 Memory 模式),记录之前是否展开,供 RestoreCollapsedPanel 恢复。</summary>
public void CollapsePanelTemporary(bool immediate = false)
{ {
_restoreVisibleAfterCollapse = IsPanelVisible; if (_temporarilyHidden == hidden)
if (!_restoreVisibleAfterCollapse) return; {
if (immediate) HidePanelImmediate(); if (immediate)
else HidePanel(); {
} RefreshVisual(immediate: true);
}
/// <summary>按 CollapsePanelTemporary 记录的状态恢复面板(之前未展开则不做任何事)。</summary> return;
public void RestoreCollapsedPanel() }
{
if (!_restoreVisibleAfterCollapse) return; var wasEffectivelyVisible = EffectiveVisible;
_restoreVisibleAfterCollapse = false; _temporarilyHidden = hidden;
ShowPanel();
if (wasEffectivelyVisible != EffectiveVisible || immediate)
{
RefreshVisual(immediate);
}
} }
public void CompleteTask(string lineId) public void CompleteTask(string lineId)
{ {
if (_taskDict.Remove(lineId, out var taskItem)) var index = FindTaskIndex(lineId);
if (index < 0)
{ {
Destroy(taskItem); return;
} }
DestroyTaskView(_tasks[index].View);
_tasks.RemoveAt(index);
MarkTaskLayoutDirty();
} }
public void ClearTasks() public void ClearTasks()
{ {
foreach (var task in _taskDict) foreach (var task in _tasks)
{ {
Destroy(task.Value); DestroyTaskView(task.View);
} }
_taskDict.Clear();
_tasks.Clear();
MarkTaskLayoutDirty();
} }
public void RunLine(LineSyncToken token) public void RunLine(LineSyncToken token)
{ {
if (_taskDict.ContainsKey(token.lineInfo.lineId)) var lineId = token.lineInfo.lineId;
if (string.IsNullOrWhiteSpace(lineId))
{ {
EnumEventSystem.Global.Send(DialogEventEnum.LineShown); Debug.LogWarning($"[{nameof(TaskPanel)}] 忽略缺少 lineId 的任务文本。", this);
token.ForceAdvance(); }
return; else if (FindTaskIndex(lineId) < 0)
{
AddTaskItem(lineId, token.lineInfo.lineText);
} }
AddTaskItem(token.lineInfo.lineId, $"\u25cf {token.lineInfo.lineText}"); CompleteLineImmediately(token);
token.TextStart();
token.TextShown();
token.ForceAdvance();
} }
public void CaptureSnapshot(out bool isPanelVisible, List<TaskEntrySnapshotDto> tasks) public void CaptureSnapshot(out bool requestedVisible, List<TaskEntrySnapshotDto> tasks)
{ {
isPanelVisible = IsPanelVisible; requestedVisible = RequestedVisible;
tasks.Clear(); tasks.Clear();
foreach (var kv in _taskDict) foreach (var task in _tasks)
{ {
var text = kv.Value.GetComponent<TMP_Text>()?.text ?? string.Empty;
tasks.Add(new TaskEntrySnapshotDto tasks.Add(new TaskEntrySnapshotDto
{ {
lineId = kv.Key, lineId = task.LineId,
text = text text = task.Text
}); });
} }
} }
public void ApplySnapshot(bool isPanelVisible, List<TaskEntrySnapshotDto> tasks, bool immediate) public void ApplySnapshot(bool requestedVisible, List<TaskEntrySnapshotDto> tasks)
{ {
ClearTasks(); ClearTasks();
@@ -123,40 +155,208 @@ namespace AibisDream.FixSystem
{ {
foreach (var entry in tasks) foreach (var entry in tasks)
{ {
if (string.IsNullOrEmpty(entry.lineId)) if (entry == null || string.IsNullOrWhiteSpace(entry.lineId))
{ {
Debug.LogWarning($"[{nameof(TaskPanel)}] 跳过缺少 lineId 的任务快照。", this);
continue; continue;
} }
AddTaskItem(entry.lineId, entry.text); if (FindTaskIndex(entry.lineId) >= 0)
{
Debug.LogWarning($"[{nameof(TaskPanel)}] 跳过重复任务快照 '{entry.lineId}'。", this);
continue;
}
AddTaskItem(entry.lineId, entry.text ?? string.Empty);
} }
} }
if (immediate) SetVisible(requestedVisible, immediate: true);
}
private void RefreshVisual(bool immediate)
{
KillMoveTween();
var visible = EffectiveVisible;
if (visible)
{ {
taskRoot.DOKill(); SetTaskTextVisible(true);
SetPanelVisibleImmediate(isPanelVisible); MarkTaskLayoutDirty();
} }
else if (isPanelVisible)
if (taskRoot == null || hiddenAnchor == null || shownAnchor == null)
{ {
ShowPanel(); if (!visible)
{
SetTaskTextVisible(false);
}
return;
} }
var targetPosition = visible
? shownAnchor.localPosition
: hiddenAnchor.localPosition;
if (immediate || animationDuration <= 0f)
{
taskRoot.localPosition = targetPosition;
if (!visible)
{
SetTaskTextVisible(false);
}
return;
}
var tween = taskRoot
.DOLocalMove(targetPosition, animationDuration)
.SetEase(animationEase);
_moveTween = tween;
tween.OnComplete(() =>
{
if (_moveTween != tween)
{
return;
}
_moveTween = null;
if (!EffectiveVisible)
{
SetTaskTextVisible(false);
}
});
} }
private void AddTaskItem(string lineId, string text) private void AddTaskItem(string lineId, string text)
{ {
var taskItem = Instantiate(_taskItemPrefab, taskListParent); TaskItem view = null;
var task = taskItem.GetComponent<TMP_Text>(); if (taskItemPrefab == null || taskListParent == null)
task.text = text; {
_taskDict[lineId] = taskItem; Debug.LogError(
LayoutRebuilder.ForceRebuildLayoutImmediate(taskListParent.GetComponent<RectTransform>()); $"[{nameof(TaskPanel)}] 无法创建任务 '{lineId}'TaskItem Prefab 或任务列表父节点未配置。",
this);
}
else
{
view = Instantiate(taskItemPrefab, taskListParent);
view.SetText(text);
}
_tasks.Add(new TaskEntry
{
LineId = lineId,
Text = text,
View = view
});
MarkTaskLayoutDirty();
} }
private void SetPanelVisibleImmediate(bool visible) private int FindTaskIndex(string lineId)
{ {
var pos = taskRoot.localPosition; if (string.IsNullOrEmpty(lineId))
pos.x = visible ? ShownLocalX : HiddenLocalX; {
taskRoot.localPosition = pos; return -1;
}
for (var i = 0; i < _tasks.Count; i++)
{
if (_tasks[i].LineId == lineId)
{
return i;
}
}
return -1;
}
private static void CompleteLineImmediately(LineSyncToken token)
{
token.TextStart();
token.TextShown();
token.ForceAdvance();
}
private static void DestroyTaskView(TaskItem view)
{
if (view == null)
{
return;
}
view.gameObject.SetActive(false);
Destroy(view.gameObject);
}
private void SetTaskTextVisible(bool visible)
{
if (taskListParent != null && taskListParent.gameObject.activeSelf != visible)
{
taskListParent.gameObject.SetActive(visible);
}
}
private void MarkTaskLayoutDirty()
{
if (taskListParent != null)
{
LayoutRebuilder.MarkLayoutForRebuild(taskListParent);
}
}
private void KillMoveTween()
{
if (_moveTween == null)
{
return;
}
var tween = _moveTween;
_moveTween = null;
tween.Kill();
}
private void ValidateConfiguration()
{
if (taskRoot == null)
{
Debug.LogError($"[{nameof(TaskPanel)}] {name} 未配置 Task Root。", this);
}
if (hiddenAnchor == null)
{
Debug.LogError($"[{nameof(TaskPanel)}] {name} 未配置 Hidden Anchor。", this);
}
if (shownAnchor == null)
{
Debug.LogError($"[{nameof(TaskPanel)}] {name} 未配置 Shown Anchor。", this);
}
if (taskRoot != null
&& hiddenAnchor != null
&& shownAnchor != null
&& (taskRoot.parent != hiddenAnchor.parent || taskRoot.parent != shownAnchor.parent))
{
Debug.LogError($"[{nameof(TaskPanel)}] {name} 的 Task Root 与两个 Anchor 必须拥有同一个父节点。", this);
}
if (taskListParent == null)
{
Debug.LogError($"[{nameof(TaskPanel)}] {name} 未配置任务列表父节点。", this);
}
if (taskItemPrefab == null)
{
Debug.LogError($"[{nameof(TaskPanel)}] {name} 未配置 TaskItem Prefab。", this);
}
else if (!taskItemPrefab.IsConfigured)
{
Debug.LogError($"[{nameof(TaskPanel)}] {name} 的 TaskItem Prefab 未绑定文本组件。", this);
}
} }
} }
} }
@@ -37,7 +37,7 @@ namespace AibisDream
[YarnCommand("show_task_panel")] [YarnCommand("show_task_panel")]
public static void ShowTaskPanel() public static void ShowTaskPanel()
{ {
TaskPanel.ShowPanel(); TaskPanel?.SetVisible(true);
} }
#endregion #endregion
@@ -1,13 +1,23 @@
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
namespace AibisDream namespace AibisDream.FixSystem
{ {
public class TaskItem : MonoBehaviour public class TaskItem : MonoBehaviour
{ {
[SerializeField] private TMP_Text label;
public bool IsConfigured => label != null;
public void SetText(string value) public void SetText(string value)
{ {
GetComponent<TMP_Text>().text = $"\u25cf {value}"; if (label == null)
{
Debug.LogError($"[{nameof(TaskItem)}] {name} 未绑定文本组件。", this);
return;
}
label.text = $"\u25cf {value}";
} }
} }
} }
@@ -106,7 +106,7 @@ namespace AibisDream.FixSystem
float duration = 0.5f; float duration = 0.5f;
var memorySystem = FixSystemCenter.SystemDic.Get<MemoryProcess>(); var memorySystem = FixSystemCenter.SystemDic.Get<MemoryProcess>();
// 进入Memory前收起Task面板 // 进入Memory前收起Task面板
FixPanelSystem.Instance.TaskPanel?.CollapsePanelTemporary(); FixPanelSystem.Instance.TaskPanel?.SetTemporarilyHidden(true);
// 先切BodyModule // 先切BodyModule
if (!CameraKit.Instance.EqualToCameraState(CameraEnum.BodyModule)) if (!CameraKit.Instance.EqualToCameraState(CameraEnum.BodyModule))
{ {
@@ -126,7 +126,7 @@ namespace AibisDream.FixSystem
{ {
var memorySystem = FixSystemCenter.SystemDic.Get<MemoryProcess>(); var memorySystem = FixSystemCenter.SystemDic.Get<MemoryProcess>();
FixPanelSystem.Instance.TaskPanel?.CollapsePanelTemporary(immediate: true); FixPanelSystem.Instance.TaskPanel?.SetTemporarilyHidden(true, immediate: true);
yield return memorySystem.DropMemoryProjector(); yield return memorySystem.DropMemoryProjector();
FixPanelSystem.GetRepairPanel<CablePanel>()?.PullUpCable(); FixPanelSystem.GetRepairPanel<CablePanel>()?.PullUpCable();
yield return CameraKit.Instance.SwitchCamera(CameraEnum.Memory, true); yield return CameraKit.Instance.SwitchCamera(CameraEnum.Memory, true);
@@ -145,7 +145,7 @@ namespace AibisDream.FixSystem
yield return CameraKit.Instance.SwitchCamera(CameraEnum.BodyModule); yield return CameraKit.Instance.SwitchCamera(CameraEnum.BodyModule);
memorySystem.PullMemoryProjector(); memorySystem.PullMemoryProjector();
// 退出Memory后恢复Task面板 // 退出Memory后恢复Task面板
FixPanelSystem.Instance.TaskPanel?.RestoreCollapsedPanel(); FixPanelSystem.Instance.TaskPanel?.SetTemporarilyHidden(false);
} }
} }
} }
+3 -3
View File
@@ -146,7 +146,7 @@ Postflight 校验
Provider 使用 `ISyncSnapshotProvider``IAsyncSnapshotProvider` 显式声明同步/异步恢复;异步 Provider 必须把等待过程返回给编排层,禁止内部 fire-and-forget。 Provider 使用 `ISyncSnapshotProvider``IAsyncSnapshotProvider` 显式声明同步/异步恢复;异步 Provider 必须把等待过程返回给编排层,禁止内部 fire-and-forget。
## 快照 JSON 结构(schemaVersion = 1 ## 快照 JSON 结构(schemaVersion = 2
| 字段 | 含义 | | 字段 | 含义 |
| --- | --- | | --- | --- |
@@ -191,11 +191,11 @@ Fix 场景各 State 的 `Enter()` 通常包含相机过渡、Timeline 播放、F
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `punchTape` | `PunchTapeSnapshotDto` | 63 | 打孔带收藏 | | `punchTape` | `PunchTapeSnapshotDto` | 63 | 打孔带收藏 |
| `fix` | `FixSnapshotDto` | 65 | FixSceneDirector 宏观模式(state + args | | `fix` | `FixSnapshotDto` | 65 | FixSceneDirector 宏观模式(state + args |
| `fixPanel` | `FixPanelSnapshotDto` | 66 | FixPanel 壳层 + 线缆/插头 + TaskPanel(展开状态与任务列表) | | `fixPanel` | `FixPanelSnapshotDto` | 66 | FixPanel 壳层 + 线缆/插头 + TaskPanel请求展开状态与任务列表) |
| `bodyModule` | `BodyModuleSnapshotDto` | 67 | 插线模块物理态 | | `bodyModule` | `BodyModuleSnapshotDto` | 67 | 插线模块物理态 |
| `eye` | `EyeSnapshotDto` | 68 | Eye 叙事阶段 | | `eye` | `EyeSnapshotDto` | 68 | Eye 叙事阶段 |
**还原顺序**punchTape → fixcue)→ fixPanel → bodyModule → eye → showcase → day2SleepPresentation → playTool → screen。`fixPanel` 须在 `fix` 之后,以覆盖 Cue `EnterImmediate` 中的 `ResetPlug`;D2 动态表现须在通用 Showcase 图片之后恢复,才能叠加虚焦、缩放或场景专属动画。 **还原顺序**punchTape → fixcue)→ fixPanel → bodyModule → eye → showcase → day2SleepPresentation → playTool → screen。`fixPanel` 须在 `fix` 之后,以覆盖 Cue `EnterImmediate` 中的 `ResetPlug`,并让 Memory Cue 先建立 TaskPanel 临时隐藏状态;D2 动态表现须在通用 Showcase 图片之后恢复,才能叠加虚焦、缩放或场景专属动画。
**维修场景门控**`FixSceneSnapshotHelper`):上述 section 仅在 `FixSystemCenter.Instance != null` 时 Capture/Restore。 **维修场景门控**`FixSceneSnapshotHelper`):上述 section 仅在 `FixSystemCenter.Instance != null` 时 Capture/Restore。
+4 -4
View File
@@ -9,7 +9,7 @@ namespace AibisDream.SaveSystem
/// </summary> /// </summary>
public static class SaveSnapshotSchema public static class SaveSnapshotSchema
{ {
public const int CurrentVersion = 1; public const int CurrentVersion = 2;
} }
/// <summary> /// <summary>
@@ -269,7 +269,7 @@ namespace AibisDream.SaveSystem
/// <summary>Yarn 行 id,如 <c>line:01dade5</c>。</summary> /// <summary>Yarn 行 id,如 <c>line:01dade5</c>。</summary>
public string lineId; public string lineId;
/// <summary>存档时 UI 显示的完整文本(含 <c>● </c> 前缀。</summary> /// <summary>存档时已完成 substitutions 的本地化文本,不含 UI 圆点前缀。</summary>
public string text; public string text;
} }
@@ -290,8 +290,8 @@ namespace AibisDream.SaveSystem
/// <summary>插头插入的 BodyModule 名(data.moduleName);null/空 = 未插入。</summary> /// <summary>插头插入的 BodyModule 名(data.moduleName);null/空 = 未插入。</summary>
public string pluggedModuleName; public string pluggedModuleName;
/// <summary>TaskPanel 是否处于展开位(<c>show_task_panel</c> 后)。</summary> /// <summary>剧情是否要求 TaskPanel 展开;Memory 等临时隐藏状态不写入存档。</summary>
public bool isTaskPanelVisible; public bool isTaskPanelRequestedVisible;
/// <summary>TaskPanel 当前未完成任务列表(保持插入顺序)。</summary> /// <summary>TaskPanel 当前未完成任务列表(保持插入顺序)。</summary>
public List<TaskEntrySnapshotDto> tasks = new(); public List<TaskEntrySnapshotDto> tasks = new();