refactor: code cleanup - warnings, dead code, naming

Warnings (9 to 0): events declared nullable, _cache nullable, IDamageModel param nullable.

Dead code removed: DefaultFormations class + FormationTemplate; AlgorithmTypes legacy types (ThreatProfile/DefenseRecommendation/DefenseSolution/MultiGroupRecommendation/RecommendedPlatform/RecommendedDetection); DroneEntity CurrentWaypointIndex/FormationOffsetX/Y/ApplyFormationOffset/wind params; Vector3 operators+Length+DistanceTo; CloudExpansionModel Phase2Duration/TimeToDensity; Kinematics EstimatedShellTime/GaussianConcentration; InterceptCandidate write-only fields (CoverageDuration/FlightTime/ShellFlightTime); ParticleParams EmitRate/ColorHex.

Naming: DefaultDefensePlanner -> DefensePlanner (only impl of IDefensePlanner); inCloudTime -> inCloudDistance (returns meters not seconds); DamageAssessment class summary fixed; wind comments clarified (drone wind removed vs cloud wind active).

Tests: 191 pass, 0 warnings.
This commit is contained in:
tian 2026-06-14 15:52:05 +08:00
parent 8515f7821a
commit 3b8fb4ee86
18 changed files with 38 additions and 215 deletions

View File

