多轮审查和修复
This commit is contained in:
104
Assets/Scripts/Enemies/EnemyQuotaManager.cs
Normal file
104
Assets/Scripts/Enemies/EnemyQuotaManager.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.Enemies
|
||||
{
|
||||
/// <summary>
|
||||
/// 敌人 BehaviorTree 配额管理器(架构 07_EnemyModule §13)。
|
||||
/// 每 REBALANCE_INTERVAL 帧,按到玩家距离排序已注册敌人,
|
||||
/// 仅对最近的 _maxActiveBehaviorTrees 个敌人启用 BT。
|
||||
/// 挂载在场景管理器 GameObject 上(每场景一个实例)。
|
||||
/// </summary>
|
||||
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<EnemyBase> _registeredSet = new();
|
||||
private readonly List<EnemyBase> _registered = new();
|
||||
private readonly Dictionary<EnemyBase, int> _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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user