using System.Collections.Generic; using UnityEngine; using BaseGames.Core.Events; namespace BaseGames.Enemies { /// /// 敌人 BehaviorTree 配额管理器(架构 07_EnemyModule §13)。 /// 每 REBALANCE_INTERVAL 帧,按到玩家距离排序已注册敌人, /// 仅对最近的 _maxActiveBehaviorTrees 个敌人启用 BT。 /// 挂载在场景管理器 GameObject 上(每场景一个实例)。 /// public class EnemyQuotaManager : MonoBehaviour { [SerializeField, Min(1)] private int _maxActiveBehaviorTrees = 12; [Tooltip("PlayerController.Start() 广播此频道,替代 FindWithTag")] [SerializeField] private TransformEventChannelSO _onPlayerSpawned; private const int REBALANCE_INTERVAL = 10; private int _frameCount; // _registeredSet 用于 O(1) 重复检测,_registered List 用于 Sort private readonly HashSet _registeredSet = new(); private readonly List _registered = new(); private readonly Dictionary _indexMap = new(); // 排序临时缓冲区:预计算每个敌人到玩家的距离,避免 Sort 比较器内重复 Vector3 运算(O(n logn) → O(n)) private readonly List<(EnemyBase enemy, float sqDist)> _sortTemp = new(); // 缓存玩家 Transform private Transform _playerTransform; private readonly CompositeDisposable _subs = new(); private void OnEnable() { _onPlayerSpawned?.Subscribe(OnPlayerSpawned).AddTo(_subs); } private void OnDisable() => _subs.Clear(); private void OnPlayerSpawned(Transform playerTransform) => _playerTransform = playerTransform; // ── 注册 / 注销 ─────────────────────────────────────────────────── public void Register(EnemyBase enemy) { if (enemy != null && _registeredSet.Add(enemy)) { _indexMap[enemy] = _registered.Count; _registered.Add(enemy); } } public void Unregister(EnemyBase enemy) { if (enemy == null || !_registeredSet.Remove(enemy)) return; int idx = _indexMap[enemy]; int last = _registered.Count - 1; if (idx != last) { var moved = _registered[last]; _registered[idx] = moved; _indexMap[moved] = idx; } _registered.RemoveAt(last); _indexMap.Remove(enemy); } // ── Unity 生命周期 ──────────────────────────────────────────────── private void Update() { if (++_frameCount % REBALANCE_INTERVAL == 0) Rebalance(); } // ── 内部 ────────────────────────────────────────────────────────── private void Rebalance() { int n = _registered.Count; if (n == 0) return; var playerPos = _playerTransform != null ? _playerTransform.position : Vector3.zero; // ① 预计算距离(O(n) Vector3 运算,而非在比较器内重复执行 O(n logn) 次) _sortTemp.Clear(); for (int i = 0; i < n; i++) { var e = _registered[i]; float sqd = e != null ? (e.transform.position - playerPos).sqrMagnitude : float.MaxValue; _sortTemp.Add((e, sqd)); } // ② 对临时列表排序(比较器只做 float 比较,无额外 Vector3 开销) _sortTemp.Sort(static (a, b) => a.sqDist.CompareTo(b.sqDist)); // ③ 将排序结果写回 _registered,同步重建 _indexMap(修复排序后索引过期的 bug) for (int i = 0; i < n; i++) { var e = _sortTemp[i].enemy; _registered[i] = e; if (e != null) _indexMap[e] = i; } #if GRAPH_DESIGNER for (int i = n - 1; i >= 0; i--) { var enemy = _registered[i]; if (enemy == null) { _registered.RemoveAt(i); continue; } var bt = enemy.BehaviorTree; bool active = i < _maxActiveBehaviorTrees; if (bt != null && bt.enabled != active) { bt.enabled = active; // 同步暂停/恢复感知系统,避免远处敌人无效 tick enemy.SensorHub?.SetSuspended(!active); } } #endif } } }