refactor: CloudExpansionModel提取RoundsNeeded/TimeToDensity/TimeToReach

- RadiusAt/DensityAt/TimeToReach/TimeToDensity/RoundsNeeded 放入独立模块
- CalcRoundsNeeded 委托给 cloudModel.RoundsNeeded(requiredCoverage)
- GenerateFireEventsAt expansionTime 用 cloudModel.TimeToReach(RadiusAt(30f))
- 147/150 (Piston hp=0.10边际,LongRoute校验)
This commit is contained in:
tian 2026-06-13 18:18:19 +08:00
parent 510403a66c
commit cc3c7199c0
2 changed files with 26 additions and 10 deletions

View File

@ -49,6 +49,26 @@ 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>覆盖指定距离需要的云团数量(间距=2R</summary>
public int RoundsNeeded(float requiredCoverage)
{
float R = RadiusAt(30f);
float spacing = 2f * R;
return Math.Max(1, (int)Math.Ceiling(requiredCoverage / spacing));
}
/// <summary>达到指定半径所需时间 (s)</summary>
public float TimeToReach(float radius)
{

View File

@ -361,16 +361,12 @@ namespace CounterDrone.Core.Algorithms
private int CalcRoundsNeeded(DroneGroup threat, AmmunitionSpec ammo,
CombatScene env, bool isAirBased, float turbulentRadius)
{
var cloudModel = new CloudExpansionModel(ammo, env);
float avgSpeed = (float)threat.Target.TypicalSpeed / 3.6f;
float spacing = 2f * turbulentRadius;
var aerosolType = (AerosolType)ammo.AerosolType;
float neededExposure = aerosolType == AerosolType.ActiveMaterial ? 2f : 6f;
float singleCoverage = neededExposure * avgSpeed;
float requiredCoverage = singleCoverage;
return Math.Max(1,
(int)Math.Ceiling(requiredCoverage / spacing));
float requiredCoverage = neededExposure * avgSpeed;
return cloudModel.RoundsNeeded(requiredCoverage);
}
private float ComputeInterceptProbability(DroneGroup threat,
@ -399,7 +395,8 @@ namespace CounterDrone.Core.Algorithms
var events = new List<FireEvent>();
var mid = ThreatMidpoint(threat);
var (effectiveR, expansionTime, _) = ComputeEffectiveRadius(threat, ammo, env);
var cloudModel = new CloudExpansionModel(ammo, env);
float expansionTime = cloudModel.TimeToReach(cloudModel.RadiusAt(30f));
// 编队 Y 偏移:按车道分布
float laneSpacingZ = yLanes > 1 ? formationWidth / (yLanes - 1) : 0f;
@ -407,7 +404,7 @@ namespace CounterDrone.Core.Algorithms
float tx = mid.X + targetOffset;
// 按目标点位置计算无人机到达时间,非中点
// 无人机到达目标点时间
float droneSpeed = (float)threat.Target.TypicalSpeed / 3.6f;
if (droneSpeed <= 0) return events;
float startX = (float)threat.Waypoints[0].PosX;
@ -420,7 +417,6 @@ namespace CounterDrone.Core.Algorithms
distToTx = Math.Max(0, Math.Min(totalDist, distToTx));
float txArrival = distToTx / droneSpeed;
float recommendedTiming = txArrival - expansionTime;
// 来不及:无人机到达时云团未形成 → 跳过此目标点
if (recommendedTiming <= 0f) return events;
float fireTime;