fix: 任务面板重构
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 → fix(cue)→ fixPanel → bodyModule → eye → showcase → day2SleepPresentation → playTool → screen。`fixPanel` 须在 `fix` 之后,以覆盖 Cue `EnterImmediate` 中的 `ResetPlug`;D2 动态表现须在通用 Showcase 图片之后恢复,才能叠加虚焦、缩放或场景专属动画。
|
**还原顺序**:punchTape → fix(cue)→ 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。
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user