修复云团间距半径基准不一致 + 消除硬编码 30f

逻辑层修复(前端反馈的云团缝隙问题):
- ComputeEffectiveRadius 应用 ExpansionFactor,返回穿越时刻半径
- 间距/车道/弹药数统一用 effectiveRadius(原用 turbulentRadius=R30)
- TryGenerateFireEvents 复用 ComputeEffectiveRadius,消除重复计算
- 概率计算间距也加重叠系数
- GaussianPuffDispersion 消除硬编码 30f,读 _ammo.Phase2Duration
- DefensePlanner 消除硬编码 30f,读 model.Phase2Duration
- 265 测试通过,Unity 编译通过
This commit is contained in:
tian 2026-06-25 21:53:34 +08:00
parent 9d81ba0b28
commit dc62ba8c82
4 changed files with 11 additions and 16 deletions

View File

@ -138,16 +138,16 @@ namespace CounterDrone.Core.Algorithms
// 单机需求
var (effectiveRadius, expansionTime, turbulentRadius) = ComputeEffectiveRadius(ammo, env);
int singleNeeded = CalcRoundsNeeded(threat, ammo, env, false, turbulentRadius);
int singleNeeded = CalcRoundsNeeded(threat, ammo, env, false, effectiveRadius);
// 车道划分(可替换策略)
var (yLanes, laneSpacing) = _laneDivider.Divide(threat, turbulentRadius);
var (yLanes, laneSpacing) = _laneDivider.Divide(threat, effectiveRadius);
float formationWidth = yLanes > 1 ? (yLanes - 1) * laneSpacing : 0f;
int totalRoundsNeeded = singleNeeded * yLanes;
// 逐单元分配:每个单元锁定一个 Y 车道
// 云团间距 = 2R × (1 - 重叠比例),重叠由配置驱动,保证有效区互相覆盖
float spacing = 2f * turbulentRadius * (1f - _config.CloudOverlapRatio);
// 云团间距 = 2R × (1 - 重叠比例R 为穿越时刻的有效半径,保证无缝覆盖
float spacing = 2f * effectiveRadius * (1f - _config.CloudOverlapRatio);
int[] laneNeeded = new int[yLanes];
float[] laneBaseTime = new float[yLanes];
bool[] laneBaseSet = new bool[yLanes];
@ -178,7 +178,7 @@ namespace CounterDrone.Core.Algorithms
laneBaseTime[yLane] = refEvt[0].FireTime;
laneBaseSet[yLane] = true;
}
float stagger = Kinematics.CloudCoverInterval(turbulentRadius * 2f, GetDroneSpeedKph(threat), c.Unit.ChannelInterval);
float stagger = Kinematics.CloudCoverInterval(effectiveRadius * 2f, GetDroneSpeedKph(threat), c.Unit.ChannelInterval);
var fevents = new List<FireEvent>();
int unitIdx = fireUnits.IndexOf(c.Unit);
@ -381,10 +381,8 @@ namespace CounterDrone.Core.Algorithms
AmmunitionSpec ammo, CombatScene env)
{
var model = new CloudExpansionModel(ammo, env);
float rPhase2 = model.RadiusAt(30f);
float expansionTime = model.TimeToReach(rPhase2);
// 云团被穿过时的真实年龄 = expansionTime云团生成后膨胀到有效半径的时长
// 这是云团自身的物理量,与无人机飞行时间无关。
float rPhase2 = model.RadiusAt(model.Phase2Duration);
float expansionTime = model.TimeToReach(rPhase2) * _config.ExpansionFactor;
float effectiveR = model.RadiusAt(expansionTime);
return (effectiveR, expansionTime, rPhase2);
}
@ -407,7 +405,7 @@ namespace CounterDrone.Core.Algorithms
{
float avgSpeed = GetDroneSpeedMs(threat);
var (effectiveR, _, _) = ComputeEffectiveRadius(ammo, env);
float spacing = 2f * effectiveR;
float spacing = 2f * effectiveR * (1f - _config.CloudOverlapRatio);
float actualCoverage = spacing * (rounds - 1) + 2f * effectiveR;
float actualExposure = actualCoverage / avgSpeed;
@ -430,10 +428,7 @@ namespace CounterDrone.Core.Algorithms
if (wps == null || wps.Count < 2) return (events, "航路无效");
var cloudModel = new CloudExpansionModel(ammo, env);
// 云龄 = expansionTime云团生成后膨胀到有效半径的时长
float effectiveAge = cloudModel.TimeToReach(cloudModel.TurbulentRadius) * _config.ExpansionFactor;
float effectiveR = cloudModel.RadiusAt(effectiveAge);
float expansionTime = cloudModel.TimeToReach(effectiveR);
var (effectiveR, expansionTime, _) = ComputeEffectiveRadius(ammo, env);
float densityAtPassage = cloudModel.DensityAt(expansionTime);
if (densityAtPassage < (float)ammo.EffectiveConcentration)

View File

@ -61,7 +61,7 @@ namespace CounterDrone.Core.Algorithms
var (vx, vy, vz) = Kinematics.WindToVector(windDir, windSpeed);
_windVelocity = new Vector3(vx, vy, vz);
if (!_inPhase3 && _elapsed >= 30f)
if (!_inPhase3 && _elapsed >= (float)_ammo.Phase2Duration)
_inPhase3 = true;
if (!_inPhase3)
@ -76,7 +76,7 @@ namespace CounterDrone.Core.Algorithms
else
{
// Phase 3: 高斯扩散
float x = windSpeed * (_elapsed - 30f);
float x = windSpeed * (_elapsed - (float)_ammo.Phase2Duration);
if (x <= 0) return; // 尚未进入有效扩散距离
var cls = Kinematics.GetStabilityClass((WeatherType)_env.WeatherType, windSpeed);
var sY = Kinematics.SigmaY(cls, x);

Binary file not shown.