refactor(ai): EnemyBase/QuotaManager 移除 Opsive BT,改接 EnemyAiBrain(Brain访问器/Died信号/OnSpawn重置/LOD裁剪)
This commit is contained in:
@@ -5,9 +5,6 @@ using BaseGames.Core.Events;
|
|||||||
using BaseGames.Core.Pool;
|
using BaseGames.Core.Pool;
|
||||||
using BaseGames.Enemies.States;
|
using BaseGames.Enemies.States;
|
||||||
using BaseGames.Enemies.Abilities;
|
using BaseGames.Enemies.Abilities;
|
||||||
#if GRAPH_DESIGNER
|
|
||||||
using Opsive.BehaviorDesigner.Runtime;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace BaseGames.Enemies
|
namespace BaseGames.Enemies
|
||||||
{
|
{
|
||||||
@@ -57,20 +54,6 @@ namespace BaseGames.Enemies
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[SerializeField] private BaseGames.Core.Events.TransformEventChannelSO _onPlayerSpawned;
|
[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 实现)───────────────────
|
// ── 导航代理(IPathAgent;由 EnemyNavAgent 实现)───────────────────
|
||||||
// 通过接口引用,避免对 Navigation 程序集的直接依赖。
|
// 通过接口引用,避免对 Navigation 程序集的直接依赖。
|
||||||
// 由子类 / Inspector 注入,或者运行时 GetComponent<IPathAgent>() 获取。
|
// 由子类 / Inspector 注入,或者运行时 GetComponent<IPathAgent>() 获取。
|
||||||
@@ -132,9 +115,6 @@ namespace BaseGames.Enemies
|
|||||||
[SerializeField] private AiPhase _dbg_AiPhase;
|
[SerializeField] private AiPhase _dbg_AiPhase;
|
||||||
[SerializeField] private bool _dbg_HasPlayer;
|
[SerializeField] private bool _dbg_HasPlayer;
|
||||||
[SerializeField] private Vector2 _dbg_LastKnownPos;
|
[SerializeField] private Vector2 _dbg_LastKnownPos;
|
||||||
#if GRAPH_DESIGNER
|
|
||||||
[SerializeField] private float _dbg_BtTickInterval;
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// POCO 状态对象字典:枚举保持对外 API 不变。
|
// POCO 状态对象字典:枚举保持对外 API 不变。
|
||||||
@@ -254,9 +234,9 @@ namespace BaseGames.Enemies
|
|||||||
/// <summary>状态效果管理器(冻结、灼烧、睡眠等)。</summary>
|
/// <summary>状态效果管理器(冻结、灼烧、睡眠等)。</summary>
|
||||||
public StatusEffects.EnemyStatusEffectManager StatusEffects => _statusEffects;
|
public StatusEffects.EnemyStatusEffectManager StatusEffects => _statusEffects;
|
||||||
private StatusEffects.EnemyStatusEffectManager _statusEffects;
|
private StatusEffects.EnemyStatusEffectManager _statusEffects;
|
||||||
#if GRAPH_DESIGNER
|
/// <summary>决策层组件(BrainGraph)。供 EnemyQuotaManager 做活跃数量 LOD 裁剪等使用。</summary>
|
||||||
public BehaviorTree BehaviorTree => _behaviorTree;
|
public EnemyAiBrain Brain => _brain;
|
||||||
#endif
|
private EnemyAiBrain _brain;
|
||||||
|
|
||||||
// ── BD 行为树接口(虚方法)────────────────────────────────────────
|
// ── BD 行为树接口(虚方法)────────────────────────────────────────
|
||||||
|
|
||||||
@@ -369,14 +349,12 @@ namespace BaseGames.Enemies
|
|||||||
=> _movement?.JumpToTarget(target);
|
=> _movement?.JumpToTarget(target);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 调整 BehaviorTree Tick 频率(非警觉=降频,警觉=高频)。
|
/// 调整决策 Tick 频率(非警觉=降频,警觉=高频)。
|
||||||
/// 由 BD_SetAlert 调用(架构 07_EnemyModule §13.5)。
|
/// 由外部(警戒/状态效果)调用(架构 07_EnemyModule §13.5)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void SetAggroTickRate(bool isAggro)
|
public virtual void SetAggroTickRate(bool isAggro)
|
||||||
{
|
{
|
||||||
#if GRAPH_DESIGNER
|
// LOD tick 速率控制迁移到后续 AiScheduler 阶段
|
||||||
_btCurrentInterval = isAggro ? _btAlertTickInterval : _btIdleTickInterval;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 警戒传播(Group Alert)────────────────────────────────────────
|
// ── 警戒传播(Group Alert)────────────────────────────────────────
|
||||||
@@ -468,20 +446,6 @@ namespace BaseGames.Enemies
|
|||||||
_currentAiPhase = phase;
|
_currentAiPhase = phase;
|
||||||
OnAiPhaseChanged?.Invoke(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)
|
if (_autoPlayPhaseAnimation && _animancer != null && _animConfig != null)
|
||||||
{
|
{
|
||||||
var clip = phase switch
|
var clip = phase switch
|
||||||
@@ -588,6 +552,7 @@ namespace BaseGames.Enemies
|
|||||||
_statusEffects = GetComponent<StatusEffects.EnemyStatusEffectManager>();
|
_statusEffects = GetComponent<StatusEffects.EnemyStatusEffectManager>();
|
||||||
_threatAssessor = GetComponent<Perception.EnemyThreatAssessor>();
|
_threatAssessor = GetComponent<Perception.EnemyThreatAssessor>();
|
||||||
_pooledObject = GetComponent<PooledObject>();
|
_pooledObject = GetComponent<PooledObject>();
|
||||||
|
_brain = GetComponent<EnemyAiBrain>();
|
||||||
_abilities.CollectFrom(gameObject);
|
_abilities.CollectFrom(gameObject);
|
||||||
_colliders = GetComponentsInChildren<Collider2D>(true);
|
_colliders = GetComponentsInChildren<Collider2D>(true);
|
||||||
|
|
||||||
@@ -602,20 +567,6 @@ namespace BaseGames.Enemies
|
|||||||
|
|
||||||
// 订阅玩家生成事件(PlayerController.Start 广播),避免每个敌人独立 FindWithTag
|
// 订阅玩家生成事件(PlayerController.Start 广播),避免每个敌人独立 FindWithTag
|
||||||
// 订阅在 OnEnable 中处理
|
// 订阅在 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()
|
protected virtual void Update()
|
||||||
@@ -626,34 +577,11 @@ namespace BaseGames.Enemies
|
|||||||
if (_playerTransform != null && _stats != null)
|
if (_playerTransform != null && _stats != null)
|
||||||
_stats.SqrDistanceToPlayer = ((Vector2)_playerTransform.position - (Vector2)transform.position).sqrMagnitude;
|
_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
|
#if UNITY_EDITOR
|
||||||
_dbg_CurrentState = _currentState;
|
_dbg_CurrentState = _currentState;
|
||||||
_dbg_AiPhase = _currentAiPhase;
|
_dbg_AiPhase = _currentAiPhase;
|
||||||
_dbg_HasPlayer = _playerTransform != null;
|
_dbg_HasPlayer = _playerTransform != null;
|
||||||
_dbg_LastKnownPos = LastKnownPlayerPosition;
|
_dbg_LastKnownPos = LastKnownPlayerPosition;
|
||||||
#if GRAPH_DESIGNER
|
|
||||||
_dbg_BtTickInterval = _btCurrentInterval;
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -691,26 +619,14 @@ namespace BaseGames.Enemies
|
|||||||
|
|
||||||
protected virtual void OnDestroy() { }
|
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>
|
/// <summary>
|
||||||
/// 停止行为树(死亡演出 / 出场演出等期间调用,防止 BT 继续 Tick 覆盖演出逻辑)。
|
/// 通知决策层终止(死亡演出 / 出场演出等期间调用,防止决策继续覆盖演出逻辑)。
|
||||||
/// 内部使用 #if GRAPH_DESIGNER 保护,调用方无需处理条件编译。
|
/// 语义为"通知决策层终止",与 PerformDeath 的 Died 信号幂等(重复 Died 无害)。
|
||||||
/// 供配置型行为组件(如 EnemyDeathSequence)调用,故为 public。
|
/// 供配置型行为组件(如 EnemyDeathSequence)调用,故为 public。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void StopBehaviorTree()
|
public void StopBehaviorTree()
|
||||||
{
|
{
|
||||||
#if GRAPH_DESIGNER
|
_brain?.Send(BaseGames.AI.AiSignal.Died);
|
||||||
_behaviorTree?.StopBehavior();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -744,6 +660,9 @@ namespace BaseGames.Enemies
|
|||||||
_deathSequenceActive = false;
|
_deathSequenceActive = false;
|
||||||
ForceState(EnemyStateType.Dead);
|
ForceState(EnemyStateType.Dead);
|
||||||
|
|
||||||
|
// 通知决策层敌人已死亡
|
||||||
|
_brain?.Send(BaseGames.AI.AiSignal.Died);
|
||||||
|
|
||||||
// 死亡时清除所有状态效果
|
// 死亡时清除所有状态效果
|
||||||
_statusEffects?.Clear();
|
_statusEffects?.Clear();
|
||||||
|
|
||||||
@@ -804,25 +723,21 @@ namespace BaseGames.Enemies
|
|||||||
// 重置能力冷却
|
// 重置能力冷却
|
||||||
_abilities.InterruptAll(InterruptReason.Dead);
|
_abilities.InterruptAll(InterruptReason.Dead);
|
||||||
|
|
||||||
#if GRAPH_DESIGNER
|
// 重置决策层(回到 Entry、清临时态)
|
||||||
_behaviorTree?.StartBehavior();
|
_brain?.ResetBrain();
|
||||||
#endif
|
|
||||||
|
|
||||||
// 通知配置型出生行为组件(如 EnemyAbilityTrigger)执行出生触发逻辑
|
// 通知配置型出生行为组件(如 EnemyAbilityTrigger)执行出生触发逻辑
|
||||||
Spawned?.Invoke();
|
Spawned?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 对象归还到池时调用,清理临时状态,停止 BT。
|
/// 对象归还到池时调用,清理临时状态。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void OnDespawn()
|
public virtual void OnDespawn()
|
||||||
{
|
{
|
||||||
_abilities.InterruptAll(InterruptReason.Dead);
|
_abilities.InterruptAll(InterruptReason.Dead);
|
||||||
_nav?.StopNavigation();
|
_nav?.StopNavigation();
|
||||||
|
// GO 停用时 EnemyAiBrain.Update 自然停止,无需显式处理。
|
||||||
#if GRAPH_DESIGNER
|
|
||||||
if (_behaviorTree != null) _behaviorTree.enabled = false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
|
|||||||
@@ -101,23 +101,22 @@ namespace BaseGames.Enemies
|
|||||||
if (e != null) _indexMap[e] = i;
|
if (e != null) _indexMap[e] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if GRAPH_DESIGNER
|
|
||||||
for (int i = n - 1; i >= 0; i--)
|
for (int i = n - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var enemy = _registered[i];
|
var enemy = _registered[i];
|
||||||
if (enemy == null) { _registered.RemoveAt(i); continue; }
|
if (enemy == null) { _registered.RemoveAt(i); continue; }
|
||||||
|
|
||||||
var bt = enemy.BehaviorTree;
|
var brain = enemy.Brain;
|
||||||
bool active = i < _maxActiveBehaviorTrees;
|
bool active = i < _maxActiveBehaviorTrees;
|
||||||
|
|
||||||
if (bt != null && bt.enabled != active)
|
if (brain != null && brain.enabled != active)
|
||||||
{
|
{
|
||||||
bt.enabled = active;
|
// 禁用 brain 即停其 Update/Tick,等价于旧的禁用行为树。
|
||||||
|
brain.enabled = active;
|
||||||
// 同步暂停/恢复感知系统,避免远处敌人无效 tick
|
// 同步暂停/恢复感知系统,避免远处敌人无效 tick
|
||||||
enemy.SensorHub?.SetSuspended(!active);
|
enemy.SensorHub?.SetSuspended(!active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user