public AssignedAI(AIController ai, float distance) { AI = ai; Distance = distance; }
private void Update() { var targetTransform = TargetOverride == null ? transform.parent : TargetOverride.transform; if (targetTransform == null) { return; } if (_cachedTransform != targetTransform) { _cachedTransform = targetTransform; _cachedAIController = _cachedTransform.GetComponent <AIController>(); _cachedMotor = _cachedTransform.GetComponent <CharacterMotor>(); } if (_cachedAIController == null) { return; } var character = Characters.Get(_cachedAIController.gameObject); if (character.Motor.IsAlive && !_cachedAIController.IsAlerted && (DisplayWhenAway || character.IsAnyInSight(0))) { _alpha += Time.deltaTime * FadeSpeed; } else { _alpha -= Time.deltaTime * FadeSpeed; } if (_cachedMotor != null) { var target = _cachedMotor.HeadLookTarget; target.y = transform.position.y; transform.LookAt(target); } _alpha = Mathf.Clamp01(_alpha); _mesh.Clear(); var vertexCount = Detail * 3; var indexCount = Detail * 3; if (_positions == null || _positions.Length != vertexCount) { _positions = new Vector3[vertexCount]; } if (_colors == null || _colors.Length != vertexCount) { _colors = new Color[vertexCount]; } if (_uv == null || _uv.Length != vertexCount) { _uv = new Vector2[vertexCount]; } var wasEdited = false; if (_alpha >= 1f / 255f) { wasEdited = true; var fov = _cachedAIController.View.FieldOfView * _alpha; var distance = _cachedAIController.View.SightDistance; for (int i = 0; i < Detail; i++) { float a0 = fov * ((float)i / (float)Detail - 0.5f) + 90; float a1 = fov * ((float)(i + 1) / (float)Detail - 0.5f) + 90; _positions[i * 3 + 0] = Vector3.zero; _positions[i * 3 + 1] = new Vector3(Mathf.Cos(a1 * Mathf.Deg2Rad), 0, Mathf.Sin(a1 * Mathf.Deg2Rad)) * distance; _positions[i * 3 + 2] = new Vector3(Mathf.Cos(a0 * Mathf.Deg2Rad), 0, Mathf.Sin(a0 * Mathf.Deg2Rad)) * distance; } for (int i = 0; i < _colors.Length; i++) { _colors[i] = new Color(1, 1, 1, _alpha); } for (int i = 0; i < _uv.Length; i++) { _uv[i] = new Vector2((_positions[i].x / distance) * 0.5f + 0.5f, (_positions[i].z / distance) * 0.5f + 0.5f); } if (_indices == null || _indices.Length != indexCount) { _indices = new int[indexCount]; for (int i = 0; i < _indices.Length; i++) { _indices[i] = i; } } } if (wasEdited || !_hasSetAtLeastOnce) { _hasSetAtLeastOnce = true; _mesh.vertices = _positions; _mesh.colors = _colors; _mesh.uv = _uv; _mesh.triangles = _indices; } }
public override void OnInspectorGUI() { DrawDefaultInspector(); if (targets.Length != 1) { return; } EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); EditorGUILayout.LabelField("Waypoints", EditorStyles.boldLabel); var controller = (AIController)targets[0]; if (controller.Waypoints != null) { int toDelete = -1; int toMakeFirst = -1; if (GUILayout.Button("Reverse")) { Undo.RecordObject(controller, "Reverse waypoints"); if (controller.Waypoints.Length == 2) { var t = controller.Waypoints[0]; controller.Waypoints[0] = controller.Waypoints[1]; controller.Waypoints[1] = t; } else if (controller.Waypoints.Length > 2) { for (int i = 1; i < controller.Waypoints.Length / 2 + 1; i++) { var t = controller.Waypoints[i]; controller.Waypoints[i] = controller.Waypoints[controller.Waypoints.Length - i]; controller.Waypoints[controller.Waypoints.Length - i] = t; } } if (_lastSelectedWaypoint > 0) { _lastSelectedWaypoint = controller.Waypoints.Length - _lastSelectedWaypoint; } } for (int i = 0; i < controller.Waypoints.Length; i++) { EditorGUILayout.Space(); var isSelected = controller == _lastSelectedController && i == _lastSelectedWaypoint; if (isSelected) { var oldColor = GUI.backgroundColor; GUI.backgroundColor = Color.green; EditorGUILayout.BeginHorizontal(EditorStyles.helpBox); GUI.backgroundColor = oldColor; } else { EditorGUILayout.BeginHorizontal(); } var rect = EditorGUILayout.BeginVertical(); GUI.Box(rect, GUIContent.none); controller.Waypoints[i].Position = EditorGUILayout.Vector3Field("Position", controller.Waypoints[i].Position); controller.Waypoints[i].Pause = EditorGUILayout.FloatField("Pause", controller.Waypoints[i].Pause); controller.Waypoints[i].Run = EditorGUILayout.Toggle("Run", controller.Waypoints[i].Run); if (!isSelected) { if (GUILayout.Button("Select")) { _lastSelectedController = controller; _lastSelectedWaypoint = i; SceneView.RepaintAll(); } } if (i > 0) { if (GUILayout.Button("Make First")) { toMakeFirst = i; } } EditorGUILayout.EndVertical(); { var oldColor = GUI.backgroundColor; GUI.backgroundColor = Color.red; if (GUILayout.Button("X", GUILayout.Width(20))) { toDelete = i; } GUI.backgroundColor = oldColor; } EditorGUILayout.EndHorizontal(); } if (toDelete >= 0) { deleteWaypoint(controller, toDelete); } if (toMakeFirst >= 0) { Undo.RecordObject(controller, "Make waypoint first"); var old = controller.Waypoints; controller.Waypoints = new Waypoint[old.Length]; for (int i = 0; i < old.Length; i++) { controller.Waypoints[i] = old[(toMakeFirst + i) % old.Length]; } _lastSelectedWaypoint = 0; } } EditorGUILayout.Space(); if (GUILayout.Button("Add Waypoint")) { if (controller.Waypoints == null) { controller.Waypoints = new Waypoint[1]; } else { var old = controller.Waypoints; controller.Waypoints = new Waypoint[old.Length + 1]; for (int i = 0; i < old.Length; i++) { controller.Waypoints[i] = old[i]; } } var value = new Waypoint(); if (controller.Waypoints.Length > 1) { value.Position = controller.Waypoints[controller.Waypoints.Length - 2].Position; } else { value.Position = controller.transform.position; } controller.Waypoints[controller.Waypoints.Length - 1] = value; } }
public bool IsAggressive(AIController ai) { return(_aggressive.Contains(ai)); }
/// <summary> /// Returns an updated situation struct. /// </summary> public void Update(AIController controller, AISituation previous, bool updateInDetail) { if (!IsAlerted) { HasInvestigatedTheLatestAlert = false; } else if (Threat == null & Vector3.Distance(CurrentPosition, ThreatGroundPosition) < controller.Distances.ThreatInvestigation) { MarkInvestigated(); } if (HasAnInvestigatedAlert) { InvestigatedAlertAge += Time.deltaTime; } // Check grenades { IsNearGrenade = false; float minDist = 1000; foreach (var grenade in GrenadeList.All) { var vec = grenade.transform.position - controller.transform.position; var dist = vec.magnitude; if (dist < grenade.ExplosionRadius) { if (!IsNearGrenade || dist < minDist) { minDist = dist; IsNearGrenade = true; NearestGrenadePosition = grenade.transform.position; if (Threat == null) { HasInvestigatedTheLatestAlert = false; HasAnInvestigatedAlert = false; ThreatGroundPosition = grenade.transform.position; } } } } } // Check friends and enemies. if (Threat == null || (!IsAlerted && !IsGettingAlerted)) { var minEnemyInfoTimer = controller.View.EnemySustainTime; foreach (var actor in Actors.All) { if (actor != controller.Actor) { if (actor.Side != controller.Actor.Side) { if (AIUtil.IsInSight(controller, actor.TopPosition)) { IsAlerted = true; ReadEnemyState(actor); break; } } else if (actor.AI != null && actor.AI.IsAlerted) { var vector = actor.transform.position - controller.transform.position; if (vector.magnitude < controller.View.CommunicationDistance) { IsAlerted = true; if (actor.AI.Situation.NoThreatTimer < actor.AI.View.EnemySustainTime && minEnemyInfoTimer > actor.AI.Situation.NoThreatTimer) { TakeEnemyState(actor.AI); } } } } } } // Check friends if they had investigated the same position if (IsAlerted && Threat == null && !HasInvestigatedTheLatestAlert) { foreach (var friend in Actors.All) { if (friend != controller.Actor && friend.Side == controller.Actor.Side && friend.AI.IsAlerted && friend.AI.Situation.HasAnInvestigatedAlert && friend.AI.Situation.InvestigatedAlertAge < 10 && Vector3.Distance(friend.transform.position, controller.transform.position) < controller.View.CommunicationDistance && Vector3.Distance(friend.AI.Situation.InvestigatedThreatPosition, ThreatGroundPosition) < controller.Distances.ThreatInvestigation) { MarkInvestigated(); break; } } } // Check threats if (Threat == null) { var minDist = 100000f; foreach (var alert in Alerts.All) { var dist = Vector3.Distance(controller.transform.position, alert.Position); if (dist < alert.Range) { if (dist < minDist) { minDist = dist; IsAlerted = true; ThreatGroundPosition = alert.Position; HasAnInvestigatedAlert = false; HasInvestigatedTheLatestAlert = false; } } } } // React to grenades if (IsNoticingGrenade) { if (GrenadeReaction < float.Epsilon) { IsNoticingGrenade = false; } else { GrenadeReaction -= Time.deltaTime; IsNearGrenade = false; } } else if (IsNearGrenade && !previous.IsNearGrenade) { GrenadeReaction = controller.Fighting.GrenadeReactionTime; IsNoticingGrenade = true; IsNearGrenade = false; } if (IsNearGrenade) { IsAlerted = true; } // React to being alerted. if (IsGettingAlerted) { if (AlertReaction < float.Epsilon) { IsGettingAlerted = false; IsAlerted = true; } else { AlertReaction -= Time.deltaTime; IsAlerted = false; } } else if (IsAlerted && !previous.IsAlerted) { AlertReaction = controller.Fighting.ReactionTime; IsGettingAlerted = true; IsAlerted = false; } if (previous.TargetCover != null && (controller.Motor.LeftCover == previous.TargetCover || controller.Motor.RightCover == previous.TargetCover || controller.Motor.Cover == previous.TargetCover)) { CurrentCover = previous.TargetCover; } else { CurrentCover = controller.Motor.Cover; } CurrentPosition = controller.transform.position; IsGunReady = controller.Motor.IsGunReady && controller.Motor.Gun.Clip >= controller.Motor.Gun.ClipSize * controller.Fighting.ReloadFraction; if (controller.Health != null && Threat != null && Threat.IsAttacking) { IsRetreating = IsAlerted && controller.Health.Health <= controller.Fighting.MinHealth; } else { IsRetreating = false; } if (IsAlerted) { var couldSeeTheEnemy = CanSeeTheThreat; CanSeeTheThreat = false; if (Threat != null) { if (couldSeeTheEnemy || updateInDetail) { CanSeeTheThreat = AIUtil.IsInSight(controller, Threat.TopPosition); } if (CanSeeTheThreat) { ReadEnemyState(Threat); } else { NoThreatTimer += Time.deltaTime; if (updateInDetail) { // Check friends and enemies. foreach (var friend in Actors.All) { if (friend != controller.Actor && friend.Side == controller.Actor.Side && friend.AI != null && friend.AI.IsAlerted && friend.AI.Situation.CanSeeTheThreat && friend.AI.Situation.Threat == Threat) { var vector = friend.transform.position - controller.transform.position; if (vector.magnitude < controller.View.CommunicationDistance) { TakeEnemyState(friend.AI); } } } } } } if (TargetCover != null && updateInDetail) { var distanceToThreat = Vector3.Distance(TargetPosition, ThreatGroundPosition); IsTargetCoverGood = distanceToThreat >= controller.Distances.MinEnemy && distanceToThreat >= controller.Cover.MinCoverToEnemyDistance && AIUtil.IsGoodAngle(controller, TargetCover, TargetPosition, ThreatGroundPosition, TargetCover.IsTall(controller.Motor.CoverSettings.TallThreshold)) && !AIUtil.IsCoverPositionTooCloseToFriends(TargetCover, controller, TargetPosition); } if (updateInDetail) { if (TargetCover != null && TargetCover.IsTall(controller.Motor.CoverSettings.TallThreshold)) { Vector3 aimPoint; if (TargetDirection < 0) { aimPoint = TargetCover.LeftCorner(0, controller.Motor.CoverSettings.CornerOffset.x); } else { aimPoint = TargetCover.RightCorner(0, controller.Motor.CoverSettings.CornerOffset.x); } CanSeeFromTargetPosition = AIUtil.IsInSight(controller, aimPoint, ThreatStandingTopPosition); } else { CanSeeFromTargetPosition = AIUtil.IsInSight(controller, TargetPosition, ThreatStandingTopPosition); } } } else { if (TargetCover != null && updateInDetail) { IsTargetCoverGood = !AIUtil.IsCoverPositionTooCloseToFriends(TargetCover, controller, TargetPosition); } CanSeeFromTargetPosition = true; } }
protected virtual void OnSceneGUI() { var controller = (AIController)target; var controlId = GUIUtility.GetControlID(_editorHash, FocusType.Passive); var hasMousePosition = false; var mousePosition = Vector3.zero; if (SceneView.currentDrawingSceneView.camera.pixelRect.Contains(HandleUtility.GUIPointToScreenPixelCoordinate(Event.current.mousePosition))) { var ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { if (Vector3.Dot(hit.normal, Vector3.up) > 0.25f) { mousePosition = hit.point; hasMousePosition = true; } } } var current = Event.current; var hasToSelect = false; switch (current.GetTypeForControl(controlId)) { case EventType.KeyDown: if (current.keyCode == KeyCode.Delete) { if (_lastSelectedController == controller && _lastSelectedController.Waypoints != null && _lastSelectedWaypoint >= 0 && _lastSelectedWaypoint < _lastSelectedController.Waypoints.Length) { deleteWaypoint(_lastSelectedController, _lastSelectedWaypoint); while (_lastSelectedWaypoint >= _lastSelectedController.Waypoints.Length) { _lastSelectedWaypoint--; } current.Use(); } } break; case EventType.MouseDown: case EventType.MouseDrag: if (current.button == 0) { if ((GUIUtility.hotControl == 0 && current.GetTypeForControl(controlId) != EventType.MouseDrag && HandleUtility.nearestControl == controlId) || GUIUtility.hotControl == controlId) { _dontDrawPreview = false; if (hasMousePosition) { if (!_wasMouseTooFarAway) { if (current.type == EventType.MouseDown) { GUIUtility.hotControl = controlId; hasToSelect = true; } } current.Use(); } } } break; case EventType.MouseUp: if (GUIUtility.hotControl == controlId) { GUIUtility.hotControl = 0; } break; case EventType.MouseMove: if (HandleUtility.nearestControl != controlId) { hasMousePosition = false; _dontDrawPreview = !_wasMouseTooFarAway; } else { _dontDrawPreview = false; } SceneView.RepaintAll(); break; case EventType.Layout: if (!_wasMouseTooFarAway) { HandleUtility.AddDefaultControl(controlId); } break; } var isHoveringPoint = false; var hasLowestPoint = false; var lowestPoint = 0f; if (controller.Waypoints != null) { for (int i = 0; i < controller.Waypoints.Length; i++) { if (!hasLowestPoint || controller.Waypoints[i].Position.y < lowestPoint) { hasLowestPoint = true; lowestPoint = controller.Waypoints[i].Position.y; } } var oldColor = Handles.color; Handles.color = Color.magenta; for (int i = 0; i < controller.Waypoints.Length; i++) { var bottom = controller.Waypoints[i].Position; bottom.y = lowestPoint; Handles.DrawLine(controller.Waypoints[i].Position, bottom); } Handles.color = oldColor; } if (controller.Waypoints != null) { var oldColor = Handles.color; Handles.color = Color.yellow; const float maxDistance = 0.5f; for (int i = 0; i < controller.Waypoints.Length; i++) { if (i == 0 && controller.Waypoints.Length > 1) { var p0 = controller.Waypoints[0].Position; var p1 = controller.Waypoints[1].Position; var right = Vector3.Cross(p1 - p0, Vector3.up); Handles.DrawLine(p1, p1 + Vector3.Lerp(p0 - p1, right, 0.35f) * 0.75f); Handles.DrawLine(p1, p1 + Vector3.Lerp(p0 - p1, -right, 0.35f) * 0.75f); Handles.DrawWireDisc(controller.Waypoints[i].Position, Vector3.up, maxDistance * 0.75f); } var isHovered = false; var wasJustSelected = false; if (hasMousePosition && !isHoveringPoint && (GUIUtility.hotControl == 0 || hasToSelect)) { if (Vector3.Distance(mousePosition, controller.Waypoints[i].Position) < maxDistance) { isHovered = true; isHoveringPoint = true; if (!_dontDrawPreview) { Handles.color = Color.white; Handles.DrawWireDisc(controller.Waypoints[i].Position, Vector3.up, maxDistance); Handles.color = Color.yellow; } if (hasToSelect) { _lastSelectedWaypoint = i; _lastSelectedController = controller; hasToSelect = false; wasJustSelected = true; EditorUtility.SetDirty(target); } } } if (!hasToSelect && !wasJustSelected) { if (_lastSelectedController == controller && _lastSelectedWaypoint == i) { Handles.color = Color.green; Handles.DrawWireDisc(controller.Waypoints[i].Position, Vector3.up, maxDistance); if (hasMousePosition) { if (GUIUtility.hotControl == controlId) { controller.Waypoints[i].Position = mousePosition; Undo.RecordObject(controller, "Moving a waypoint"); } } Handles.color = Color.yellow; } else if (!isHovered) { Handles.DrawWireDisc(controller.Waypoints[i].Position, Vector3.up, maxDistance * 0.5f); } } if (controller.Waypoints.Length > 1) { var next = controller.Waypoints[i].Position; Vector3 previous; if (i == 0) { previous = controller.Waypoints[controller.Waypoints.Length - 1].Position; } else { previous = controller.Waypoints[i - 1].Position; } Handles.DrawLine(previous, next); } } Handles.color = oldColor; } var canPlacePoint = false; if (hasMousePosition && !isHoveringPoint && (GUIUtility.hotControl == 0 || hasToSelect)) { var oldColor = Handles.color; Handles.color = Color.white; var indexInBetween = -1; var minDist = 0f; if (controller.Waypoints != null) { const float maxDistance = 8; if (controller.Waypoints.Length == 0) { indexInBetween = -1; canPlacePoint = Vector3.Distance(mousePosition, controller.transform.position) < maxDistance; } else if (controller.Waypoints.Length == 1) { indexInBetween = 0; canPlacePoint = Vector3.Distance(mousePosition, controller.Waypoints[0].Position) < maxDistance; if (canPlacePoint && !_dontDrawPreview) { Handles.DrawLine(mousePosition, controller.Waypoints[0].Position); } } else { for (int i = 0; i < controller.Waypoints.Length; i++) { var next = (i == controller.Waypoints.Length - 1) ? 0 : i + 1; var p0 = controller.Waypoints[i].Position; var p1 = controller.Waypoints[next].Position; var dist = Vector3.Distance(mousePosition, Util.FindClosestToPath(p0, p1, mousePosition)); if (dist < maxDistance) { if (indexInBetween < 0 || dist < minDist) { indexInBetween = i; minDist = dist; } } } if (indexInBetween >= 0) { canPlacePoint = true; if (!_dontDrawPreview) { var second = (indexInBetween == controller.Waypoints.Length - 1) ? 0 : indexInBetween + 1; Handles.DrawLine(mousePosition, controller.Waypoints[indexInBetween].Position); Handles.DrawLine(mousePosition, controller.Waypoints[second].Position); } } } } if (canPlacePoint) { if (!_dontDrawPreview) { Handles.DrawWireDisc(mousePosition, Vector3.up, 0.5f); if (hasLowestPoint) { Handles.color = Color.grey; Handles.DrawLine(mousePosition, new Vector3(mousePosition.x, lowestPoint, mousePosition.z)); Handles.color = Color.white; } } if (hasToSelect) { Undo.RecordObject(controller, "Adding a waypoint"); var index = indexInBetween + 1; if (controller.Waypoints == null) { controller.Waypoints = new Waypoint[1]; } else if (controller.Waypoints.Length == 1) { var old = controller.Waypoints[0]; controller.Waypoints = new Waypoint[2]; controller.Waypoints[0] = old; } else { var old = controller.Waypoints; controller.Waypoints = new Waypoint[old.Length + 1]; for (int i = 0; i < index; i++) { controller.Waypoints[i] = old[i]; } for (int i = index; i < old.Length; i++) { controller.Waypoints[i + 1] = old[i]; } } controller.Waypoints[index].Position = mousePosition; if (index > 0) { controller.Waypoints[index].Run = controller.Waypoints[index - 1].Run; } else { controller.Waypoints[index].Run = controller.Waypoints[controller.Waypoints.Length - 1].Run; } _lastSelectedController = controller; _lastSelectedWaypoint = index; } } Handles.color = oldColor; } _wasMouseTooFarAway = !canPlacePoint && !isHoveringPoint; }
/// <summary> /// Updates the target situation to approach an enemy that's not in cover. /// </summary> public static void ApproachAFree(AIController controller, ref AISituation situation, NavMeshAgent agent, float maxDistance, bool approachStanding) { situation.TargetCover = null; situation.TargetPosition = situation.ThreatGroundPosition; var path = new NavMeshPath(); agent.CalculatePath(situation.ThreatGroundPosition, path); var corners = new Vector3[16]; var count = path.GetCornersNonAlloc(corners); if (count < 2) { return; } var i = 0; var p0 = corners[i]; var p1 = corners[i + 1]; var f = 1f; var targetPosition = approachStanding ? situation.ThreatStandingTopPosition : situation.ThreatGroundPosition; { var distLeft = Vector3.Distance(controller.transform.position, situation.ThreatGroundPosition); while (distLeft > maxDistance) { var pd = Vector3.Distance(p0, p1); var left = distLeft - maxDistance; distLeft -= pd; if (pd >= left) { f = left / pd; break; } else { i++; if (i + 1 >= count) { i = 0; break; } else { p0 = corners[i]; p1 = corners[i + 1]; } } } } while (i + 1 < count) { var p = p0 + (p1 - p0) * f; if (AIUtil.IsInSight(controller, p, targetPosition)) { situation.TargetPosition = p; break; } f += 0.2f; if (f >= 1f) { if (AIUtil.IsInSight(controller, p1, targetPosition)) { situation.TargetPosition = p1; break; } f = 0; i++; } } }
/// <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> /// Returns true if the given position on the cover protects the character from the enemy. /// </summary> public static bool IsGoodAngle(AIController controller, Cover cover, Vector3 positionOnCover, Vector3 enemy, bool isTall) { return(IsGoodAngle(controller.Cover.MaxTallAngle, controller.Cover.MaxLowAngle, cover, positionOnCover, enemy, isTall)); }
/// <summary> /// Returns true if a given position is in sight. /// </summary> public static bool IsInSight(AIController controller, Vector3 target) { return(IsInSight(controller, controller.transform.position, target)); }
/// <summary> /// Returns an updated situation struct. /// </summary> public void Update(AIController controller, AISituation previous, bool updateInDetail) { if (!IsAlerted) { HasInvestigatedTheLatestAlert = false; } else if (!HasInvestigatedTheLatestAlert && Vector3.Distance(CurrentPosition, InvestigationPosition) < controller.Distances.ThreatInvestigation) { MarkInvestigated(); } if (HasAnInvestigatedAlert) { InvestigatedAlertAge += Time.deltaTime; } if (IsIrritated) { if (IrritationTime > controller.Fighting.Irritation) { IrritationTime = 0; IsIrritated = false; } else { IrritationTime += Time.deltaTime; } } else { IrritationTime = 0; } // Check grenades { IsNearGrenade = false; float minDist = 1000; foreach (var grenade in GrenadeList.All) { var vec = grenade.transform.position - controller.transform.position; var dist = vec.magnitude; if (dist < grenade.ExplosionRadius) { if (!IsNearGrenade || dist < minDist) { minDist = dist; IsNearGrenade = true; NearestGrenadePosition = grenade.transform.position; if (Threat == null) { HasInvestigatedTheLatestAlert = false; HasAnInvestigatedAlert = false; IsThreatPositionANewAlert = true; SetThreatPosition(grenade.transform.position, true); } } } } } // Check friends and enemies. if (Threat == null || (!IsAlerted && !IsGettingAlerted)) { foreach (var actor in Actors.All) { if (actor != controller.Actor) { if (actor.Side != controller.Actor.Side) { if (AIUtil.IsInSight(controller, actor.TopPosition)) { IsAlerted = true; ReadEnemyState(actor); break; } } else if (actor.AI != null && actor.AI.IsAlerted) { var vector = actor.transform.position - controller.transform.position; if (vector.magnitude < controller.View.CommunicationDistance) { IsAlerted = true; if (actor.AI.State != AIState.investigate) { if (actor.AI.Situation.Threat != null && actor.AI.Situation.HasBetterThreatInfo(actor.AI, ref this)) { TakeEnemyState(actor.AI); break; } } } } } } } // Check friends if they had investigated the same position if (IsAlerted && !HasInvestigatedTheLatestAlert) { foreach (var friend in Actors.All) { if (friend != controller.Actor && friend.Side == controller.Actor.Side && friend.AI != null && friend.AI.IsAlerted && friend.AI.Situation.HasAnInvestigatedAlert && friend.AI.Situation.InvestigatedAlertAge < 4 && Vector3.Distance(friend.transform.position, controller.transform.position) < controller.View.CommunicationDistance && Vector3.Distance(friend.AI.Situation.InvestigatedThreatPosition, InvestigationPosition) < controller.Distances.ThreatInvestigation) { MarkInvestigated(); break; } } } var isCheckingAThreatInCover = Threat != null && IsThreatInCover && !CanSeeTheThreat; // Check threats if (Threat == null || CanSeeThatNoThreatAtLastPosition || isCheckingAThreatInCover) { var minDist = 100000f; foreach (var alert in Alerts.All) { bool isOk; Actor newThreat = null; if (Threat != null) { if (alert.Actor == null) { isOk = NoThreatVisibilityTime > 6; } else if (alert.Actor.Side != controller.Actor.Side) { isOk = true; newThreat = alert.Actor; } else if (alert.Actor.AI != null) { isOk = NoThreatVisibilityTime > 2 && alert.Actor.AI.Situation.NoThreatVisibilityTime < 1; } else { isOk = NoThreatVisibilityTime > 6; } } else { isOk = true; } if (isOk) { var dist = Vector3.Distance(controller.transform.position, alert.Position); if (dist < alert.Range) { if (dist < minDist) { minDist = dist; IsAlerted = true; HasAnInvestigatedAlert = false; HasInvestigatedTheLatestAlert = false; if (newThreat != null) { ReadEnemyState(newThreat); } else { IsThreatPositionANewAlert = true; SetThreatPosition(alert.Position, true); } } } } } } // React to grenades if (IsNoticingGrenade) { if (GrenadeReaction < float.Epsilon) { IsNoticingGrenade = false; } else { GrenadeReaction -= Time.deltaTime; IsNearGrenade = false; } } else if (IsNearGrenade && !previous.IsNearGrenade) { GrenadeReaction = controller.Fighting.GrenadeReactionTime; IsNoticingGrenade = true; IsNearGrenade = false; } if (IsNearGrenade) { IsAlerted = true; } // React to being alerted. if (IsGettingAlerted) { if (AlertReaction < float.Epsilon) { IsGettingAlerted = false; IsAlerted = true; } else { AlertReaction -= Time.deltaTime; IsAlerted = false; } } else if (IsAlerted && !previous.IsAlerted) { AlertReaction = controller.Fighting.ReactionTime; IsGettingAlerted = true; IsAlerted = false; } if (previous.TargetCover != null && (controller.Motor.LeftCover == previous.TargetCover || controller.Motor.RightCover == previous.TargetCover || controller.Motor.Cover == previous.TargetCover)) { CurrentCover = previous.TargetCover; } else { CurrentCover = controller.Motor.Cover; } CurrentPosition = controller.transform.position; IsGunReady = controller.Motor.IsGunReady && controller.Motor.Gun.Clip >= controller.Motor.Gun.ClipSize * controller.Fighting.ReloadFraction; if (Threat == null || Threat.IsAggressive) { WouldLikeToRetreat = controller.Health.Health <= controller.Fighting.MinHealth; } else { WouldLikeToRetreat = false; } if (IsAlerted) { var couldSeeTheEnemy = CanSeeTheThreat; CanSeeTheThreat = false; if (Threat != null) { var noPatience = NoThreatVisibilityTime > controller.Fighting.Patience; if (couldSeeTheEnemy || updateInDetail) { CanSeeTheThreat = AIUtil.IsInSight(controller, Threat.TopPosition); } if (CanSeeTheThreat) { ReadEnemyState(Threat); if (!couldSeeTheEnemy && noPatience) { IsIrritated = true; } } else { if (noPatience || (NoThreatVisibilityTime > 2 && (!IsThreatInCover || Vector3.Dot(ThreatCoverForward, ThreatGroundPosition - CurrentPosition) > 0))) { if (!IsThreatInCover || noPatience || Vector3.Distance(CurrentPosition, ThreatGroundPosition) < controller.Distances.ThreatInvestigation || AIUtil.IsInSight(controller, (ThreatGroundPosition + ThreatTopPosition) * 0.5f)) { CanSeeThatNoThreatAtLastPosition = true; } } NoThreatVisibilityTime += Time.deltaTime; if (updateInDetail) { // Check friends. foreach (var friend in Actors.All) { if (friend != controller.Actor && friend.Side == controller.Actor.Side && friend.AI != null && friend.AI.IsAlerted && friend.AI.State != AIState.investigate && friend.AI.Situation.Threat == Threat && friend.AI.Situation.HasBetterThreatInfo(friend.AI, ref this)) { var vector = friend.transform.position - controller.transform.position; if (vector.magnitude < controller.View.CommunicationDistance) { TakeEnemyState(friend.AI); } } } } } } if (TargetCover != null && updateInDetail) { var distanceToThreat = Vector3.Distance(TargetPosition, ThreatGroundPosition); IsTargetCoverGood = distanceToThreat >= controller.Distances.MinEnemy && distanceToThreat >= controller.Cover.MinCoverToEnemyDistance && AIUtil.IsGoodAngle(controller, TargetCover, TargetPosition, ThreatGroundPosition, TargetCover.IsTall) && !AIUtil.IsCoverPositionTooCloseToFriends(TargetCover, controller, TargetPosition); } if (updateInDetail) { if (IsThreatInCover) { if (CurrentCover != null && CurrentCover.IsTall) { var aimPoint = Vector3.zero; var isGood = true; if (TargetDirection < 0) { isGood = CurrentCover.IsLeft(Util.AngleOfVector(ThreatStandingTopPosition - CurrentCover.LeftCorner(0)), controller.Motor.CoverSettings.Angles.LeftCorner, false); if (isGood) { aimPoint = CurrentCover.LeftCorner(0, controller.Motor.CoverSettings.CornerOffset.x); } } else { isGood = CurrentCover.IsRight(Util.AngleOfVector(ThreatStandingTopPosition - CurrentCover.RightCorner(0)), controller.Motor.CoverSettings.Angles.LeftCorner, false); if (isGood) { aimPoint = CurrentCover.RightCorner(0, controller.Motor.CoverSettings.CornerOffset.x); } } CanSeeFromCurrentPosition = AIUtil.IsInSight(controller, aimPoint, ThreatStandingTopPosition); } else { CanSeeFromCurrentPosition = AIUtil.IsInSight(controller, CurrentPosition, ThreatStandingTopPosition); } } else { CanSeeFromCurrentPosition = CanSeeTheThreat; } } } else { if (TargetCover != null && updateInDetail) { IsTargetCoverGood = !AIUtil.IsCoverPositionTooCloseToFriends(TargetCover, controller, TargetPosition); } } }