SimulationEngine: 6个观察者事件 + 字段声明修复 + 重写整理
This commit is contained in:
parent
2d14c17d9c
commit
1d8277cecd
@ -9,16 +9,14 @@ using SQLite;
|
|||||||
|
|
||||||
namespace CounterDrone.Core.Simulation
|
namespace CounterDrone.Core.Simulation
|
||||||
{
|
{
|
||||||
/// <summary>发射计划中的单次事件</summary>
|
|
||||||
public class FireEvent
|
public class FireEvent
|
||||||
{
|
{
|
||||||
public float FireTime;
|
public float FireTime;
|
||||||
public int PlatformIndex; // 第几个发射平台(0-based)
|
public int PlatformIndex;
|
||||||
public float TargetX, TargetY, TargetZ;
|
public float TargetX, TargetY, TargetZ;
|
||||||
public float MuzzleVelocity;
|
public float MuzzleVelocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>仿真引擎 — 纯执行器,不包含任何决策逻辑</summary>
|
|
||||||
/// <summary>仿真引擎 — 纯执行器,接收发射计划按时间表执行</summary>
|
/// <summary>仿真引擎 — 纯执行器,接收发射计划按时间表执行</summary>
|
||||||
public class SimulationEngine
|
public class SimulationEngine
|
||||||
{
|
{
|
||||||
@ -30,35 +28,37 @@ namespace CounterDrone.Core.Simulation
|
|||||||
public SimulationState State { get; private set; } = SimulationState.Idle;
|
public SimulationState State { get; private set; } = SimulationState.Idle;
|
||||||
public int FrameIndex { get; private set; }
|
public int FrameIndex { get; private set; }
|
||||||
public float SimulationTime { get; private set; }
|
public float SimulationTime { get; private set; }
|
||||||
/// <summary>时间倍速:>1 加速仿真(仅测试或快速推演用)</summary>
|
|
||||||
public float TimeScale { get; set; } = 1f;
|
public float TimeScale { get; set; } = 1f;
|
||||||
private float _tickInterval;
|
private float _tickInterval;
|
||||||
|
|
||||||
|
public IReadOnlyList<DroneEntity> Drones => _drones;
|
||||||
|
public IReadOnlyList<CloudEntity> Clouds => _clouds;
|
||||||
|
public IReadOnlyList<MunitionEntity> Munitions => _munitions;
|
||||||
|
public IReadOnlyList<SimEvent> Events => _allEvents;
|
||||||
|
|
||||||
|
public event Action<MunitionEntity> OnMunitionLaunched;
|
||||||
|
public event Action<CloudEntity> OnCloudGenerated;
|
||||||
|
public event Action<DroneEntity> OnDroneDestroyed;
|
||||||
|
public event Action<DroneEntity> OnDroneReachedTarget;
|
||||||
|
public event Action<DroneEntity, ControlZoneEntity> OnZoneIntruded;
|
||||||
|
public event Action OnSimulationEnded;
|
||||||
|
|
||||||
private List<DroneEntity> _drones = new();
|
private List<DroneEntity> _drones = new();
|
||||||
private List<PlatformEntity> _platforms = new();
|
private List<PlatformEntity> _platforms = new();
|
||||||
private List<MunitionEntity> _munitions = new();
|
private List<MunitionEntity> _munitions = new();
|
||||||
private List<CloudEntity> _clouds = new();
|
private List<CloudEntity> _clouds = new();
|
||||||
private List<ControlZoneEntity> _zones = new();
|
private List<ControlZoneEntity> _zones = new();
|
||||||
|
|
||||||
private CombatScene _scene = new();
|
private CombatScene _scene = new();
|
||||||
private float _disperseHeight;
|
private float _disperseHeight;
|
||||||
private AmmunitionSpec _ammoSpec = new();
|
private AmmunitionSpec _ammoSpec = new();
|
||||||
private int _tickRate = 20;
|
private int _tickRate = 20;
|
||||||
|
|
||||||
// 发射计划:算法预计算,引擎按时间表执行
|
|
||||||
private readonly List<FireEvent> _fireSchedule = new();
|
private readonly List<FireEvent> _fireSchedule = new();
|
||||||
private int _nextFireIndex;
|
private int _nextFireIndex;
|
||||||
|
|
||||||
private readonly List<SimEvent> _allEvents = new();
|
private readonly List<SimEvent> _allEvents = new();
|
||||||
private SQLiteConnection _frameDb = null!;
|
private SQLiteConnection _frameDb = null!;
|
||||||
private string _taskId = string.Empty;
|
private string _taskId = string.Empty;
|
||||||
private int _entityCounter;
|
private int _entityCounter;
|
||||||
|
|
||||||
public IReadOnlyList<DroneEntity> Drones => _drones;
|
|
||||||
public IReadOnlyList<CloudEntity> Clouds => _clouds;
|
|
||||||
public IReadOnlyList<MunitionEntity> Munitions => _munitions;
|
|
||||||
public IReadOnlyList<SimEvent> Events => _allEvents;
|
|
||||||
|
|
||||||
public SimulationEngine(IScenarioService scenarioService, FrameDataStore frameStore,
|
public SimulationEngine(IScenarioService scenarioService, FrameDataStore frameStore,
|
||||||
IDamageModel damageModel, IPathProvider paths)
|
IDamageModel damageModel, IPathProvider paths)
|
||||||
{
|
{
|
||||||
@ -68,7 +68,6 @@ namespace CounterDrone.Core.Simulation
|
|||||||
_paths = paths;
|
_paths = paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>设置发射计划(来自推荐算法),在 Initialize 之前调用</summary>
|
|
||||||
public void SetFireSchedule(List<FireEvent> schedule)
|
public void SetFireSchedule(List<FireEvent> schedule)
|
||||||
{
|
{
|
||||||
_fireSchedule.Clear();
|
_fireSchedule.Clear();
|
||||||
@ -88,13 +87,10 @@ namespace CounterDrone.Core.Simulation
|
|||||||
_tickInterval = 1.0f / _tickRate;
|
_tickInterval = 1.0f / _tickRate;
|
||||||
|
|
||||||
using (var ammoDb = new SQLiteConnection(_paths.GetMainDbPath()))
|
using (var ammoDb = new SQLiteConnection(_paths.GetMainDbPath()))
|
||||||
{
|
|
||||||
_ammoSpec = ammoDb.Table<AmmunitionSpec>()
|
_ammoSpec = ammoDb.Table<AmmunitionSpec>()
|
||||||
.FirstOrDefault(a => a.AerosolType == config.Cloud.AerosolType)
|
.FirstOrDefault(a => a.AerosolType == config.Cloud.AerosolType)
|
||||||
?? new AmmunitionSpec { InitialRadius = 50, CoreDensity = 1.0f, SourceStrength = 1.0, MaxRadius = 200, MaxDuration = 60 };
|
?? DefaultAmmunition.GetByType((AerosolType)config.Cloud.AerosolType);
|
||||||
}
|
|
||||||
|
|
||||||
// 无人机
|
|
||||||
_drones.Clear();
|
_drones.Clear();
|
||||||
foreach (var target in config.Targets)
|
foreach (var target in config.Targets)
|
||||||
{
|
{
|
||||||
@ -105,18 +101,12 @@ namespace CounterDrone.Core.Simulation
|
|||||||
target, config.Waypoints, i, spacing, mode));
|
target, config.Waypoints, i, spacing, mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 装备
|
|
||||||
_platforms.Clear();
|
_platforms.Clear();
|
||||||
foreach (var equip in config.Equipment)
|
foreach (var equip in config.Equipment)
|
||||||
{
|
|
||||||
if (equip.EquipmentRole != (int)EquipmentRole.Detection)
|
if (equip.EquipmentRole != (int)EquipmentRole.Detection)
|
||||||
{
|
|
||||||
for (int i = 0; i < equip.Quantity; i++)
|
for (int i = 0; i < equip.Quantity; i++)
|
||||||
_platforms.Add(new PlatformEntity($"plat_{++_entityCounter}", equip));
|
_platforms.Add(new PlatformEntity($"plat_{++_entityCounter}", equip));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 管控区域
|
|
||||||
_zones.Clear();
|
_zones.Clear();
|
||||||
foreach (var z in config.ControlZones)
|
foreach (var z in config.ControlZones)
|
||||||
_zones.Add(new ControlZoneEntity(z.Id, z.Name, z.VerticesJson,
|
_zones.Add(new ControlZoneEntity(z.Id, z.Name, z.VerticesJson,
|
||||||
@ -129,7 +119,6 @@ namespace CounterDrone.Core.Simulation
|
|||||||
SimulationTime = 0;
|
SimulationTime = 0;
|
||||||
_entityCounter = 0;
|
_entityCounter = 0;
|
||||||
_nextFireIndex = 0;
|
_nextFireIndex = 0;
|
||||||
|
|
||||||
_frameDb = _frameStore.CreateOrOpen(_taskId);
|
_frameDb = _frameStore.CreateOrOpen(_taskId);
|
||||||
State = SimulationState.Running;
|
State = SimulationState.Running;
|
||||||
}
|
}
|
||||||
@ -145,12 +134,10 @@ namespace CounterDrone.Core.Simulation
|
|||||||
var frameEvents = new List<SimEvent>();
|
var frameEvents = new List<SimEvent>();
|
||||||
|
|
||||||
// 1. 按发射计划执行
|
// 1. 按发射计划执行
|
||||||
while (_nextFireIndex < _fireSchedule.Count &&
|
while (_nextFireIndex < _fireSchedule.Count && _fireSchedule[_nextFireIndex].FireTime <= SimulationTime)
|
||||||
_fireSchedule[_nextFireIndex].FireTime <= SimulationTime)
|
|
||||||
{
|
{
|
||||||
var fe = _fireSchedule[_nextFireIndex++];
|
var fe = _fireSchedule[_nextFireIndex++];
|
||||||
var platform = _platforms.Count > fe.PlatformIndex
|
var platform = _platforms.Count > fe.PlatformIndex ? _platforms[fe.PlatformIndex] : null;
|
||||||
? _platforms[fe.PlatformIndex] : null;
|
|
||||||
if (platform != null && platform.Ready)
|
if (platform != null && platform.Ready)
|
||||||
{
|
{
|
||||||
platform.Fire();
|
platform.Fire();
|
||||||
@ -158,16 +145,10 @@ namespace CounterDrone.Core.Simulation
|
|||||||
platform.PlatformType ?? Models.PlatformType.GroundBased,
|
platform.PlatformType ?? Models.PlatformType.GroundBased,
|
||||||
platform.AerosolType ?? Models.AerosolType.InertGas,
|
platform.AerosolType ?? Models.AerosolType.InertGas,
|
||||||
platform.PosX, platform.PosY, platform.PosZ,
|
platform.PosX, platform.PosY, platform.PosZ,
|
||||||
fe.TargetX, fe.TargetY, fe.TargetZ,
|
fe.TargetX, fe.TargetY, fe.TargetZ, fe.MuzzleVelocity, _disperseHeight);
|
||||||
fe.MuzzleVelocity, _disperseHeight);
|
|
||||||
_munitions.Add(m);
|
_munitions.Add(m);
|
||||||
frameEvents.Add(new SimEvent
|
OnMunitionLaunched?.Invoke(m);
|
||||||
{
|
frameEvents.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = SimulationTime, SourceId = platform.Id, Description = $"计划发射 {m.Id}" });
|
||||||
Type = SimEventType.MunitionLaunched,
|
|
||||||
OccurredAt = SimulationTime,
|
|
||||||
SourceId = platform.Id,
|
|
||||||
Description = $"计划发射 {m.Id}",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,16 +159,12 @@ namespace CounterDrone.Core.Simulation
|
|||||||
if (m.HasArrived)
|
if (m.HasArrived)
|
||||||
{
|
{
|
||||||
var disp = AlgorithmFactory.Create<ICloudDispersionModel>();
|
var disp = AlgorithmFactory.Create<ICloudDispersionModel>();
|
||||||
disp.Initialize(_ammoSpec, _scene,
|
disp.Initialize(_ammoSpec, _scene, new Algorithms.Vector3(m.TargetX, m.TargetY, m.TargetZ), SimulationTime);
|
||||||
new Algorithms.Vector3(m.TargetX, m.TargetY, m.TargetZ), SimulationTime);
|
var cloud = new CloudEntity($"cloud_{++_entityCounter}", m.AerosolType, disp, SimulationTime);
|
||||||
_clouds.Add(new CloudEntity($"cloud_{++_entityCounter}", m.AerosolType, disp, SimulationTime));
|
_clouds.Add(cloud);
|
||||||
_munitions.Remove(m);
|
_munitions.Remove(m);
|
||||||
frameEvents.Add(new SimEvent
|
OnCloudGenerated?.Invoke(cloud);
|
||||||
{
|
frameEvents.Add(new SimEvent { Type = SimEventType.CloudGenerated, OccurredAt = SimulationTime, Description = "云团生成" });
|
||||||
Type = SimEventType.CloudGenerated,
|
|
||||||
OccurredAt = SimulationTime,
|
|
||||||
Description = $"云团生成",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,18 +183,13 @@ namespace CounterDrone.Core.Simulation
|
|||||||
if (cloud.ContainsPoint(drone.PosX, drone.PosY, drone.PosZ))
|
if (cloud.ContainsPoint(drone.PosX, drone.PosY, drone.PosZ))
|
||||||
{
|
{
|
||||||
drone.ExposureTime += scaledDt;
|
drone.ExposureTime += scaledDt;
|
||||||
var dmg = _damageModel.CalculateDamage(
|
var dmg = _damageModel.CalculateDamage(drone.TargetType, drone.PowerType, cloud.AerosolType, cloud.Dispersion.CoreDensity, drone.ExposureTime, scaledDt);
|
||||||
drone.TargetType, drone.PowerType, cloud.AerosolType,
|
|
||||||
cloud.Dispersion.CoreDensity, drone.ExposureTime, scaledDt);
|
|
||||||
drone.ApplyDamage(dmg);
|
drone.ApplyDamage(dmg);
|
||||||
if (drone.Status == DroneStatus.Destroyed)
|
if (drone.Status == DroneStatus.Destroyed)
|
||||||
frameEvents.Add(new SimEvent
|
{
|
||||||
{
|
OnDroneDestroyed?.Invoke(drone);
|
||||||
Type = SimEventType.DroneDestroyed,
|
frameEvents.Add(new SimEvent { Type = SimEventType.DroneDestroyed, OccurredAt = SimulationTime, TargetId = drone.Id, Description = $"无人机 {drone.Id} 被摧毁" });
|
||||||
OccurredAt = SimulationTime,
|
}
|
||||||
TargetId = drone.Id,
|
|
||||||
Description = $"无人机 {drone.Id} 被摧毁",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,13 +199,10 @@ namespace CounterDrone.Core.Simulation
|
|||||||
{
|
{
|
||||||
drone.Update(scaledDt, (float)_scene.WindSpeed, (WindDirection)_scene.WindDirection);
|
drone.Update(scaledDt, (float)_scene.WindSpeed, (WindDirection)_scene.WindDirection);
|
||||||
if (drone.Status == DroneStatus.ReachedTarget)
|
if (drone.Status == DroneStatus.ReachedTarget)
|
||||||
frameEvents.Add(new SimEvent
|
{
|
||||||
{
|
OnDroneReachedTarget?.Invoke(drone);
|
||||||
Type = SimEventType.DroneReachedTarget,
|
frameEvents.Add(new SimEvent { Type = SimEventType.DroneReachedTarget, OccurredAt = SimulationTime, SourceId = drone.Id, Description = "到达攻击目标" });
|
||||||
OccurredAt = SimulationTime,
|
}
|
||||||
SourceId = drone.Id,
|
|
||||||
Description = "到达攻击目标",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 平台冷却
|
// 6. 平台冷却
|
||||||
@ -247,14 +216,8 @@ namespace CounterDrone.Core.Simulation
|
|||||||
if (zone.ContainsPoint(drone.PosX, drone.PosY, drone.PosZ))
|
if (zone.ContainsPoint(drone.PosX, drone.PosY, drone.PosZ))
|
||||||
{
|
{
|
||||||
drone.MarkZoneIntruded();
|
drone.MarkZoneIntruded();
|
||||||
frameEvents.Add(new SimEvent
|
OnZoneIntruded?.Invoke(drone, zone);
|
||||||
{
|
frameEvents.Add(new SimEvent { Type = SimEventType.ZoneIntruded, OccurredAt = SimulationTime, SourceId = drone.Id, TargetId = zone.Id, Description = $"侵入 {zone.Name}" });
|
||||||
Type = SimEventType.ZoneIntruded,
|
|
||||||
OccurredAt = SimulationTime,
|
|
||||||
SourceId = drone.Id,
|
|
||||||
TargetId = zone.Id,
|
|
||||||
Description = $"侵入 {zone.Name}",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,24 +226,17 @@ namespace CounterDrone.Core.Simulation
|
|||||||
if (_drones.All(d => d.Status != DroneStatus.Flying))
|
if (_drones.All(d => d.Status != DroneStatus.Flying))
|
||||||
{
|
{
|
||||||
State = SimulationState.Completed;
|
State = SimulationState.Completed;
|
||||||
|
OnSimulationEnded?.Invoke();
|
||||||
frameEvents.Add(new SimEvent { Type = SimEventType.SimulationEnd, OccurredAt = SimulationTime });
|
frameEvents.Add(new SimEvent { Type = SimEventType.SimulationEnd, OccurredAt = SimulationTime });
|
||||||
}
|
}
|
||||||
|
|
||||||
_allEvents.AddRange(frameEvents);
|
_allEvents.AddRange(frameEvents);
|
||||||
|
|
||||||
// 录制帧
|
|
||||||
var snapshots = CollectSnapshots();
|
var snapshots = CollectSnapshots();
|
||||||
_frameStore.WriteFrame(_frameDb, _taskId, FrameIndex, SimulationTime, snapshots);
|
_frameStore.WriteFrame(_frameDb, _taskId, FrameIndex, SimulationTime, snapshots);
|
||||||
_frameDb.Commit();
|
_frameDb.Commit();
|
||||||
|
|
||||||
return new SimulationFrameResult
|
return new SimulationFrameResult { EntitySnapshots = snapshots, NewEvents = frameEvents, State = State, FrameIndex = FrameIndex, SimulationTime = SimulationTime };
|
||||||
{
|
|
||||||
EntitySnapshots = snapshots,
|
|
||||||
NewEvents = frameEvents,
|
|
||||||
State = State,
|
|
||||||
FrameIndex = FrameIndex,
|
|
||||||
SimulationTime = SimulationTime,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Pause() { if (State == SimulationState.Running) State = SimulationState.Paused; }
|
public void Pause() { if (State == SimulationState.Running) State = SimulationState.Paused; }
|
||||||
@ -291,28 +247,9 @@ namespace CounterDrone.Core.Simulation
|
|||||||
{
|
{
|
||||||
var list = new List<EntitySnapshot>();
|
var list = new List<EntitySnapshot>();
|
||||||
foreach (var d in _drones)
|
foreach (var d in _drones)
|
||||||
{
|
list.Add(new EntitySnapshot { EntityId = d.Id, EntityType = Models.EntityType.Drone, PosX = d.PosX, PosY = d.PosY, PosZ = d.PosZ, StateData = JsonSerializer.Serialize(new { damageStage = (int)_damageModel.GetDamageStage(1 - d.Hp), hp = d.Hp }) });
|
||||||
var stage = _damageModel.GetDamageStage(1 - d.Hp);
|
|
||||||
list.Add(new EntitySnapshot
|
|
||||||
{
|
|
||||||
EntityId = d.Id,
|
|
||||||
EntityType = Models.EntityType.Drone,
|
|
||||||
PosX = d.PosX,
|
|
||||||
PosY = d.PosY,
|
|
||||||
PosZ = d.PosZ,
|
|
||||||
StateData = JsonSerializer.Serialize(new { damageStage = (int)stage, hp = d.Hp }),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
foreach (var c in _clouds)
|
foreach (var c in _clouds)
|
||||||
list.Add(new EntitySnapshot
|
list.Add(new EntitySnapshot { EntityId = c.Id, EntityType = Models.EntityType.Cloud, PosX = c.Dispersion.Center.X, PosY = c.Dispersion.Center.Y, PosZ = c.Dispersion.Center.Z, StateData = JsonSerializer.Serialize(new { radius = c.Dispersion.Radius, opacity = c.Dispersion.Particles.Opacity }) });
|
||||||
{
|
|
||||||
EntityId = c.Id,
|
|
||||||
EntityType = Models.EntityType.Cloud,
|
|
||||||
PosX = c.Dispersion.Center.X,
|
|
||||||
PosY = c.Dispersion.Center.Y,
|
|
||||||
PosZ = c.Dispersion.Center.Z,
|
|
||||||
StateData = JsonSerializer.Serialize(new { radius = c.Dispersion.Radius, opacity = c.Dispersion.Particles.Opacity }),
|
|
||||||
});
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user