fix: 空基 FireEvent.MuzzleVelocity=0 + 空基弹道单元测试

- GenerateFireEventsAt: 空基时 MuzzleVelocity=0(而非800)
- 新增 AirBased_FireTime_EarlierThanArrival 验证提前量
- 新增 AirBased_FireTime_EarlierThanGroundBased 验证空基更早
- 新增 AirBased_NoMuzzleVelocityInFireEvent 验证 MuzzleVelocity=0
- 空基弹道计算本身正确(recommendedTiming - flightTime - fallTime)
This commit is contained in:
tian 2026-06-13 11:05:34 +08:00
parent c610573e34
commit 7fa0268463
2 changed files with 64 additions and 2 deletions

View File

@ -438,7 +438,7 @@ namespace CounterDrone.Core.Algorithms
TargetX = tx,
TargetY = mid.Y,
TargetZ = tz,
MuzzleVelocity = unit.MuzzleVelocity,
MuzzleVelocity = unit.Type == PlatformType.AirBased ? 0f : unit.MuzzleVelocity,
});
return events;

View File

@ -234,7 +234,7 @@ namespace CounterDrone.Core.Tests
}
// ═══════════════════════════════════════
// 空基
// 空基弹道
// ═══════════════════════════════════════
[Fact]
@ -251,6 +251,68 @@ namespace CounterDrone.Core.Tests
Assert.True(result.Best.ThreatsEngaged > 0);
}
[Fact]
public void AirBased_FireTime_EarlierThanArrival()
{
// 空基需要飞行+下落时间,发射应远早于到达时间
var units = new List<FireUnit>
{
new FireUnit { Id = "u0", Type = PlatformType.AirBased,
Position = new Vector3(3000, 2500, -2000), GunCount = 4, ChannelsPerGun = 4,
CruiseSpeed = 55, ReleaseAltitude = 1500, TotalMunitions = 16,
AmmoTypes = new() { AerosolType.InertGas } },
};
var threat = MakeThreat(PowerType.Piston, 120);
var result = new DefaultDefensePlanner(TestAmmo).Plan(units, new() { threat }, new CombatScene());
float arrivalTime = threat.GetArrivalTime();
// 空基至少需要 (飞行距离/55 + 下落时间) 的提前量
float minAdvance = 20f; // 至少提前 20 秒
Assert.All(result.Best.MergedSchedule, fe =>
Assert.True(fe.FireTime < arrivalTime - minAdvance,
$"空基FireTime={fe.FireTime:F1}, arrival={arrivalTime:F1}, 应至少提前{minAdvance}s"));
}
[Fact]
public void AirBased_FireTime_EarlierThanGroundBased()
{
// 同样威胁,空基应比地基更早发射(需要飞行时间)
var threat = MakeThreat(PowerType.Piston, 120);
var airUnits = new List<FireUnit>
{
new FireUnit { Id = "u0", Type = PlatformType.AirBased,
Position = new Vector3(3000, 2500, -2000), GunCount = 4, ChannelsPerGun = 4,
CruiseSpeed = 55, ReleaseAltitude = 1500, TotalMunitions = 16,
AmmoTypes = new() { AerosolType.InertGas } },
};
var groundUnits = MakeGroundUnits(1);
var airResult = new DefaultDefensePlanner(TestAmmo).Plan(airUnits, new() { threat }, new CombatScene());
var groundResult = new DefaultDefensePlanner(TestAmmo).Plan(groundUnits, new() { threat }, new CombatScene());
float airFirst = airResult.Best.MergedSchedule.Min(e => e.FireTime);
float groundFirst = groundResult.Best.MergedSchedule.Min(e => e.FireTime);
Assert.True(airFirst < groundFirst,
$"空基首发射={airFirst:F1}s, 地基首发射={groundFirst:F1}s, 空基应更早");
}
[Fact]
public void AirBased_NoMuzzleVelocityInFireEvent()
{
// 空基 FireEvent 的 MuzzleVelocity 应为 0不适用
var units = new List<FireUnit>
{
new FireUnit { Id = "u0", Type = PlatformType.AirBased,
Position = new Vector3(3000, 2500, -2000), GunCount = 4, ChannelsPerGun = 4,
CruiseSpeed = 55, ReleaseAltitude = 1500, TotalMunitions = 16,
AmmoTypes = new() { AerosolType.InertGas } },
};
var threat = MakeThreat(PowerType.Piston, 120);
var result = new DefaultDefensePlanner(TestAmmo).Plan(units, new() { threat }, new CombatScene());
Assert.All(result.Best.MergedSchedule, fe =>
Assert.True(fe.MuzzleVelocity < 10f,
$"空基 MuzzleVelocity={fe.MuzzleVelocity}, 应≈0"));
}
// ═══════════════════════════════════════
// 集成验证
// ═══════════════════════════════════════