diff --git a/Assets/_Game/Scripts/Enemies/Abilities/EnemyAttackSelector.cs b/Assets/_Game/Scripts/Enemies/Abilities/EnemyAttackSelector.cs index d10ee61c..d69ef58e 100644 --- a/Assets/_Game/Scripts/Enemies/Abilities/EnemyAttackSelector.cs +++ b/Assets/_Game/Scripts/Enemies/Abilities/EnemyAttackSelector.cs @@ -10,9 +10,14 @@ namespace BaseGames.Enemies.Abilities public sealed class EnemyAttackSelector { private readonly List _candidates; + // 权重缓冲区(与 _candidates 等长,每次选招复用):避免每次选招 new List 造成 GC + private readonly List _weightBuf; public EnemyAttackSelector(IEnumerable candidates) - => _candidates = new List(candidates); + { + _candidates = new List(candidates); + _weightBuf = new List(_candidates.Count); + } public int Count => _candidates.Count; @@ -47,22 +52,19 @@ namespace BaseGames.Enemies.Abilities private IAttackCandidate SelectByWeight(bool hasLOS, bool grounded) { - float total = 0f; + // 有效权重:不合格候选填 0(由共享原语保证零权重项绝不被选中)。缓冲区复用 → 选招零 GC。 + _weightBuf.Clear(); for (int i = 0; i < _candidates.Count; i++) { var c = _candidates[i]; - if (Eligible(c, hasLOS, grounded)) total += Mathf.Max(0f, c.Weight); + _weightBuf.Add(Eligible(c, hasLOS, grounded) ? Mathf.Max(0f, c.Weight) : 0f); } - if (total <= 0f) return SelectByPriority(hasLOS, grounded); // 权重全 0 → 退化为按 Priority 选(并列取首个) - float roll = Random.value * total; - for (int i = 0; i < _candidates.Count; i++) - { - var c = _candidates[i]; - if (!Eligible(c, hasLOS, grounded)) continue; - roll -= Mathf.Max(0f, c.Weight); - if (roll <= 0f) return c; - } - return null; // 理论不达(浮点边界兜底) + + int idx = BaseGames.Core.WeightedPick.Index(_weightBuf); + if (idx >= 0) return _candidates[idx]; + + // 无正权重(合格候选权重全 0)→ 退化为按 Priority 选(并列取首个) + return SelectByPriority(hasLOS, grounded); } } }