diff --git a/src/CounterDrone.Core/Simulation/FrameDataStore.cs b/src/CounterDrone.Core/Simulation/FrameDataStore.cs index bf9b1d1..4aa3357 100644 --- a/src/CounterDrone.Core/Simulation/FrameDataStore.cs +++ b/src/CounterDrone.Core/Simulation/FrameDataStore.cs @@ -14,6 +14,8 @@ namespace CounterDrone.Core.Simulation private string _taskId = string.Empty; public int RetentionDays { get; set; } = 180; public int BufferedFrameCount { get; private set; } + /// 仿真刚结束时保留内存快照,支持快速回放。Flush 后自动清空。 + public List LiveFrames { get; private set; } = new(); public FrameDataStore(IPathProvider paths) { @@ -25,6 +27,7 @@ namespace CounterDrone.Core.Simulation { _taskId = taskId; _buffer.Clear(); + LiveFrames.Clear(); BufferedFrameCount = 0; } @@ -60,7 +63,7 @@ namespace CounterDrone.Core.Simulation { var fr = _buffer[i]; var s = fr.Snapshot; - records.Add(new SimFrameRecord + var record = new SimFrameRecord { TaskId = _taskId, FrameIndex = fr.FrameIndex, Timestamp = fr.Timestamp, EntityId = s.EntityId, EntityType = (int)s.EntityType, @@ -72,8 +75,13 @@ namespace CounterDrone.Core.Simulation Models.EntityType.Cloud => $"{{\"radius\":{s.CloudRadius:F1},\"opacity\":{s.CloudOpacity:F2},\"phase\":{s.CloudPhase},\"elapsed\":{s.CloudElapsed:F1}}}", _ => "{}", }, - }); + }; + records.Add(record); } + + // 保留一份在内存供快速回放 + LiveFrames = records; + _buffer.Clear(); var dbPath = Path.Combine(_paths.GetFramesDir(), $"{_taskId}.db"); @@ -84,7 +92,7 @@ namespace CounterDrone.Core.Simulation db.InsertAll(records); } - public void Discard() { _buffer.Clear(); BufferedFrameCount = 0; } + public void Discard() { _buffer.Clear(); LiveFrames.Clear(); BufferedFrameCount = 0; } public List ReadFrames(string taskId, int fromFrame, int toFrame) { diff --git a/src/Unity/Assets/Scripts/Managers/ReplayController.cs b/src/Unity/Assets/Scripts/Managers/ReplayController.cs index 0d4e496..3a6f96f 100644 --- a/src/Unity/Assets/Scripts/Managers/ReplayController.cs +++ b/src/Unity/Assets/Scripts/Managers/ReplayController.cs @@ -7,7 +7,7 @@ using UnityEngine; namespace CounterDrone.Unity { - /// 回放控制器 — 从帧数据库读取历史仿真数据 + /// 回放控制器 — 仿真刚结束时用内存数据快速回放,历史仿真从数据库读取 public class ReplayController : MonoBehaviour { private FrameDataStore _frameStore; @@ -23,9 +23,19 @@ namespace CounterDrone.Unity _frameStore = new FrameDataStore(_paths); } - public void LoadReplay(string taskId) + /// 加载回放数据:优先使用传入的 frameStore 内存数据(仿真刚结束),否则读数据库 + public void LoadReplay(string taskId, FrameDataStore frameStore = null) { - CurrentFrames = _frameStore.ReadFrames(taskId, 0, int.MaxValue); + if (frameStore != null && frameStore.LiveFrames.Count > 0) + { + _frameStore = frameStore; + CurrentFrames = _frameStore.LiveFrames; + } + else + { + _frameStore ??= new FrameDataStore(_paths); + CurrentFrames = _frameStore.ReadFrames(taskId, 0, int.MaxValue); + } TotalFrames = CurrentFrames.Count > 0 ? CurrentFrames.Max(f => f.FrameIndex) + 1 diff --git a/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs b/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs index 38dd87d..41d74c9 100644 --- a/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs +++ b/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs @@ -20,6 +20,7 @@ namespace CounterDrone.Unity private SimulationEngine _engine; private FrameDataStore _frameStore; + public FrameDataStore FrameStore => _frameStore; private IScenarioService _scenario; private IPathProvider _paths; private string _taskId;