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(); // 缓存玩家 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() { if (_registered.Count == 0) return; var playerPos = _playerTransform != null ? _playerTransform.position : Vector3.zero; // 按距离平方升序排序(避免开方,性能更好) _registered.Sort((a, b) => { if (a == null) return 1; if (b == null) return -1; float sqA = (a.transform.position - playerPos).sqrMagnitude; float sqB = (b.transform.position - playerPos).sqrMagnitude; return sqA.CompareTo(sqB); }); #if GRAPH_DESIGNER for (int i = _registered.Count - 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; } #endif } } }