/// <summary> /// Takes cover based on the given search only if it was already adjacent to the previously held cover. /// </summary> /// <param name="search">Cover search.</param> /// <param name="observer">Position of the character.</param> public void Maintain(CoverSearch search, Vector3 observer) { Observer = observer; var closest = search.FindClosest(); var previousMain = Main; if (Main != null && Main != closest) { if (closest == null || (!Main.IsLeftAdjacent(closest, observer) && !Main.IsRightAdjacent(closest, observer))) { Main = null; } } if (Main != null) { LeftAdjacent = Main.LeftAdjacent; RightAdjacent = Main.RightAdjacent; } else { LeftAdjacent = null; RightAdjacent = null; } }
public static bool GetClosestCover(Vector3 position, float radius, ref Cover resultCover, ref Vector3 resultPosition) { var minDistance = 0f; var count = Physics.OverlapSphereNonAlloc(position, radius, Colliders, Layers.Cover, QueryTriggerInteraction.Collide); resultCover = null; for (int i = 0; i < count; i++) { var cover = CoverSearch.GetCover(Util.Colliders[i].gameObject); if (cover != null) { var point = cover.ClosestPointTo(position, 0.3f, 0.3f); var distance = Vector3.Distance(position, point); if (distance < minDistance || resultCover == null) { resultCover = cover; resultPosition = point; minDistance = distance; } } } return(resultCover != null); }
/// <summary> /// Takes cover based on the given search. /// </summary> /// <param name="search">Cover search.</param> /// <param name="observer">Position of the character.</param> /// <returns>Was the cover taken.</returns> public bool Take(CoverSearch search, Vector3 observer) { Observer = observer; var wasIn = In; var closest = search.FindClosest(); var previousMain = Main; if (Main == null && closest != null) { Main = closest; LeftAdjacent = Main.LeftAdjacent; RightAdjacent = Main.RightAdjacent; } else { Clear(); } if (In && !wasIn) { return(true); } else { return(false); } }
/// <summary> /// Takes cover based on the given search. /// </summary> /// <param name="search">Cover search.</param> /// <param name="observer">Position of the character.</param> /// <returns>Was the cover taken.</returns> public bool Take(CoverSearch search, Vector3 observer, float tallThreshold) { Observer = observer; ObserverTallThreshold = tallThreshold; var wasIn = In; var closest = search.FindClosest(); var previousMain = Main; if (Main == null && closest != null) { Main = closest; LeftAdjacent = Main.LeftAdjacent; RightAdjacent = Main.RightAdjacent; } else { Clear(); } if (Main != previousMain) { MainChangeAge = 0; } if (In && !wasIn) { return(true); } else { return(false); } }
/// <summary> /// Takes cover based on the given search only if it was already adjacent to the previously held cover. /// </summary> /// <param name="search">Cover search.</param> /// <param name="observer">Position of the character.</param> public void Maintain(CoverSearch search, Vector3 observer, float tallThreshold) { Observer = observer; ObserverTallThreshold = tallThreshold; var closest = search.FindClosest(); var previousMain = Main; if (Main != null && Main != closest) { if (closest != null) { if (closest == LeftAdjacent) { StandLeft(); Main = closest; } else if (closest == RightAdjacent) { StandRight(); Main = closest; } else { Main = null; } } else { Main = null; } } if (Main != null) { LeftAdjacent = Main.LeftAdjacent; RightAdjacent = Main.RightAdjacent; } else { LeftAdjacent = null; RightAdjacent = null; } if (Main != previousMain) { MainChangeAge = 0; } }
/// <summary> /// Finds covers near the position in a given radius. Calculates distances and sorts them. /// </summary> public void Reset(Vector3 observer, float maxDistance, bool detailedPositions = true) { Items.Clear(); var count = Physics.OverlapSphereNonAlloc(observer, maxDistance, Util.Colliders, Layers.Cover, QueryTriggerInteraction.Collide); for (int i = 0; i < count; i++) { var collider = Util.Colliders[i]; if (!collider.isTrigger) { continue; } var cover = CoverSearch.GetCover(collider.gameObject); if (cover == null) { continue; } if (cover.IsTall && detailedPositions) { if (cover.OpenLeft) { consider(cover, cover.LeftCorner(0, -0.3f), -1, observer, maxDistance); } if (cover.OpenRight) { consider(cover, cover.RightCorner(0, -0.3f), 1, observer, maxDistance); } } else { consider(cover, cover.ClosestPointTo(observer, 0.3f, 0.3f), 0, observer, maxDistance); } } Items.Sort((a, b) => a.Distance.CompareTo(b.Distance)); }
/// <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); } }
/// <summary> /// Told by the brains to investigate a position. /// </summary> /// <param name="position"></param> public void ToInvestigatePosition(Vector3 position) { _isInvestigating = true; _position = position; _cover = null; var minDistance = 0f; for (int i = 0; i < Physics.OverlapSphereNonAlloc(position, CoverSearchDistance, _colliders, 0x1 << 8, QueryTriggerInteraction.Collide); i++) { var cover = CoverSearch.GetCover(_colliders[i].gameObject); if (cover != null) { var point = cover.ClosestPointTo(position, 0.3f, 0.3f); var distance = Vector3.Distance(position, point); if (distance < minDistance || _cover == null) { _cover = cover; _position = point; minDistance = distance; } } } _verifyDistance = Util.GetViewDistance(_position, VerifyDistance, true); if (_cover == null) { _hasReachedCoverLine = false; if (isActiveAndEnabled) { Message("ToWalkTo", position); Message("OnInvestigationStart"); } } else { var vector = _position - transform.position; _hasReachedCoverLine = Vector3.Dot(_cover.Forward, vector) > 0; if (_hasReachedCoverLine) { if (isActiveAndEnabled) { Message("ToWalkTo", _position); Message("OnInvestigationStart"); } } else { var left = _cover.LeftCorner(_cover.Bottom, CoverOffset) - _cover.Forward * 1.0f; var right = _cover.RightCorner(_cover.Bottom, CoverOffset) - _cover.Forward * 1.0f; AIUtil.Path(ref _path, transform.position, left); var leftLength = 0f; if (_path.status == NavMeshPathStatus.PathInvalid) { leftLength = 999999f; } else { for (int i = 1; i < _path.GetCornersNonAlloc(_corners); i++) { leftLength += Vector3.Distance(_corners[i], _corners[i - 1]); } } AIUtil.Path(ref _path, transform.position, right); var rightLength = 0f; if (_path.status == NavMeshPathStatus.PathInvalid) { rightLength = 999999f; } else { for (int i = 1; i < _path.GetCornersNonAlloc(_corners); i++) { rightLength += Vector3.Distance(_corners[i], _corners[i - 1]); } } if (leftLength < rightLength) { _approachPosition = left; } else { _approachPosition = right; } var distance = Vector3.Distance(_approachPosition, _position); if (distance + VerifyRadius > _verifyDistance) { _approachPosition = _position + Vector3.Normalize(_approachPosition - _position) * (_verifyDistance + VerifyRadius - 0.1f); } if (isActiveAndEnabled) { Message("ToWalkTo", _approachPosition); Message("OnInvestigationStart"); } } } }