using Data; using UnityEngine; namespace AI { public class JobNode_Wander : LeafNodeBase { private float _wanderRange = 10f; // 徘徊的最大范围 private float _minWanderDistance = 2f; // 随机目标点距离当前位置的最小距离,避免原地踏步 private int _maxTargetSearchAttempts = 20; // 寻找可达目标点的最大尝试次数 private float _positionCheckThreshold = 0.001f; // 检测是否卡住的距离阈值 private const int _maxStuckFrames = 5; // 超过此帧数认为卡住 private Vector3 _wanderTargetPosition; // 当前徘徊的目标点 private Vector3 _lastPosition; // 上一帧的实体位置 private int _stuckFrameCount; // 累计在同一位置的帧数 /// /// 核心业务逻辑,用于执行随机徘徊。 /// LeafNodeBase 会在计时器管理后调用此方法。 /// /// 节点的执行状态(Running, Success, Failure)。 protected override Status ExecuteLeafLogic() { // 如果没有目标点,尝试寻找一个 if (_wanderTargetPosition == Vector3.zero) // Vector3.zero 作为未设置目标的标志 { if (!FindRandomWanderTarget()) { // 无法找到有效目标点,判定为失败 Debug.LogError( $"实体 {SelfEntity.currentDimensionId} 在 {_wanderRange} 范围内尝试 {_maxTargetSearchAttempts} 次后未能找到徘徊目标。"); return Status.Failure; } // 找到目标后,设置初始上一帧位置,并重置卡住计数 _lastPosition = SelfEntity.Position; _stuckFrameCount = 0; } // 尝试沿着路径移动 SelfEntity.TryMove(); // 检查是否到达目标点 if (SelfEntity.OnTargetPoint) { return Status.Success; } // 检查是否卡住 var currentPosition = SelfEntity.Position; // 使用 SqrMagnitude 比 Distance 略快,因为它避免了开方根运算 if (Vector3.SqrMagnitude(currentPosition - _lastPosition) < _positionCheckThreshold * _positionCheckThreshold) { _stuckFrameCount++; if (_stuckFrameCount > _maxStuckFrames) { Debug.LogWarning( $"实体 {SelfEntity.currentDimensionId} 在 {currentPosition} 卡住 {_stuckFrameCount} 帧。当前目标: {_wanderTargetPosition}"); return Status.Failure; // 卡住,返回失败 } } else { _stuckFrameCount = 0; // 位置发生变化,重置卡住计数 _lastPosition = currentPosition; } // 既未成功也未失败,继续运行 return Status.Running; } /// /// 寻找一个随机且可达的徘徊目标点。 /// /// 如果成功找到并设置目标点,则返回true;否则返回false。 private bool FindRandomWanderTarget() { var currentEntityPosition = SelfEntity.Position; var currentDimensionId = SelfEntity.currentDimensionId; for (var i = 0; i < _maxTargetSearchAttempts; i++) { // 生成相对当前位置的随机偏移 var randomOffset = Random.insideUnitCircle * _wanderRange; var potentialTarget = currentEntityPosition + new Vector3(randomOffset.x, randomOffset.y, 0); // 假定2D平面,Z轴不变 // 确保新目标点与当前位置有足够的距离 if (Vector3.Distance(potentialTarget, currentEntityPosition) < _minWanderDistance) { continue; // 距离太近,重试 } // 检查目标点的可达性 var travelCost = Program.Instance.GetDimension(currentDimensionId).landform .GetTravelCost(potentialTarget); if (travelCost < 1.0f) // 小于1表示可达 { _wanderTargetPosition = potentialTarget; SelfEntity.SetTarget(_wanderTargetPosition); return true; } } return false; // 达到最大尝试次数仍未找到有效目标 } /// /// 重置叶节点的状态,包括基类状态和内部计时器。 /// public override void Reset() { base.Reset(); // 调用基类的Reset方法,将CurrentStatus设为Ready并重置_elapsedFrames _wanderTargetPosition = Vector3.zero; // 重置目标点 _lastPosition = Vector3.zero; // 重置上一帧位置 _stuckFrameCount = 0; // 重置卡住计数 } } }