@ -3,53 +3,6 @@ using CounterDrone.Core.Models;
namespace CounterDrone.Core.Algorithms
{
/// <summary>威胁画像</summary>
public class ThreatProfile
{
public CombatScene Environment { get; set; } = new();
public List<TargetConfig> Targets { get; set; } = new();
public RoutePlan Route { get; set; } = new();
public List<Waypoint> Waypoints { get; set; } = new();
public List<ControlZone> ControlZones { get; set; } = new();
/// <summary>偏好平台类型null = 算法自行决定,当前默认地基)</summary>
public PlatformType? PreferredPlatformType { get; set; }
}
/// <summary>防御推荐方案</summary>
public class DefenseRecommendation
{
public DefenseSolution Best { get; set; } = new();
public DefenseSolution Critical { get; set; } = new();
}
/// <summary>单套防御方案</summary>
public class DefenseSolution
{
public AerosolType RecommendedAerosolType { get; set; }
public string AerosolRationale { get; set; } = string.Empty;
public CloudDispersal RecommendedCloud { get; set; } = new();
public List<RecommendedPlatform> Platforms { get; set; } = new();
public List<RecommendedDetection> Detections { get; set; } = new();
public float InterceptProbability { get; set; }
public string SummaryRationale { get; set; } = string.Empty;
public List<FireEvent> FireSchedule { get; set; } = new();
/// <summary>此方案占用的平台起始索引(多编队合并用)</summary>
public int PlatformOffset { get; set; }
}
/// <summary>多编队推荐结果</summary>
public class MultiGroupRecommendation
{
/// <summary>每组的独立方案</summary>
public List<DefenseSolution> GroupSolutions { get; set; } = new();
/// <summary>合并后的总发射计划</summary>
public List<FireEvent> MergedFireSchedule { get; set; } = new();
}
/// <summary>火力单元(完整的武器系统)</summary>
public class FireUnit
{
@ -121,47 +74,17 @@ namespace CounterDrone.Core.Algorithms
}
}
public class RecommendedPlatform
{
public PlatformType Type { get; set; }
public Vector3 Position { get; set; }
public int Quantity { get; set; }
public int MunitionCount { get; set; }
public float CoverageVolume { get; set; }
public float Cooldown { get; set; } = 5f;
public float MuzzleVelocity { get; set; } = 800f;
/// <summary>空基平台巡航速度 m/s</summary>
public float CruiseSpeed { get; set; } = 55f;
/// <summary>空基平台投放高度 m</summary>
public float ReleaseAltitude { get; set; }
}
public class RecommendedDetection
{
public Vector3 Position { get; set; }
public float DetectionRadius { get; set; }
public int Quantity { get; set; }
}
/// <summary>三维向量(纯 C#,不依赖 UnityEngine</summary>
public struct Vector3
{
public float X, Y, Z;
public Vector3(float x, float y, float z) { X = x; Y = y; Z = z; }
public static Vector3 operator +(Vector3 a, Vector3 b) => new(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
public static Vector3 operator -(Vector3 a, Vector3 b) => new(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
public static Vector3 operator *(Vector3 a, float s) => new(a.X * s, a.Y * s, a.Z * s);
public float Length => (float)System.Math.Sqrt(X * X + Y * Y + Z * Z);
public float DistanceTo(Vector3 other) => (this - other).Length;
}
/// <summary>粒子渲染参数</summary>
public class ParticleParams
{
public float EmitRate { get; set; } = 100;
public float Opacity { get; set; } = 1.0f;
public string ColorHex { get; set; } = "#FFFFFF";
public float SizeMultiplier { get; set; } = 1.0f;
}
@ -184,10 +107,7 @@ namespace CounterDrone.Core.Algorithms
public FireUnit Unit { get; set; } = null!;
public AerosolType AmmoType { get; set; }
public float EarliestInterceptTime { get; set; }
public float CoverageDuration { get; set; }
public float KillProbability { get; set; }
public float FlightTime { get; set; } // 弹药/平台飞行时间
public float ShellFlightTime { get; set; } // 仅地基用
}
/// <summary>单元分配:一个火力单元被分配给一个威胁的配置</summary>

View File

@ -20,8 +20,6 @@ namespace CounterDrone.Core.Algorithms
/// <summary>Phase 2 结束时的有效半径30s 湍流膨胀)</summary>
public float TurbulentRadius => RadiusAt(30f);
/// <summary>Phase 2 边界时间</summary>
public float Phase2Duration => 30f;
/// <summary>湍流膨胀系数 k</summary>
public float TurbulentExpansionK => (float)_ammo.TurbulentExpansionK;
@ -55,17 +53,6 @@ namespace CounterDrone.Core.Algorithms
return volume > 0.001f ? (float)_ammo.SourceStrength / volume : (float)_ammo.CoreDensity;
}
/// <summary>达到指定浓度所需时间(密度随膨胀下降,较晚时间密度更低)</summary>
/// <returns>浓度首次低于threshold的时间(s)如果不会低于则返回MaxDuration</returns>
public float TimeToDensity(float threshold)
{
// 密度 ≈ SourceStrength / (4/3 π R³)随R增大而下降
// 求R使得密度=threshold: R³ = SourceStrength / (threshold * 4π/3)
float src = (float)_ammo.SourceStrength;
float volume = src / Math.Max(threshold, 0.000001f);
float r = (float)Math.Pow(volume * 3f / (4f * (float)Math.PI), 1f / 3f);
return TimeToReach(r);
}
/// <summary>覆盖指定距离需要的云团数量</summary>
/// <param name="requiredCoverage">需要覆盖的距离 (m)</param>

View File

