From 4f509e5a2a80ff4f9dd2e5e4bdf1609b37a72e61 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Sat, 13 Jun 2026 14:04:00 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=AF=8F=E5=8D=95=E5=85=83?= =?UTF-8?q?=E9=94=81=E5=AE=9A=E4=B8=80=E4=B8=AAY=E8=BD=A6=E9=81=93?= =?UTF-8?q?=EF=BC=8C=E9=9D=9E=E8=BD=AE=E8=BD=AC=E5=88=86=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 单元→车道固定分配(管转向跟不上一发一个Y) - 车道需求 tracked,单元依次填满当前车道 --- .../Algorithms/DefaultDefensePlanner.cs | 86 ++++++++----------- 1 file changed, 34 insertions(+), 52 deletions(-) diff --git a/src/CounterDrone.Core/Algorithms/DefaultDefensePlanner.cs b/src/CounterDrone.Core/Algorithms/DefaultDefensePlanner.cs index 72758b4..ee43d27 100644 --- a/src/CounterDrone.Core/Algorithms/DefaultDefensePlanner.cs +++ b/src/CounterDrone.Core/Algorithms/DefaultDefensePlanner.cs @@ -145,68 +145,50 @@ namespace CounterDrone.Core.Algorithms } int totalRoundsNeeded = singleNeeded * yLanes; - // 计算所需齐射轮数 - int totalChannels = candidates.Sum(c => c.Unit.TotalChannels); - int salvosNeeded = (int)Math.Ceiling((float)totalRoundsNeeded / Math.Max(1, totalChannels)); - - // 逐轮齐射分配 + // 逐单元分配:每个单元锁定一个 Y 车道 + var (effR2, _) = ComputeEffectiveRadius(threat, ammo, env); + float spacing = effR2 * 1.5f; + int[] laneNeeded = new int[yLanes]; + for (int l = 0; l < yLanes; l++) laneNeeded[l] = singleNeeded; + int currentLane = 0; int roundsCollected = 0; int eventIdx = 0; var assignedUnits = new List<(FireUnit unit, int rounds, List events)>(); - // Y 车道间距 - var (effR2, _) = ComputeEffectiveRadius(threat, ammo, env); - float laneSpacingY = yLanes > 1 ? formationWidth / (yLanes - 1) : 0f; - - for (int salvo = 0; salvo < salvosNeeded; salvo++) + foreach (var c in candidates.OrderBy(c => c.EarliestInterceptTime) + .ThenByDescending(c => c.KillProbability)) { - float salvoDelay = salvo * candidates[0].Unit.Cooldown; + if (roundsCollected >= totalRoundsNeeded) break; + int remaining = remainingMunitions.GetValueOrDefault(c.Unit.Id, 0); + if (remaining <= 0) continue; - // 车道参数 - var (laneEffR, _) = ComputeEffectiveRadius(threat, ammo, env); - float cloudDiameter = laneEffR * 2f; - float droneSpeed = (float)threat.Target.TypicalSpeed / 3.6f; - float physicsInterval = Math.Max(candidates[0].Unit.ChannelInterval, - cloudDiameter / droneSpeed); - float spacing = laneEffR * 1.5f; + // 找下一个有需求的车道 + while (currentLane < yLanes && laneNeeded[currentLane] <= 0) currentLane++; + if (currentLane >= yLanes) break; - // 所有弹药以中点偏移=0为基准计算基础发射时间 - var refEvent = GenerateFireEventsAt(threat, candidates[0].Unit, neededAmmo, ammo, env, 0, 0, yLanes, formationWidth); - float globalBaseFireTime = refEvent[0].FireTime; + int toTake = Math.Min(Math.Min(c.Unit.TotalChannels, remaining), laneNeeded[currentLane]); + if (toTake <= 0) continue; - foreach (var c in candidates.OrderBy(c => c.EarliestInterceptTime) - .ThenByDescending(c => c.KillProbability)) + // 此单元的所有弹药锁定同一个 Y 车道 + int yLane = currentLane; + var refEvt = GenerateFireEventsAt(threat, c.Unit, c.AmmoType, ammo, env, 0, yLane, yLanes, formationWidth); + float unitBaseTime = refEvt[0].FireTime; + + var fevents = new List(); + for (int ch = 0; ch < toTake; ch++) { - if (roundsCollected >= totalRoundsNeeded) break; - int remaining = remainingMunitions.GetValueOrDefault(c.Unit.Id, 0); - if (remaining <= 0) continue; - - int maxSalvo = Math.Min(c.Unit.TotalChannels, remaining); - int toTake = Math.Min(maxSalvo, totalRoundsNeeded - roundsCollected); - if (toTake <= 0) continue; - - // 所有单元以航路中点计算相同的基准发射时间,仅通道间隔错开 - var refEvt = GenerateFireEventsAt(threat, c.Unit, c.AmmoType, ammo, env, 0, 0, yLanes, formationWidth); - float unitBaseTime = refEvt[0].FireTime; - - var fevents = new List(); - for (int ch = 0; ch < toTake; ch++) - { - float offset = (eventIdx - (totalRoundsNeeded - 1) / 2f) * spacing; - int yLane = eventIdx % yLanes; - var fe = GenerateFireEventsAt(threat, c.Unit, c.AmmoType, ammo, env, offset, yLane, yLanes, formationWidth); - foreach (var e in fe) - { - e.FireTime = unitBaseTime + salvoDelay + ch * c.Unit.ChannelInterval; - } - fevents.AddRange(fe); - eventIdx++; - } - - assignedUnits.Add((c.Unit, toTake, fevents)); - remainingMunitions[c.Unit.Id] -= toTake; - roundsCollected += toTake; + float offset = (eventIdx - (totalRoundsNeeded - 1) / 2f) * spacing; + var fe = GenerateFireEventsAt(threat, c.Unit, c.AmmoType, ammo, env, offset, yLane, yLanes, formationWidth); + foreach (var e in fe) + e.FireTime = unitBaseTime + ch * c.Unit.ChannelInterval; + fevents.AddRange(fe); + eventIdx++; } + + assignedUnits.Add((c.Unit, toTake, fevents)); + remainingMunitions[c.Unit.Id] -= toTake; + laneNeeded[currentLane] -= toTake; + roundsCollected += toTake; } if (roundsCollected <= 0) { plan.ThreatsUnengaged++; continue; }