refactor(ai): E001 AI 重写为纯决策层,符合设计并委托能力模块

依据小怪设计文档重构 E001 状态机 Idle_Disguise→Skill_Start→Skill_Loop→Skill_End→Move_Patrol:
追击移动/接触伤害全部委托 e001_chase(ContactChaseAbility),AI 不再裸 MoveTo。
新增 ISensor.DetectsPlayer(视线OR近身,对齐设计)、IMover.UseChase/PatrolSpeed、
IEnemyActor.SetPhase(AI 声明阶段、EnemyBase 播动画)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-03 16:51:47 +08:00
co-authored by Claude Opus 4.8
parent decd5503fb
commit 4ce798ea6e
7 changed files with 82 additions and 25 deletions
@@ -7,7 +7,7 @@ namespace BaseGames.Enemies
/// 把 EnemyBase 的子系统包装成 BrainGraph 的能力接口(每敌人一实例)。
/// 状态逻辑只依赖这些接口,不直接引用具体子系统。
/// </summary>
public sealed class EnemyBrainContext : IAiContext, ISensor, IMover, ICombatant, IActorVitals
public sealed class EnemyBrainContext : IAiContext, ISensor, IMover, ICombatant, IActorVitals, IEnemyActor
{
readonly EnemyBase _enemy;
readonly Blackboard _blackboard = new Blackboard();
@@ -26,7 +26,7 @@ namespace BaseGames.Enemies
/// <summary>每帧由 EnemyAiBrain 在 Tick 之前调用,维护最后已知位置与丢失计时。</summary>
public void Refresh(float dt)
{
if (_enemy.IsPlayerVisible() && _enemy.PlayerTransform != null)
if (DetectsPlayer() && _enemy.PlayerTransform != null)
{
_lastKnown = _enemy.PlayerTransform.position;
_lostTimer = 0f;
@@ -47,8 +47,15 @@ namespace BaseGames.Enemies
// ---- ISensor ----
public bool SeesPlayer() => _enemy.IsPlayerVisible();
/// <summary>视线 OR 近身 aggro 槽位,任一探到玩家即为真(对齐设计"视线 OR 碰撞触发圆")。</summary>
public bool DetectsPlayer()
{
if (_enemy.IsPlayerVisible()) return true;
var hub = _enemy.SensorHub;
return hub != null && hub.HasAnyDetection(Perception.SensorSlotNames.Aggro);
}
public bool InRange(float range) => _enemy.IsPlayerInRange(range);
public bool LostFor(float seconds) => !_enemy.IsPlayerVisible() && _lostTimer >= seconds;
public bool LostFor(float seconds) => !DetectsPlayer() && _lostTimer >= seconds;
public Vector2 LastKnown => _lastKnown;
// ---- IMover ----
@@ -61,6 +68,8 @@ namespace BaseGames.Enemies
if (nav != null && !nav.IsMoving) nav.WalkToRandom();
}
public void LookAround() => _enemy.BeginLookAround();
public void UseChaseSpeed() => _enemy.Nav?.SetSpeed(_enemy.Stats != null ? _enemy.Stats.RunSpeed : 0f);
public void UsePatrolSpeed() => _enemy.Nav?.SetSpeed(_enemy.Stats != null ? _enemy.Stats.WalkSpeed : 0f);
// ---- ICombatant ----
public bool UseAbility(string abilityId)
@@ -101,5 +110,8 @@ namespace BaseGames.Enemies
}
}
public bool HpBelow(float ratio) => HpPercent < ratio;
// ---- IEnemyActor ----
public void SetPhase(AiPhase phase) => _enemy.SetAiPhase(phase);
}
}