feat(ai): 敌人感知↔状态模型——待机/警觉/追击/巡逻 + 可复用感知状态机
感知门面 EnemyBase.InChaseZone(追逐/aggro)/InVisionZone(视野/los,缺省回退aggro),纯查询; 转换逻辑归 AI 层: 新增可复用 PerceptionStateMachine(统一规则,HasAlert/视野缺省自动降级); 规则: 未发现进视野→警觉, 进追逐→追击(委托能力), 追击脱离视野→巡逻(永不回警觉,再进视野再警觉). EnemyStatsSO.HasAlertState 勾选项; ContactChaseAbility 改纯执行器(退出由AI中断); E001 用可复用件. PlayMode 验证四态转换全通过. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,10 +23,10 @@ namespace BaseGames.Enemies
|
||||
public IActorVitals Vitals => this;
|
||||
public Blackboard Blackboard => _blackboard;
|
||||
|
||||
/// <summary>每帧由 EnemyAiBrain 在 Tick 之前调用,维护最后已知位置与丢失计时。</summary>
|
||||
/// <summary>每帧由 EnemyAiBrain 在 Tick 之前调用,维护最后已知位置与丢失计时(供需要的敌人用)。</summary>
|
||||
public void Refresh(float dt)
|
||||
{
|
||||
if (DetectsPlayer() && _enemy.PlayerTransform != null)
|
||||
if (InVisionZone() && _enemy.PlayerTransform != null)
|
||||
{
|
||||
_lastKnown = _enemy.PlayerTransform.position;
|
||||
_lostTimer = 0f;
|
||||
@@ -45,17 +45,12 @@ namespace BaseGames.Enemies
|
||||
_lostTimer = 0f;
|
||||
}
|
||||
|
||||
// ---- ISensor ----
|
||||
// ---- ISensor ----(纯感知查询,转换由 AI 状态机决定)
|
||||
public bool InChaseZone() => _enemy.InChaseZone();
|
||||
public bool InVisionZone() => _enemy.InVisionZone();
|
||||
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) => !DetectsPlayer() && _lostTimer >= seconds;
|
||||
public bool LostFor(float seconds) => !InVisionZone() && _lostTimer >= seconds;
|
||||
public Vector2 LastKnown => _lastKnown;
|
||||
|
||||
// ---- IMover ----
|
||||
@@ -97,6 +92,8 @@ namespace BaseGames.Enemies
|
||||
var a = _enemy.Abilities?.Get(abilityId);
|
||||
return a != null && a.CanUse;
|
||||
}
|
||||
public void InterruptAbilities()
|
||||
=> _enemy.Abilities?.InterruptAll(Abilities.InterruptReason.ExternalRequest);
|
||||
|
||||
// ---- IActorVitals ----
|
||||
public bool IsAlive => _enemy.IsAlive;
|
||||
@@ -113,5 +110,6 @@ namespace BaseGames.Enemies
|
||||
|
||||
// ---- IEnemyActor ----
|
||||
public void SetPhase(AiPhase phase) => _enemy.SetAiPhase(phase);
|
||||
public bool HasAlertState => _enemy.StatsSO != null && _enemy.StatsSO.HasAlertState;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user