@ -3,7 +3,7 @@ using CounterDrone.Core.Models;
namespace CounterDrone.Core.Algorithms
{
/// <summary>毁伤评估——路径积分计算无人机穿过云团的暴露时间</summary>
/// <summary>毁伤评估——路径积分计算无人机穿过云团的路径长度(米),用于换算暴露时间</summary>
public static class DamageAssessment
{
/// <summary>线段 (p1→p2) 在球体内的长度,换算为路径长度 (m)</summary>

View File

@ -5,15 +5,15 @@ using CounterDrone.Core.Models;
namespace CounterDrone.Core.Algorithms
{
/// <summary>默认防御规划器 — 五步流水线,全部调用与引擎共享的物理工具类</summary>
public class DefaultDefensePlanner : IDefensePlanner
/// <summary>防御规划器 — 五步流水线,全部调用与引擎共享的物理工具类</summary>
public class DefensePlanner : IDefensePlanner
{
private readonly List<AmmunitionSpec> _ammoCatalog;
private readonly IDamageModel _damageModel;
private readonly PlannerConfig _config;
public DefaultDefensePlanner(List<AmmunitionSpec> ammoCatalog, PlannerConfig config,
IDamageModel damageModel = null)
public DefensePlanner(List<AmmunitionSpec> ammoCatalog, PlannerConfig config,
IDamageModel? damageModel = null)
{
_ammoCatalog = ammoCatalog ?? throw new ArgumentNullException(nameof(ammoCatalog));
_config = config ?? throw new ArgumentNullException(nameof(config));
@ -290,10 +290,7 @@ namespace CounterDrone.Core.Algorithms
Unit = unit,
AmmoType = ammoType,
EarliestInterceptTime = interceptTime,
CoverageDuration = effectiveR * 2f,
KillProbability = prob,
FlightTime = shellTime,
ShellFlightTime = shellTime,
};
}
@ -324,10 +321,7 @@ namespace CounterDrone.Core.Algorithms
Unit = unit,
AmmoType = ammoType,
EarliestInterceptTime = interceptTime,
CoverageDuration = effectiveR * 2f,
KillProbability = prob,
FlightTime = totalTime,
ShellFlightTime = fallTime,
};
}

View File

@ -55,12 +55,6 @@ namespace CounterDrone.Core.Algorithms
return range / (muzzleVelocity * cosAngle);
}
/// <summary>炮弹飞行时间:抛物线轨迹到目标点的时间(自动求解发射角,下行段命中)</summary>
public static float EstimatedShellTime(float horizontalDist, float muzzleVelocity)
{
return ParabolicShellTime(horizontalDist, 0f, muzzleVelocity);
}
/// <summary>抛物线飞行时间:解 tan(θ) 二次方程求到达 (horizontalDist, heightDiff) 的时间</summary>
public static float ParabolicShellTime(float horizontalDist, float heightDiff, float muzzleVelocity)
{
@ -232,16 +226,6 @@ namespace CounterDrone.Core.Algorithms
var denom = (float)Math.Pow(2f * (float)Math.PI, 1.5f) * sigmaY * sigmaY * sigmaZ;
return denom > 0.001f ? sourceStrength / denom : 0f;
}
/// <summary>高斯烟团某点浓度 C(x,y,z) — 简化:相对中心的偏移</summary>
public static float GaussianConcentration(float q, float sx, float sy, float sz, float offsetY, float offsetZ)
{
var norm = q / ((float)Math.Pow(2f * (float)Math.PI, 1.5f) * sx * sy * sz);
var ey = (float)Math.Exp(-0.5f * offsetY * offsetY / (sy * sy));
var ez = (float)Math.Exp(-0.5f * offsetZ * offsetZ / (sz * sz));
var ezr = (float)Math.Exp(-0.5f * offsetZ * offsetZ / (sz * sz));
return norm * ey * (ez + ezr);
}
public static bool PointInPolygon(float px, float pz, ReadOnlySpan<(float X, float Z)> vertices)
{
if (vertices.Length < 3) return false;

View File

@ -4,7 +4,7 @@ namespace CounterDrone.Core
{
public static class DefaultFireUnits
{
private static List<FireUnitTemplate> _cache;
private static List<FireUnitTemplate>? _cache;
public static List<FireUnitTemplate> GetAll()
{

View File

@ -1,42 +0,0 @@
using System.Collections.Generic;
namespace CounterDrone.Core
{
public static class DefaultFormations
{
private static List<FormationTemplate> _cache;
public static List<FormationTemplate> GetAll()
{
if (_cache != null) return _cache;
_cache = new List<FormationTemplate>
{
new() { Id = "single", Name = "单机", FormationMode = 0, LateralCount = 1, LongitudinalCount = 1 },
new() { Id = "line-abreast-3", Name = "3机横队", FormationMode = 1, LateralCount = 3, LongitudinalCount = 1,
LateralSpacing = 50 },
new() { Id = "line-abreast-5", Name = "5机横队", FormationMode = 1, LateralCount = 5, LongitudinalCount = 1,
LateralSpacing = 50 },
new() { Id = "column-3", Name = "3机纵队", FormationMode = 1, LateralCount = 1, LongitudinalCount = 3,
LongitudinalSpacing = 100 },
new() { Id = "box-2x2", Name = "2×2方队", FormationMode = 1, LateralCount = 2, LongitudinalCount = 2,
LateralSpacing = 50, LongitudinalSpacing = 100 },
new() { Id = "swarm-10", Name = "蜂群(10架)", FormationMode = 2, LateralCount = 10, LongitudinalCount = 1,
LateralSpacing = 30 },
};
return _cache;
}
public static FormationTemplate GetById(string id) => GetAll().Find(t => t.Id == id);
}
public class FormationTemplate
{
public string Id { get; set; } = "";
public string Name { get; set; } = "";
public int FormationMode { get; set; }
public int LateralCount { get; set; } = 1;
public int LongitudinalCount { get; set; } = 1;
public double LateralSpacing { get; set; }
public double LongitudinalSpacing { get; set; }
}
}

View File

@ -14,7 +14,6 @@ namespace CounterDrone.Core.Simulation
public float Wingspan { get; }
public float TypicalSpeed { get; }
public List<Waypoint> Route { get; }
public int CurrentWaypointIndex { get; private set; }
public float PosX { get; private set; }
public float PosY { get; private set; }
public float PosZ { get; private set; }
@ -25,8 +24,6 @@ namespace CounterDrone.Core.Simulation
public float PrevX { get; private set; }
public float PrevY { get; private set; }
public float PrevZ { get; private set; }
public float FormationOffsetX { get; set; }
public float FormationOffsetY { get; set; }
/// <summary>沿航路已飞行弧长(米),由 RouteGeometry 驱动</summary>
private float _traveledArc;
@ -79,36 +76,19 @@ namespace CounterDrone.Core.Simulation
}
}
private void ApplyFormationOffset(int latIdx, float latSpacing, int longIdx, float longSpacing, FormationMode mode)
{
if (latIdx == 0 && longIdx == 0) return;
switch (mode)
{
case FormationMode.Formation:
FormationOffsetY = latIdx * latSpacing;
FormationOffsetX = -longIdx * longSpacing;
break;
case FormationMode.Swarm:
var rng = new Random((latIdx + longIdx * 100) * 137);
FormationOffsetX = (float)(rng.NextDouble() - 0.5) * latSpacing * 3;
FormationOffsetY = (float)(rng.NextDouble() - 0.5) * latSpacing * 3;
break;
}
}
public void SavePreviousPosition()
{
PrevX = PosX; PrevY = PosY; PrevZ = PosZ;
}
public void Update(float deltaTime, float windSpeed, WindDirection windDir)
public void Update(float deltaTime)
{
if (Status != DroneStatus.Flying) return;
if (Route.Count == 0) return;
// 运动模型统一在 RouteGeometry弧长推进 + 位置查询。
// 无人机严格沿航路匀速飞行,不受风偏影响(风偏已移除,见风偏补偿说明)。
// windSpeed/windDir 保留签名供未来飞控模型使用,当前不参与运动
// 无人机严格按航路匀速飞行。无人机不受风偏影响(真实无人机有飞控修正航向),
// 但云团受风偏影响(见 SimulationEngine 毁伤判定的云团参考系修正)。
float totalLen = RouteGeometry.TotalLength(Route);
if (totalLen <= 0f)
{

View File

@ -28,12 +28,12 @@ namespace CounterDrone.Core.Simulation
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;
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<PlatformEntity> _platforms = new();
@ -237,7 +237,7 @@ namespace CounterDrone.Core.Simulation
// 5. 无人机移动
foreach (var drone in _drones.Where(d => d.Status == DroneStatus.Flying))
{
drone.Update(scaledDt, (float)_scene.WindSpeed, (WindDirection)_scene.WindDirection);
drone.Update(scaledDt);
if (drone.Status == DroneStatus.ReachedTarget)
{
OnDroneReachedTarget?.Invoke(drone);
@ -257,14 +257,14 @@ namespace CounterDrone.Core.Simulation
{
float ccx = cloud.Dispersion.Center.X, ccy = cloud.Dispersion.Center.Y, ccz = cloud.Dispersion.Center.Z;
// 无人机线段换算到云团参考系:相对位置 = drone - cloud
float inCloudTime = DamageAssessment.PathInSphere(
float inCloudDistance = DamageAssessment.PathInSphere(
drone.PrevX - (ccx - cloudDx), drone.PrevY - (ccy - cloudDy), drone.PrevZ - (ccz - cloudDz),
drone.PosX - ccx, drone.PosY - ccy, drone.PosZ - ccz,
0f, 0f, 0f,
cloud.Dispersion.EffectiveRadius);
if (inCloudTime > 0 && cloud.Dispersion.CoreDensity >= (float)_ammoSpec.EffectiveConcentration)
if (inCloudDistance > 0 && cloud.Dispersion.CoreDensity >= (float)_ammoSpec.EffectiveConcentration)
{
float exposureIncrement = inCloudTime / (drone.TypicalSpeed / 3.6f);
float exposureIncrement = inCloudDistance / (drone.TypicalSpeed / 3.6f);
drone.ExposureTime += exposureIncrement;
var dmg = _damageModel.CalculateDamage(drone.TargetType, drone.PowerType, cloud.AerosolType, cloud.Dispersion.CoreDensity, drone.ExposureTime, exposureIncrement);
drone.ApplyDamage(dmg);

View File

@ -72,7 +72,7 @@ namespace CounterDrone.Unity
TotalMunitions = 3,
AmmoTypes = new() { AerosolType.InertGas, AerosolType.ActiveMaterial, AerosolType.ActiveFuel },
});
var result = new DefaultDefensePlanner(ammoCatalog, PlannerConfig.Load(paths)).Plan(fireUnits, new List<DroneGroup> { droneGroup }, detail.Scene);
var result = new DefensePlanner(ammoCatalog, PlannerConfig.Load(paths)).Plan(fireUnits, new List<DroneGroup> { droneGroup }, detail.Scene);
scenarioMgr.SaveCloud(taskId, new CloudDispersal
{
PositionX = (droneGroup.Waypoints[0].PosX + droneGroup.Waypoints[^1].PosX) / 2,

View File

@ -67,7 +67,7 @@ namespace CounterDrone.Unity
// 推荐方案
var detail = scenario.GetTaskDetail(taskId);
var ammoCatalog = db.Table<AmmunitionSpec>().ToList();
var planner = new DefaultDefensePlanner(ammoCatalog, PlannerConfig.Load(paths));
var planner = new DefensePlanner(ammoCatalog, PlannerConfig.Load(paths));
var droneGroup = new DroneGroup
{
GroupId = "default",

View File

@ -48,7 +48,7 @@ namespace CounterDrone.Unity
new RoutePlanRepository(_db), new WaypointRepository(_db));
_frameStore = new FrameDataStore(_paths);
_engine = new SimulationEngine(_scenario, _frameStore, new DamageModelRouter(), _paths, new DefaultDefensePlanner(DefaultAmmunition.GetAll(), PlannerConfig.Load(_paths)));
_engine = new SimulationEngine(_scenario, _frameStore, new DamageModelRouter(), _paths, new DefensePlanner(DefaultAmmunition.GetAll(), PlannerConfig.Load(_paths)));
}
public void LoadAndStart(string taskId)

View File

@ -59,7 +59,7 @@ namespace CounterDrone.Core.Tests
private PlannerResult Plan(List<FireUnit> units, DroneGroup threat,
CombatScene? env = null)
=> new DefaultDefensePlanner(TestAmmo, TestPlannerConfig.Instance).Plan(units, new() { threat }, env ?? new CombatScene());
=> new DefensePlanner(TestAmmo, TestPlannerConfig.Instance).Plan(units, new() { threat }, env ?? new CombatScene());
// ═══════════════════════════════════════
// 威胁排序
@ -233,7 +233,7 @@ namespace CounterDrone.Core.Tests
[Fact]
public void Allocation_MultiUnit_PoolsChannels()
{
var result = new DefaultDefensePlanner(TestAmmo, TestPlannerConfig.Instance).Plan(
var result = new DefensePlanner(TestAmmo, TestPlannerConfig.Instance).Plan(
new() { MakeGroundUnit("u0", 5000), MakeGroundUnit("u1", 5100) },
new() { MakeThreat(speed: 200) }, new CombatScene());
Assert.True(result.Best.ThreatsEngaged == 1);
@ -259,8 +259,8 @@ namespace CounterDrone.Core.Tests
// 边界
// ═══════════════════════════════════════
[Fact] public void Edge_NoUnits_Unengaged() => Assert.Equal(1, new DefaultDefensePlanner(TestAmmo, TestPlannerConfig.Instance).Plan(new(), new() { MakeThreat() }, new CombatScene()).Best.ThreatsUnengaged);
[Fact] public void Edge_NoThreats_Empty() => Assert.Equal(0, new DefaultDefensePlanner(TestAmmo, TestPlannerConfig.Instance).Plan(new() { MakeGroundUnit("u0", 5000) }, new(), new CombatScene()).Best.ThreatsEngaged);
[Fact] public void Edge_NoUnits_Unengaged() => Assert.Equal(1, new DefensePlanner(TestAmmo, TestPlannerConfig.Instance).Plan(new(), new() { MakeThreat() }, new CombatScene()).Best.ThreatsUnengaged);
[Fact] public void Edge_NoThreats_Empty() => Assert.Equal(0, new DefensePlanner(TestAmmo, TestPlannerConfig.Instance).Plan(new() { MakeGroundUnit("u0", 5000) }, new(), new CombatScene()).Best.ThreatsEngaged);
[Fact]
public void Edge_OutOfRange_Ground()
@ -293,7 +293,7 @@ namespace CounterDrone.Core.Tests
{
var a = MakeThreat(PowerType.Piston, 120); a.GroupId = "g0";
var b = MakeThreat(PowerType.Jet, 300); b.GroupId = "g1";
var result = new DefaultDefensePlanner(TestAmmo, TestPlannerConfig.Instance).Plan(
var result = new DefensePlanner(TestAmmo, TestPlannerConfig.Instance).Plan(
new() { MakeGroundUnit("u0", 5000), MakeGroundUnit("u1", 5100) },
new() { a, b }, new CombatScene());
Assert.Equal(2, result.Best.ThreatsEngaged);

View File

@ -44,7 +44,7 @@ namespace CounterDrone.Core.Tests
var drone = new DroneEntity("d1", "g1", CreateConfig(),
CreateRoute(0, 1000, 300, 120), 0, 50, 0, 0, FormationMode.Single);
float prevX = drone.PosX;
drone.Update(1f, 0, WindDirection.N);
drone.Update(1f);
Assert.True(drone.PosX > prevX);
}
@ -53,7 +53,7 @@ namespace CounterDrone.Core.Tests
{
var drone = new DroneEntity("d1", "g1", CreateConfig(360),
CreateRoute(0, 100, 300, 360), 0, 50, 0, 0, FormationMode.Single);
drone.Update(2f, 0, WindDirection.N);
drone.Update(2f);
Assert.Equal(DroneStatus.ReachedTarget, drone.Status);
}
@ -140,25 +140,25 @@ namespace CounterDrone.Core.Tests
0, 0, 0, 0, FormationMode.Single);
// 推进到第一段中点(弧长 50应在 X=50, Z=0
drone.Update(50f / (60f / 3.6f), 0, WindDirection.N);
drone.Update(50f / (60f / 3.6f));
Assert.Equal(50f, drone.PosX, 1);
Assert.Equal(0f, drone.PosZ, 1);
Assert.Equal(DroneStatus.Flying, drone.Status);
// 推进到拐点(弧长 100应在 X=100, Z=0
drone.Update(50f / (60f / 3.6f), 0, WindDirection.N);
drone.Update(50f / (60f / 3.6f));
Assert.Equal(100f, drone.PosX, 1);
Assert.Equal(0f, drone.PosZ, 1);
Assert.Equal(DroneStatus.Flying, drone.Status);
// 推进到第二段中点(弧长 150应在 X=100, Z=50
drone.Update(50f / (60f / 3.6f), 0, WindDirection.N);
drone.Update(50f / (60f / 3.6f));
Assert.Equal(100f, drone.PosX, 1);
Assert.Equal(50f, drone.PosZ, 1);
Assert.Equal(DroneStatus.Flying, drone.Status);
// 推进到终点(弧长 200应在 X=100, Z=100状态 ReachedTarget
drone.Update(50f / (60f / 3.6f), 0, WindDirection.N);
drone.Update(50f / (60f / 3.6f));
Assert.Equal(100f, drone.PosX, 1);
Assert.Equal(100f, drone.PosZ, 1);
Assert.Equal(DroneStatus.ReachedTarget, drone.Status);

View File

@ -65,7 +65,7 @@ namespace CounterDrone.Core.Tests
});
var engine = new SimulationEngine(_scenario, new FrameDataStore(new TestPathProvider(_testDir)),
new DamageModelRouter(), new TestPathProvider(_testDir), new DefaultDefensePlanner(DefaultAmmunition.GetAll(), TestPlannerConfig.Instance));
new DamageModelRouter(), new TestPathProvider(_testDir), new DefensePlanner(DefaultAmmunition.GetAll(), TestPlannerConfig.Instance));
engine.Initialize(_taskId);
engine.TimeScale = 4f;
@ -107,7 +107,7 @@ namespace CounterDrone.Core.Tests
});
var engine = new SimulationEngine(_scenario, new FrameDataStore(new TestPathProvider(_testDir)),
new DamageModelRouter(), new TestPathProvider(_testDir), new DefaultDefensePlanner(DefaultAmmunition.GetAll(), TestPlannerConfig.Instance));
new DamageModelRouter(), new TestPathProvider(_testDir), new DefensePlanner(DefaultAmmunition.GetAll(), TestPlannerConfig.Instance));
engine.Initialize(_taskId);
engine.TimeScale = 4f;
@ -148,7 +148,7 @@ namespace CounterDrone.Core.Tests
});
var engine = new SimulationEngine(_scenario, new FrameDataStore(new TestPathProvider(_testDir)),
new DamageModelRouter(), new TestPathProvider(_testDir), new DefaultDefensePlanner(DefaultAmmunition.GetAll(), TestPlannerConfig.Instance));
new DamageModelRouter(), new TestPathProvider(_testDir), new DefensePlanner(DefaultAmmunition.GetAll(), TestPlannerConfig.Instance));
engine.Initialize(_taskId);
engine.TimeScale = 4f;

View File

@ -49,7 +49,7 @@ namespace CounterDrone.Core.Tests
private SimulationEngine RunSimulation(int maxTicks, float tickDt = 1f / 20f, float timeScale = 8f)
{
var engine = new SimulationEngine(_scenario, _frameStore, new DamageModelRouter(), _paths, new DefaultDefensePlanner(_ammoCatalog, TestPlannerConfig.Instance));
var engine = new SimulationEngine(_scenario, _frameStore, new DamageModelRouter(), _paths, new DefensePlanner(_ammoCatalog, TestPlannerConfig.Instance));
engine.Initialize(_taskId);
engine.TimeScale = timeScale; // 8倍速加速

View File

@ -36,7 +36,7 @@ namespace CounterDrone.Core.Tests
new RoutePlanRepository(_mainDb), new WaypointRepository(_mainDb));
_engine = new SimulationEngine(_scenarioService, new FrameDataStore(paths),
new DamageModelRouter(), paths, new DefaultDefensePlanner(DefaultAmmunition.GetAll(), TestPlannerConfig.Instance));
new DamageModelRouter(), paths, new DefensePlanner(DefaultAmmunition.GetAll(), TestPlannerConfig.Instance));
}
public void Dispose()