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;