refactor(ai): EnemyBase/QuotaManager 移除 Opsive BT,改接 EnemyAiBrain(Brain访问器/Died信号/OnSpawn重置/LOD裁剪)

This commit is contained in:
2026-07-03 15:10:55 +08:00
parent a9acace0a0
commit 23a3a1c274
2 changed files with 21 additions and 107 deletions
+17 -102
View File
@@ -5,9 +5,6 @@ using BaseGames.Core.Events;
using BaseGames.Core.Pool;
using BaseGames.Enemies.States;
using BaseGames.Enemies.Abilities;
#if GRAPH_DESIGNER
using Opsive.BehaviorDesigner.Runtime;
#endif
namespace BaseGames.Enemies
{
@@ -57,20 +54,6 @@ namespace BaseGames.Enemies
/// </summary>
[SerializeField] private BaseGames.Core.Events.TransformEventChannelSO _onPlayerSpawned;
#if GRAPH_DESIGNER
[Header("BT Tick 分级(LOD")]
[Tooltip("Idle 阶段 BT Tick 最小间隔(s);0 = 每帧全速。")]
[SerializeField] private float _btIdleTickInterval = 0.30f;
[Tooltip("Patrol/ReturnHome 阶段 BT Tick 间隔(s")]
[SerializeField] private float _btPatrolTickInterval = 0.15f;
[Tooltip("Alert/Investigate 阶段 BT Tick 间隔(s")]
[SerializeField] private float _btAlertTickInterval = 0.08f;
[Tooltip("Chase 阶段 BT Tick 间隔(s")]
[SerializeField] private float _btChaseTickInterval = 0.05f;
[Tooltip("Combat 阶段 BT Tick 间隔(s);0 = 每帧全速")]
[SerializeField] private float _btCombatTickInterval = 0f;
#endif
// ── 导航代理(IPathAgent;由 EnemyNavAgent 实现)───────────────────
// 通过接口引用,避免对 Navigation 程序集的直接依赖。
// 由子类 / Inspector 注入,或者运行时 GetComponent<IPathAgent>() 获取。
@@ -132,9 +115,6 @@ namespace BaseGames.Enemies
[SerializeField] private AiPhase _dbg_AiPhase;
[SerializeField] private bool _dbg_HasPlayer;
[SerializeField] private Vector2 _dbg_LastKnownPos;
#if GRAPH_DESIGNER
[SerializeField] private float _dbg_BtTickInterval;
#endif
#endif
// POCO 状态对象字典:枚举保持对外 API 不变。
@@ -254,9 +234,9 @@ namespace BaseGames.Enemies
/// <summary>状态效果管理器(冻结、灼烧、睡眠等)。</summary>
public StatusEffects.EnemyStatusEffectManager StatusEffects => _statusEffects;
private StatusEffects.EnemyStatusEffectManager _statusEffects;
#if GRAPH_DESIGNER
public BehaviorTree BehaviorTree => _behaviorTree;
#endif
/// <summary>决策层组件(BrainGraph)。供 EnemyQuotaManager 做活跃数量 LOD 裁剪等使用。</summary>
public EnemyAiBrain Brain => _brain;
private EnemyAiBrain _brain;
// ── BD 行为树接口(虚方法)────────────────────────────────────────
@@ -369,14 +349,12 @@ namespace BaseGames.Enemies
=> _movement?.JumpToTarget(target);
/// <summary>
/// 调整 BehaviorTree Tick 频率(非警觉=降频,警觉=高频)。
/// 由 BD_SetAlert 调用(架构 07_EnemyModule §13.5)。
/// 调整决策 Tick 频率(非警觉=降频,警觉=高频)。
/// 由外部(警戒/状态效果)调用(架构 07_EnemyModule §13.5)。
/// </summary>
public virtual void SetAggroTickRate(bool isAggro)
{
#if GRAPH_DESIGNER
_btCurrentInterval = isAggro ? _btAlertTickInterval : _btIdleTickInterval;
#endif
// LOD tick 速率控制迁移到后续 AiScheduler 阶段
}
// ── 警戒传播(Group Alert)────────────────────────────────────────
@@ -468,20 +446,6 @@ namespace BaseGames.Enemies
_currentAiPhase = phase;
OnAiPhaseChanged?.Invoke(phase);
#if GRAPH_DESIGNER
// 按行为阶段动态调整 BT Tick 频率(5 档 LOD
_btCurrentInterval = phase switch
{
AiPhase.Patrol => _btPatrolTickInterval,
AiPhase.Alert => _btAlertTickInterval,
AiPhase.Investigate => _btAlertTickInterval,
AiPhase.Chase => _btChaseTickInterval,
AiPhase.Combat => _btCombatTickInterval,
AiPhase.ReturnHome => _btPatrolTickInterval,
_ => _btIdleTickInterval, // Idle + 未来新阶段兜底
};
#endif
if (_autoPlayPhaseAnimation && _animancer != null && _animConfig != null)
{
var clip = phase switch
@@ -588,6 +552,7 @@ namespace BaseGames.Enemies
_statusEffects = GetComponent<StatusEffects.EnemyStatusEffectManager>();
_threatAssessor = GetComponent<Perception.EnemyThreatAssessor>();
_pooledObject = GetComponent<PooledObject>();
_brain = GetComponent<EnemyAiBrain>();
_abilities.CollectFrom(gameObject);
_colliders = GetComponentsInChildren<Collider2D>(true);
@@ -602,20 +567,6 @@ namespace BaseGames.Enemies
// 订阅玩家生成事件(PlayerController.Start 广播),避免每个敌人独立 FindWithTag
// 订阅在 OnEnable 中处理
#if GRAPH_DESIGNER
_behaviorTree = GetComponent<BehaviorTree>();
if (_behaviorTree != null)
{
_behaviorTree.StartWhenEnabled = false;
_behaviorTree.PauseWhenDisabled = true;
// Manual 模式:由 EnemyBase.Update 按 AiPhase 分级节流 Tick。
_behaviorTree.UpdateMode = Opsive.BehaviorDesigner.Runtime.Components.UpdateMode.Manual;
_btManualMode = true;
_btCurrentInterval = _btIdleTickInterval;
_behaviorTree.StartBehavior();
}
#endif
}
protected virtual void Update()
@@ -626,34 +577,11 @@ namespace BaseGames.Enemies
if (_playerTransform != null && _stats != null)
_stats.SqrDistanceToPlayer = ((Vector2)_playerTransform.position - (Vector2)transform.position).sqrMagnitude;
#if GRAPH_DESIGNER
// BT Tick LOD:按当前 AiPhase 分级节流,降低空闲状态的 BT 开销。
if (_btManualMode && _behaviorTree != null)
{
float interval = _btCurrentInterval;
if (interval <= 0f)
{
_behaviorTree.Tick();
}
else
{
_btTickTimer += Time.deltaTime;
if (_btTickTimer >= interval)
{
_btTickTimer -= interval;
_behaviorTree.Tick();
}
}
}
#endif
#if UNITY_EDITOR
_dbg_CurrentState = _currentState;
_dbg_AiPhase = _currentAiPhase;
_dbg_HasPlayer = _playerTransform != null;
_dbg_LastKnownPos = LastKnownPlayerPosition;
#if GRAPH_DESIGNER
_dbg_BtTickInterval = _btCurrentInterval;
#endif
#endif
}
@@ -691,26 +619,14 @@ namespace BaseGames.Enemies
protected virtual void OnDestroy() { }
// BehaviorTree 引用(#if GRAPH_DESIGNER 保护,避免空引用)
#if GRAPH_DESIGNER
private BehaviorTree _behaviorTree;
private bool _btManualMode;
private float _btTickTimer;
private float _btCurrentInterval;
#endif
/// <summary>
/// 停止行为树(死亡演出 / 出场演出等期间调用,防止 BT 继续 Tick 覆盖演出逻辑)。
/// 内部使用 #if GRAPH_DESIGNER 保护,调用方无需处理条件编译
/// 通知决策层终止(死亡演出 / 出场演出等期间调用,防止决策继续覆盖演出逻辑)。
/// 语义为"通知决策层终止",与 PerformDeath 的 Died 信号幂等(重复 Died 无害)
/// 供配置型行为组件(如 EnemyDeathSequence)调用,故为 public。
/// </summary>
public void StopBehaviorTree()
{
#if GRAPH_DESIGNER
_behaviorTree?.StopBehavior();
#endif
_brain?.Send(BaseGames.AI.AiSignal.Died);
}
/// <summary>
@@ -744,6 +660,9 @@ namespace BaseGames.Enemies
_deathSequenceActive = false;
ForceState(EnemyStateType.Dead);
// 通知决策层敌人已死亡
_brain?.Send(BaseGames.AI.AiSignal.Died);
// 死亡时清除所有状态效果
_statusEffects?.Clear();
@@ -804,25 +723,21 @@ namespace BaseGames.Enemies
// 重置能力冷却
_abilities.InterruptAll(InterruptReason.Dead);
#if GRAPH_DESIGNER
_behaviorTree?.StartBehavior();
#endif
// 重置决策层(回到 Entry、清临时态)
_brain?.ResetBrain();
// 通知配置型出生行为组件(如 EnemyAbilityTrigger)执行出生触发逻辑
Spawned?.Invoke();
}
/// <summary>
/// 对象归还到池时调用,清理临时状态,停止 BT
/// 对象归还到池时调用,清理临时状态。
/// </summary>
public virtual void OnDespawn()
{
_abilities.InterruptAll(InterruptReason.Dead);
_nav?.StopNavigation();
#if GRAPH_DESIGNER
if (_behaviorTree != null) _behaviorTree.enabled = false;
#endif
// GO 停用时 EnemyAiBrain.Update 自然停止,无需显式处理。
}
#if UNITY_EDITOR