using Data; using Managers; using UnityEngine; namespace AI { public class JobNode_MoveToAttackRange : LeafNodeBase { // 卡住检测的常量 private const int MAX_STUCK_FRAMES = 10; // 实体在多少帧内没有显著移动,则认为卡住 private const float STUCK_POSITION_THRESHOLD_SQ = 0.00001f; // 位置变化的平方距离阈值 // 新增的偏移尝试常量 private const int _maxOffsetAttempts = 5; // 尝试偏移的最大次数 private const float _offsetDistance = 0.5f; // 每次偏移的距离 private bool _isPathSet; // 指示当前是否已设置路径 private Vector3 _lastKnownSelfPosition; // 上次已知自身位置 private int _stuckFrameCount; // 卡住帧计数 private Entity.Entity _targetHostileEntity; // 目标敌对实体 private Vector3 _targetMovePosition; // 目标移动位置 // 便利属性,获取实体的攻击范围 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; var dir = (SelfEntity.Position - _targetHostileEntity.Position); if (dir.sqrMagnitude<=AttackRange*AttackRange) { return Status.Success; } // 计算目标攻击范围边缘的位置 var directionToSelf = dir.normalized; // 这次计算出的目标点是最初的精确临界点 var initialTargetMovePosition = _targetHostileEntity.Position + directionToSelf * AttackRange; _targetMovePosition = initialTargetMovePosition; // 暂时设置,可能被偏移后的点更新 // 3. 通知 SelfEntity 设置路径目标,并检查是否成功 if (SelfEntity.SetTarget(initialTargetMovePosition)) { // 路径成功生成,可以开始移动 _isPathSet = true; _lastKnownSelfPosition = SelfEntity.Position; // 初始化上次已知位置,用于卡住检测 _stuckFrameCount = 0; // 重置卡住计数器 return Status.Running; // 路径已设置,开始移动 } var pathFoundAfterOffset = false; for (var i = 0; i < _maxOffsetAttempts; i++) { // 生成随机偏移向量,确保在二维平面内 (假设Z轴不变) var randomOffsetCircle = Random.insideUnitCircle * _offsetDistance; var potentialOffsetTarget = initialTargetMovePosition + new Vector3(randomOffsetCircle.x, randomOffsetCircle.y, 0); // 再次尝试设置目标 if (SelfEntity.SetTarget(potentialOffsetTarget)) { // 偏移后路径成功生成 _targetMovePosition = potentialOffsetTarget; // 更新节点内部的目标为成功的偏移点 pathFoundAfterOffset = true; break; // 跳出偏移尝试循环 } } if (pathFoundAfterOffset) { // 偏移后找到路径,初始化状态并返回Running _isPathSet = true; _lastKnownSelfPosition = SelfEntity.Position; _stuckFrameCount = 0; return Status.Running; } _isPathSet = false; // 确保路径标志为false return Status.Failure; // 最终无法设置路径,任务失败 } // 路径已设置,继续管理移动和卡住检测 // 实体已到达目标点,或已经进入目标的攻击范围 if (SelfEntity.OnTargetPoint || (SelfEntity.Position - _targetHostileEntity.Position).sqrMagnitude < SelfEntity.AttributesNow.attackRange * SelfEntity.AttributesNow.attackRange) return Status.Success; // 成功移动到攻击范围 if (SelfEntity.AttributesNow.moveSpeed <= 0) return Status.Failure; // 检查是否卡住 // 计算当前位置与上次已知位置的平方距离,避免开方运算,提高性能 var currentPositionChangeSq = (SelfEntity.Position - _lastKnownSelfPosition).sqrMagnitude; if (currentPositionChangeSq < STUCK_POSITION_THRESHOLD_SQ) { _stuckFrameCount++; if (_stuckFrameCount >= MAX_STUCK_FRAMES) { _isPathSet = false; return Status.Running; } } else { // 实体有移动,重置卡住计数器 _stuckFrameCount = 0; } _lastKnownSelfPosition = SelfEntity.Position; 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; } } }