using Data; using Managers; using UnityEngine; namespace AI { public class JobNode_MoveToAttackRange : LeafNodeBase { private Entity.Entity _targetHostileEntity; // 目标敌对实体 private Vector3 _targetMovePosition; // 目标移动位置 private bool _isPathSet; // 指示当前是否已设置路径 private Vector3 _lastKnownSelfPosition; // 上次已知自身位置 private int _stuckFrameCount; // 卡住帧计数 // 卡住检测的常量 private const int MAX_STUCK_FRAMES = 10; // 实体在多少帧内没有显著移动,则认为卡住 private const float STUCK_POSITION_THRESHOLD_SQ = 0.001f; // 位置变化的平方距离阈值 // 便利属性,获取实体的攻击范围 private float AttackRange => SelfEntity.AttributesNow.attackRange; /// /// 执行移动到攻击范围的逻辑。 /// /// 行为树节点状态。 protected override Status ExecuteLeafLogic() { // 如果路径尚未设置,或目标实体无效(null或死亡),则需要重新寻找目标并设置路径 if (!_isPathSet || !_targetHostileEntity || _targetHostileEntity.IsDead) { // 1. 寻找最近的敌对实体 var hostileEntityRecord = EntityManager.Instance.FindNearestEntityByRelation( SelfEntity.currentDimensionId, SelfEntity.entityPrefab, Relation.Hostile); // 如果没有找到敌对目标,任务失败 if (!hostileEntityRecord || !hostileEntityRecord.entity) { return Status.Failure; } _targetHostileEntity = hostileEntityRecord.entity; // 2. 计算目标移动点(在敌对目标的攻击距离边缘) // 目标点是:从敌对实体指向 SelfEntity 的向量方向上,距离敌对实体 AttackRange 远的点。 // 这样做是为了让 SelfEntity 停在敌对实体攻击范围内,而不是直接重叠。 Vector3 directionToSelf = (SelfEntity.Position - _targetHostileEntity.Position).normalized; _targetMovePosition = _targetHostileEntity.Position + directionToSelf * AttackRange; // 3. 通知 SelfEntity 设置路径目标 SelfEntity.SetTarget(_targetMovePosition); _isPathSet = true; _lastKnownSelfPosition = SelfEntity.Position; // 初始化上次已知位置,用于卡住检测 _stuckFrameCount = 0; // 重置卡住计数器 return Status.Running; // 路径已设置,开始移动 } // 路径已设置,继续管理移动和卡住检测 // 4. 检查是否已到达目标点 if (SelfEntity.OnTargetPoint) { return Status.Success; // 成功移动到攻击范围 } // 5. 卡住检测 // 计算当前位置与上次已知位置的平方距离,避免开方运算,提高性能 float currentPositionChangeSq = (SelfEntity.Position - _lastKnownSelfPosition).sqrMagnitude; if (currentPositionChangeSq < STUCK_POSITION_THRESHOLD_SQ) { _stuckFrameCount++; if (_stuckFrameCount >= MAX_STUCK_FRAMES) { // 实体长时间未移动,被判定为卡住,需要重新规划路径 Debug.LogWarning( $"[{SelfEntity.entityDef.defName}] 行为节点<移动到攻击范围>: 实体卡住了! 重新计算到目标 [{_targetHostileEntity.entityDef.defName}] 的路径。"); _isPathSet = false; // 重置此标志,在下一帧会触发重新寻找目标和设置路径 return Status.Running; // 尽管卡住,但节点仍然在尝试完成任务 } } else { // 实体有移动,重置卡住计数器 _stuckFrameCount = 0; } // 更新上次已知位置 _lastKnownSelfPosition = SelfEntity.Position; // 6. 持续移动 SelfEntity.TryMove(); return Status.Running; // 正在向目标移动 } /// /// 重置此移动节点的所有内部状态。 /// public override void Reset() { base.Reset(); // 调用基类的 Reset,重置 CurrentStatus 和 _elapsedFrames _targetHostileEntity = null; _targetMovePosition = Vector3.zero; _isPathSet = false; _lastKnownSelfPosition = Vector3.zero; _stuckFrameCount = 0; } } }