#if GRAPH_DESIGNER
using UnityEngine;
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.BehaviorDesigner.Runtime.Tasks.Actions;
using BaseGames.Enemies;
namespace BaseGames.Enemies.AI
{
///
/// BD Action:多步搜查(Navigate → LookAround → RandomWalk × N)。
///
///
/// - Navigate:导航至最后可见位置。
/// - LookAround:到达后原地环顾(播放 Look 动画,持续 LookAroundDuration)。
/// - WalkRandom:向搜查半径内的随机点移动,重复 RandomStepCount 次。
/// - 任意阶段重新发现玩家 → 返回 Failure,BD 树重入追击。
/// - 所有步骤完成仍未发现 → 返回 Success,BD 树归位/恢复巡逻。
///
///
[TaskName("Investigate Last Known Pos")]
[TaskCategory("BaseGames/Enemy/Movement")]
[TaskDescription("多步搜查:导航到最后已知位置 → 环顾四周 → 随机游走 N 次")]
public sealed class BD_InvestigateLastKnown : Action
{
[Tooltip("到达搜查点后环顾的时长(s)")]
[SerializeField] private float m_LookAroundDuration = 0.8f;
[Tooltip("随机行走步数(完成环顾后随机游荡的次数)")]
[SerializeField] [Min(0)] private int m_RandomStepCount = 2;
[Tooltip("随机行走半径(m)")]
[SerializeField] private float m_SearchRadius = 2.5f;
[Tooltip("到达目标点的判定半径(m)")]
[SerializeField] private float m_ArriveRadius = 0.6f;
private enum InvestigateSubStep { Navigate, LookAround, WalkRandom }
private EnemyBase _enemy;
private InvestigateSubStep _step;
private float _stepTimer;
private int _stepsRemaining;
private Vector2 _randomTarget;
private bool _pathFailed;
private bool _subscribed;
public override void OnAwake() => _enemy = gameObject.GetComponent();
public override void OnStart()
{
_enemy.SetAiPhase(AiPhase.Investigate);
_step = InvestigateSubStep.Navigate;
_stepTimer = 0f;
_stepsRemaining = m_RandomStepCount;
_pathFailed = false;
var ac = _enemy.AnimConfig;
if (_enemy.Animancer != null)
{
var clip = ac?.Investigate ?? ac?.Walk;
if (clip != null) _enemy.Animancer.Play(clip);
}
if (!_subscribed && _enemy.Nav != null)
{
_enemy.Nav.OnNavPathFailed += HandlePathFailed;
_subscribed = true;
}
_enemy.MoveTo(_enemy.LastKnownPlayerPosition);
}
public override TaskStatus OnUpdate()
{
if (_enemy == null) return TaskStatus.Failure;
if (_enemy.IsPlayerVisible()) return TaskStatus.Failure;
switch (_step)
{
case InvestigateSubStep.Navigate: return UpdateNavigate();
case InvestigateSubStep.LookAround: return UpdateLookAround();
case InvestigateSubStep.WalkRandom: return UpdateWalkRandom();
default: return TaskStatus.Success;
}
}
public override void OnEnd()
{
if (_subscribed && _enemy?.Nav != null)
{
_enemy.Nav.OnNavPathFailed -= HandlePathFailed;
_subscribed = false;
}
_enemy?.StopMovement();
}
// ── 阶段逻辑 ────────────────────────────────────────────────────
private TaskStatus UpdateNavigate()
{
Vector2 self = _enemy.transform.position;
bool arrived = _pathFailed ||
(self - _enemy.LastKnownPlayerPosition).sqrMagnitude <= m_ArriveRadius * m_ArriveRadius;
if (!arrived) return TaskStatus.Running;
_enemy.StopMovement();
EnterLookAround();
return TaskStatus.Running;
}
private TaskStatus UpdateLookAround()
{
_stepTimer += Time.deltaTime;
if (_stepTimer < m_LookAroundDuration) return TaskStatus.Running;
if (_stepsRemaining > 0)
EnterRandomWalk();
else
return TaskStatus.Success;
return TaskStatus.Running;
}
private TaskStatus UpdateWalkRandom()
{
Vector2 self = _enemy.transform.position;
bool arrived = _pathFailed ||
(self - _randomTarget).sqrMagnitude <= m_ArriveRadius * m_ArriveRadius;
if (!arrived) return TaskStatus.Running;
_enemy.StopMovement();
_stepsRemaining--;
_pathFailed = false;
if (_stepsRemaining > 0)
EnterRandomWalk();
else
return TaskStatus.Success;
return TaskStatus.Running;
}
// ── 辅助方法 ────────────────────────────────────────────────────
private void EnterLookAround()
{
_step = InvestigateSubStep.LookAround;
_stepTimer = 0f;
var ac = _enemy.AnimConfig;
if (_enemy.Animancer != null)
{
var clip = ac?.Investigate ?? ac?.Idle;
if (clip != null) _enemy.Animancer.Play(clip);
}
}
private void EnterRandomWalk()
{
_step = InvestigateSubStep.WalkRandom;
_pathFailed = false;
Vector2 origin = _enemy.LastKnownPlayerPosition;
// 横板地形中只在水平方向随机偏移,保留 origin.y;
// PathBerserker2d 寻路负责处理跨平台的纵向路径规划。
float dir = Random.value > 0.5f ? 1f : -1f;
float dist = Random.Range(0.5f, m_SearchRadius);
_randomTarget = new Vector2(origin.x + dir * dist, origin.y);
_enemy.MoveTo(_randomTarget);
var ac = _enemy.AnimConfig;
if (_enemy.Animancer != null)
{
var clip = ac?.Walk;
if (clip != null) _enemy.Animancer.Play(clip);
}
}
private void HandlePathFailed() => _pathFailed = true;
}
}
#endif