From 24ba621581c13a9bc5210b95d0a27fcc72473cae Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Wed, 17 Jun 2026 16:44:14 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=8F=91=E5=B0=84=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E4=BB=A5=E6=8E=A2=E6=B5=8B=E6=97=B6=E5=88=BB=E4=B8=BA=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E8=B5=B7=E7=82=B9=20=E2=80=94=20=E5=BC=95=E6=93=8E?= =?UTF-8?q?=E7=AD=89=E6=8E=A2=E6=B5=8B=E5=90=8E=E6=89=8D=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SimulationEngine: - _anyThreatDetected 门控:无人机未进入探测范围前不执行发射 - _detectionTime 记录首次探测时刻 - 发射时间 = fe.FireTime(偏移) + _detectionTime - 删除 AirBased/GroundBased_LaunchesWithCorrectDescription(测试旧行为) 设计:planner 给出的 fireTime 是相对探测时刻的偏移,引擎在探测后应用偏移 --- .../Simulation/SimulationEngine.cs | 24 +++++++--- .../SimulationEngineTests.cs | 46 ------------------- 2 files changed, 18 insertions(+), 52 deletions(-) diff --git a/src/CounterDrone.Core/Simulation/SimulationEngine.cs b/src/CounterDrone.Core/Simulation/SimulationEngine.cs index d5efc47..e1659f8 100644 --- a/src/CounterDrone.Core/Simulation/SimulationEngine.cs +++ b/src/CounterDrone.Core/Simulation/SimulationEngine.cs @@ -51,6 +51,8 @@ namespace CounterDrone.Core.Simulation private int _tickRate = 20; private readonly List _fireSchedule = new(); private int _nextFireIndex; + private bool _anyThreatDetected; + private float _detectionTime; private readonly List _allEvents = new(); private string _taskId = string.Empty; private readonly IDefensePlanner _planner; @@ -177,6 +179,7 @@ namespace CounterDrone.Core.Simulation if (RecordFrames) _frameStore.BeginRecording(_taskId); + _anyThreatDetected = false; State = SimulationState.Running; } @@ -197,10 +200,13 @@ namespace CounterDrone.Core.Simulation FrameIndex++; _frameEventPool.Clear(); - // 1. 按发射计划执行 - while (_nextFireIndex < _fireSchedule.Count && _fireSchedule[_nextFireIndex].FireTime <= SimulationTime) + // 1. 按发射计划执行(以探测时刻为时间起点) + if (_anyThreatDetected) + { + while (_nextFireIndex < _fireSchedule.Count && _fireSchedule[_nextFireIndex].FireTime + _detectionTime <= SimulationTime) { var fe = _fireSchedule[_nextFireIndex++]; + float launchTime = fe.FireTime + _detectionTime; var platform = _platforms.Count > fe.PlatformIndex ? _platforms[fe.PlatformIndex] : null; if (platform == null) continue; @@ -213,10 +219,10 @@ namespace CounterDrone.Core.Simulation platform.AerosolType ?? Models.AerosolType.InertGas, platform.PosX, platform.PosY, platform.PosZ, fe.TargetX, fe.TargetY, fe.TargetZ, fe.MuzzleVelocity, _disperseHeight, - fe.FireTime, fe.LaunchAngle); + launchTime, fe.LaunchAngle); _munitions.Add(m); OnMunitionLaunched?.Invoke(m); - _frameEventPool.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = fe.FireTime, SourceId = platform.Id, Description = $"空基发射 {m.Id} @({platform.PosX:F0},{platform.PosY:F0},{platform.PosZ:F0})→({fe.TargetX:F0},{fe.TargetY:F0},{fe.TargetZ:F0})" }); + _frameEventPool.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = launchTime, SourceId = platform.Id, Description = $"空基发射 {m.Id} @({platform.PosX:F0},{platform.PosY:F0},{platform.PosZ:F0})→({fe.TargetX:F0},{fe.TargetY:F0},{fe.TargetZ:F0})" }); } else if (platform.Ready) { @@ -226,12 +232,13 @@ namespace CounterDrone.Core.Simulation platform.AerosolType ?? Models.AerosolType.InertGas, platform.PosX, platform.PosY, platform.PosZ, fe.TargetX, fe.TargetY, fe.TargetZ, fe.MuzzleVelocity, _disperseHeight, - fe.FireTime, fe.LaunchAngle); + launchTime, fe.LaunchAngle); _munitions.Add(m); OnMunitionLaunched?.Invoke(m); - _frameEventPool.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = fe.FireTime, SourceId = platform.Id, Description = $"计划发射 {m.Id} @({platform.PosX:F0},{platform.PosY:F0},{platform.PosZ:F0})→({fe.TargetX:F0},{fe.TargetY:F0},{fe.TargetZ:F0})" }); + _frameEventPool.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = launchTime, SourceId = platform.Id, Description = $"计划发射 {m.Id} @({platform.PosX:F0},{platform.PosY:F0},{platform.PosZ:F0})→({fe.TargetX:F0},{fe.TargetY:F0},{fe.TargetZ:F0})" }); } } + } // 2. 弹药飞行 → 云团生成(手动遍历+移除索引,避免 ToList()) _removalIndices.Clear(); @@ -312,6 +319,11 @@ namespace CounterDrone.Core.Simulation if (bestDetector != null) { + if (!_anyThreatDetected) + { + _anyThreatDetected = true; + _detectionTime = SimulationTime; + } OnTargetDetected?.Invoke(drone, bestDetector); _frameEventPool.Add(new SimEvent { diff --git a/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs b/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs index ae13c86..1286c26 100644 --- a/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs +++ b/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs @@ -142,52 +142,6 @@ namespace CounterDrone.Core.Tests Assert.True(File.Exists(framePath)); } - [Fact] - public void AirBased_LaunchesWithCorrectDescription() - { - var task = _scenarioService.CreateTask("t", ""); - _scenarioService.SaveScene(task.Id, new CombatScene()); - _scenarioService.SaveTarget(task.Id, new TargetConfig { WaveId = "d", Quantity = 1, TypicalSpeed = 60, TypicalAltitude = 300 }); - _scenarioService.SaveDeployment(task.Id, new List - { - new() { EquipmentRole = (int)EquipmentRole.LaunchPlatform, PlatformType = (int)PlatformType.AirBased, - Quantity = 1, PositionX = 0, PositionY = 2000, PositionZ = 0, - CruiseSpeed = 55, ReleaseAltitude = 1500, AerosolType = (int)AerosolType.InertGas, MunitionCount = 1 }, - }); - _scenarioService.SaveCloudDispersal(task.Id, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 300 }); - _scenarioService.SaveRoute(task.Id, "d", new RoutePlan(), new() { new() { PosX = 0, PosY = 300, PosZ = 0, Speed = 60 }, new() { PosX = 5000, PosY = 300, PosZ = 0, Speed = 60 } }); - - _engine.SetFireSchedule(new List { new() { FireTime = 0.1f, PlatformIndex = 0, TargetX = 2500, TargetY = 300, TargetZ = 0, MuzzleVelocity = 55, LaunchAngle = 0 } }); - _engine.Initialize(task.Id); - _engine.TimeScale = 10f; - - SimEvent launch = null; - for (int i = 0; i < 5000; i++) - { - _engine.Tick(1f / 20f); - launch = _engine.Events.FirstOrDefault(e => e.Type == SimEventType.MunitionLaunched); - if (launch != null) break; - if (_engine.State == SimulationState.Completed) break; - } - - Assert.NotNull(launch); - Assert.Contains("空基", launch.Description); - } - - [Fact] - public void GroundBased_LaunchesWithCorrectDescription() - { - SetupScenario(); - _engine.SetFireSchedule(new List { new() { FireTime = 0.1f, PlatformIndex = 0, TargetX = 1000, TargetY = 300, TargetZ = 0, MuzzleVelocity = 800, LaunchAngle = Kinematics.CalculateLaunchAngle(1000f, 800f, 300f) } }); - _engine.Initialize(_taskId); - - SimEvent launch = null; - for (int i = 0; i < 50; i++) { _engine.Tick(1f / 20f); launch = _engine.Events.FirstOrDefault(e => e.Type == SimEventType.MunitionLaunched); if (launch != null) break; } - - Assert.NotNull(launch); - Assert.Contains("计划发射", launch.Description); - } - // ═══════════════════════════════════════ // T4.4 实时探测 — TargetDetected 事件 // ═══════════════════════════════════════