private void consider(Cover cover, Vector3 position, int direction, Vector3 observer, float maxDistance) { if (float.IsNaN(position.x) || float.IsNaN(position.z)) { return; } CoverItem item = new CoverItem(); item.Cover = cover; item.Position = position; item.Position.y = cover.Bottom; item.Distance = Vector3.Distance(observer, item.Position); item.Direction = direction; var distanceToObserver = Vector3.Distance(observer, item.Position); if (distanceToObserver > maxDistance) { return; } var areThereOthers = false; const float threshold = 3; if (cover.IsTall) { if (!AIUtil.IsCoverPositionFree(cover, item.Position, threshold, null)) { areThereOthers = true; } } else { var hasChangedPosition = false; Vector3 side; if (Vector3.Dot((item.Position - observer).normalized, cover.Right) > 0) { side = cover.Right; } else { side = cover.Left; } do { hasChangedPosition = false; if (!AIUtil.IsCoverPositionFree(cover, item.Position, threshold, null)) { var next = item.Position + side * 0.5f; if (cover.IsInFront(next, false)) { item.Position = next; hasChangedPosition = true; } else { areThereOthers = true; } } }while (hasChangedPosition); } if (areThereOthers) { return; } Items.Add(item); }
/// <summary> /// Updates the target situation to take cover. Returns true if a cover was found. /// </summary> public static bool TakeCover(AIController controller, ref AISituation situation) { var currentVectorToTarget = situation.ThreatGroundPosition - situation.CurrentPosition; var currentDistanceToTarget = currentVectorToTarget.magnitude; Cover result = null; float resultPathDistance = 0; int resultDirection = 0; Vector3 resultPosition = situation.CurrentPosition; var path = new NavMeshPath(); var corners = new Vector3[32]; var isAlreadyTooClose = currentDistanceToTarget <= controller.Distances.MinEnemy || currentDistanceToTarget <= controller.Cover.MinCoverToEnemyDistance; var covers = new List <CoverItem>(); foreach (var collider in Physics.OverlapSphere(controller.transform.position, controller.Cover.MaxDistance, 0x1 << 8, QueryTriggerInteraction.Collide)) { if (!collider.isTrigger) { continue; } var cover = CoverSearch.GetCover(collider.gameObject); if (cover == null || cover == situation.CurrentCover) { continue; } var item = new CoverItem(); item.Cover = cover; item.IsTall = cover.IsTall(controller.Motor.CoverSettings.TallThreshold); if (item.IsTall) { item.Direction = cover.ClosestCornerTo(situation.CurrentPosition, controller.Motor.CoverSettings.CornerAimTriggerDistance, out item.Point); } else { item.Point = cover.ClosestPointTo(situation.CurrentPosition, controller.Motor.CoverSettings.LowSideEnterRadius, 0.3f); } if (float.IsNaN(item.Point.x) || float.IsNaN(item.Point.z)) { continue; } item.Point.y = cover.Bottom; item.Distance = Vector3.Distance(controller.transform.position, item.Point); covers.Add(item); } foreach (var item in covers.OrderBy(o => o.Distance)) { var isTall = item.IsTall; var cover = item.Cover; var point = item.Point; var coverDirection = item.Direction; if (!AIUtil.IsGoodAngle(controller, cover, point, situation.ThreatGroundPosition, isTall)) { continue; } var distanceToTarget = Vector3.Distance(situation.ThreatGroundPosition, point); if (distanceToTarget < controller.Distances.MinEnemy || distanceToTarget < controller.Cover.MinCoverToEnemyDistance) { continue; } if (situation.CurrentCover != null && Vector3.Distance(situation.CurrentPosition, point) < controller.Cover.MinSwitchDistance) { continue; } if ((controller.Health == null || controller.Health.Health > controller.Fighting.MinHealth)) { if (isTall) { Vector3 aimPoint; coverDirection = cover.ClosestCornerTo(point, -controller.Motor.CoverSettings.CornerOffset.x, out aimPoint); if (!AIUtil.IsInSight(controller, aimPoint, situation.ThreatStandingTopPosition)) { continue; } } else if (!AIUtil.IsInSight(controller, point, situation.ThreatStandingTopPosition)) { continue; } } var distanceToOrigin = Vector3.Distance(situation.CurrentPosition, point); if (situation.CurrentCover == null) { if (distanceToOrigin > controller.Cover.MaxDistance) { continue; } } else if (distanceToOrigin > controller.Cover.MaxSwitchDistance) { continue; } var areThereFriends = false; { var hasChangedPosition = false; Vector3 side; if (Vector3.Dot((point - situation.CurrentPosition).normalized, cover.Right) > 0) { side = cover.Right; } else { side = cover.Left; } do { hasChangedPosition = false; if (AIUtil.IsCoverPositionTooCloseToFriends(cover, controller, point)) { var next = point + side * 0.5f; if (cover.IsInFront(next, false)) { point = next; hasChangedPosition = true; } else { areThereFriends = true; } } }while (hasChangedPosition); } if (areThereFriends) { continue; } var isOk = false; NavMesh.CalculatePath(situation.CurrentPosition, point, NavMesh.AllAreas, path); float pathDistance = 0f; var count = path.GetCornersNonAlloc(corners); if (count < 2) { continue; } var isTooCloseToEnemy = false; for (int i = 1; i < count; i++) { pathDistance += Vector3.Distance(corners[i - 1], corners[i]); if (!isAlreadyTooClose) { if (Util.DistanceToSegment(situation.ThreatGroundPosition, corners[i - 1], corners[i]) <= controller.Distances.MinPassing) { isTooCloseToEnemy = true; break; } } } if (isTooCloseToEnemy) { continue; } if (situation.CurrentCover == null) { isOk = result == null || pathDistance < resultPathDistance; } else if (controller.Health == null || controller.Health.Health > controller.Fighting.MinHealth) { isOk = (isAlreadyTooClose || distanceToTarget < currentDistanceToTarget) && (result == null || pathDistance < resultPathDistance); } else { isOk = distanceToTarget > currentDistanceToTarget && (result == null || pathDistance < resultPathDistance); } if (isOk) { result = cover; resultPosition = point; resultPathDistance = pathDistance; resultDirection = coverDirection; break; } } situation.TargetDirection = resultDirection; if (result == null) { if (situation.IsThreatInCover) { ApproachACovered(controller, ref situation); } else { ApproachAFree(controller, ref situation, controller.Agent, controller.Distances.MaxWalkingFight, true); } return(false); } else { situation.IsNewCover = true; situation.TargetCover = result; situation.TargetPosition = resultPosition; return(true); } }