private Vector3 followerPositionAt(FormationType formation, int index) { switch (formation) { case FormationType.Square: { var right = Vector3.Cross(Direction, Vector3.up); Vector3 position; switch (index) { case 0: position = transform.position - (right + Direction).normalized * Square.Distance; break; case 1: position = transform.position - (-right + Direction).normalized * Square.Distance; break; default: position = transform.position - Direction * Square.Distance * index; break; } var ideal = position; AIUtil.GetClosestStandablePosition(ref position); var closest = Vector3.zero; var hasClosest = false; var minDist = 0f; for (int i = 0; i < _followers.Count; i++) { var dist = Vector3.Distance(_followers[i].transform.position, ideal); if (_followers[i]._obstacle != null && _followers[i]._obstacle.enabled && dist <= _followers[i]._obstacle.radius + float.Epsilon) { dist = 0; } if (!hasClosest || dist < minDist) { closest = _followers[i].transform.position; minDist = dist; hasClosest = true; } } if (hasClosest && Vector3.Distance(position, ideal) > minDist) { position = closest; } return(position); } case FormationType.Line: return(_line[index]); default: Debug.Assert(false); return(Vector3.zero); } }
/// <summary> /// React to attacks on other AI. /// </summary> public void OnFriendHit(Actor friend) { if (friend == null) { return; } var vector = friend.transform.position - transform.position; if (vector.magnitude < View.CommunicationDistance) { _situation.IsAlerted = true; } else if (vector.magnitude < View.SightDistance && !IsAlerted) { if (AIUtil.IsInSight(this, friend.TopPosition)) { _situation.IsAlerted = true; } } if (_situation.IsAlerted && _situation.Threat == null) { var ai = friend.GetComponent <AIController>(); if (ai != null) { _situation.TakeEnemyState(ai); } } }
private bool updateIsMelee() { _meleeTarget = null; var weapon = _motor.EquippedWeapon; if (!weapon.HasMelee || weapon.Gun != null) { return(false); } var minDist = 0f; for (int i = 0; i < AIUtil.FindActors(_motor.transform.position, MeleeRadius, _actor); i++) { var actor = AIUtil.Actors[i]; if (actor.Side == _actor.Side) { continue; } var dist = Vector3.Distance(_motor.transform.position, actor.transform.position); if (_meleeTarget == null || dist < minDist) { _meleeTarget = actor; minDist = dist; } } return(_meleeTarget != null); }
/// <summary> /// Sets up the navigation agent to move to the givent position. /// </summary> private void updatePath() { AIUtil.Path(ref _path, transform.position, _target); _pathLength = _path.GetCornersNonAlloc(_pathPoints); _currentPathIndex = 0; if (_pathLength > _pathPoints.Length) { _pathLength = _pathPoints.Length; } if (_pathLength > 1) { var vector = _pathPoints[1] - _pathPoints[0]; var distance = vector.magnitude; if (distance > 0.3f) { updateDirection(vector / distance, true); } } _hasCheckedIfReachable = false; _positionToCheckIfReachable = _target; }
private bool canMoveInDirection(Vector3 vector) { if (AIUtil.IsNavigationBlocked(transform.position, transform.position + vector)) { return(false); } return(true); }
/// <summary> /// Sets up the navigation agent to move to the givent position. /// </summary> private void updatePath() { AIUtil.Path(ref _path, transform.position, _target); _pathLength = _path.GetCornersNonAlloc(_pathPoints); _currentPathIndex = 0; if (_pathLength > 1) { updateDirection((_pathPoints[1] - _pathPoints[0]).normalized, true); } }
/// <summary> /// Catches the animator event during the execution and may perform the action. Returns true if that is the case. /// </summary> public override bool OnFinishAction() { var count = AIUtil.FindActorsIncludingDead(_targetPosition, Radius); for (int i = 0; i < count; i++) { PlayEffect(AIUtil.Actors[i], AIUtil.Actors[i].transform.position); Perform(AIUtil.Actors[i]); } return(true); }
private bool shouldRunTo(Vector3 position) { var distance = Vector3.Distance(transform.position, position); if (distance > WalkDistance || (distance > VerifyDistance && !AIUtil.IsInSight(_actor, position, VerifyDistance, 360))) { return(true); } else { return(false); } }
private bool isValidSide(Vector3 vector) { if (!_motor.IsFree(vector, 0.5f, 0.1f)) { return(false); } if (AIUtil.IsObstructed(transform.position + Vector3.up * 2, _target + Vector3.up * 2, 1000)) { return(false); } return(true); }
/// <summary> /// React to attacks and notify other AI about it. /// </summary> public void OnHit(Hit hit) { _situation.IsAlerted = true; if (hit.Attacker != null && _situation.Threat == null) { var threat = hit.Attacker.GetComponent <Actor>(); if (threat != null && threat.Side != _actor.Side) { _situation.ReadEnemyState(threat); } } AIUtil.NotifyFriends(_actor, "OnFriendHit", _actor); }
private void runTo(Vector3 position) { if (_isKeepingCloseTo && Vector3.Distance(position, _keepCloseTo.Position) > _keepCloseTo.Distance) { position = _keepCloseTo.Position + (position - _keepCloseTo.Position).normalized * _keepCloseTo.Distance; } if (AIUtil.GetClosestStandablePosition(ref position)) { Message("ToRunTo", position); } else { OnPositionUnreachable(position); } }
/// <summary> /// Updates the target situation to approach an enemy that's in cover. /// </summary> public static void ApproachACovered(AIController controller, ref AISituation situation) { var path = new NavMeshPath(); var corners = new Vector3[16]; var resultPosition = situation.ThreatGroundPosition; var resultDistance = 9999f; var coverAngle = Util.AngleOfVector(situation.ThreatCoverForward); for (int a = 0; a < 18; a++) { var angle = coverAngle - 180f + a * 10; var position = situation.ThreatGroundPosition + new Vector3(Mathf.Cos(angle * Mathf.Deg2Rad), 0, Mathf.Sin(angle * Mathf.Deg2Rad)) * controller.Distances.MinEnemy; NavMeshHit hit; if (NavMesh.SamplePosition(position, out hit, 1.0f, NavMesh.AllAreas) && AIUtil.IsInSight(controller, hit.position, situation.ThreatStandingTopPosition)) { NavMesh.CalculatePath(controller.transform.position, hit.position, NavMesh.AllAreas, path); if (path.status == NavMeshPathStatus.PathComplete && !AIUtil.IsPositionTooCloseToFriends(controller, hit.position)) { var dist = 0f; for (int i = 1; i < path.GetCornersNonAlloc(corners); i++) { dist += Vector3.Distance(corners[i - 1], corners[i]); } if (dist < resultDistance) { resultDistance = dist; resultPosition = position; } } } } situation.TargetPosition = resultPosition; situation.TargetCover = null; }
private bool canBeInvestigated(SearchPoint point) { var position = point.Position + Vector3.up * VerifyHeight; var distanceToPoint = Vector3.Distance(transform.position, position); var checkDistance = VerifyDistance; if (point.Visibility < checkDistance) { checkDistance = point.Visibility; } if (distanceToPoint < checkDistance && (distanceToPoint < 1 || AIUtil.IsInSight(_actor, position, checkDistance, FieldOfView))) { return(!point.RequiresReaching || distanceToPoint < 1.1f); } return(false); }
/// <summary> /// Notified by the brains of a new threat position. /// </summary> public void OnThreatPosition(Vector3 position) { if (!_isAssaulting || !isActiveAndEnabled) { return; } _threatPosition = position; if (Vector3.Distance(position, _targetPosition) > 0.5f) { _targetPosition = position; if (AIUtil.GetClosestStandablePosition(ref position)) { Message("ToRunTo", position); } else { OnPositionUnreachable(position); } } }
/// <summary> /// Checks if the given actor is field of view. Result is immediate, does not execute any events or messages. /// </summary> public bool CheckVisibility(Actor actor) { var viewDistance = actor.GetViewDistance(Distance, _isAlerted); return(AIUtil.IsInSight(_actor, actor.TopPosition, viewDistance, FieldOfView, ObstacleIgnoreDistance)); }
private void Update() { if (_situation.IsAlerted) { if (!_hasAddedToAlertedControllers) { _hasAddedToAlertedControllers = true; _alertedControllers.Add(this); } } else if (_hasAddedToAlertedControllers) { _alertedControllers.Remove(this); } _stateTime += Time.deltaTime; _agent.updatePosition = false; _agent.updateRotation = false; _agent.updateUpAxis = false; _agent.autoRepath = true; _agent.autoBraking = false; if (!_motor.IsAlive) { _situation.TargetCover = null; } if (_lastTargetCover != _situation.TargetCover) { if (_lastTargetCover != null) { _lastTargetCover.UnregisterUser(_actor); } _lastTargetCover = _situation.TargetCover; if (_lastTargetCover != null) { _lastTargetCover.RegisterUser(_actor); } } if (_isWaitingForPath && !_agent.pathPending) { _pathLength = _agent.path.GetCornersNonAlloc(_path); _currentPathIndex = 0; _isWaitingForPath = false; } if (!_motor.IsAlive) { _state = AIState.none; _agent.enabled = false; return; } else { _agent.enabled = true; } if (_motor.PotentialCover != null) { _motor.InputTakeCover(); } if (_situation.IsAlerted) { if (!_motor.IsInCover) { _motor.InputAim(); } // Make the AI pick up a weapon. if (Fighting.WeaponToUse <= 0) { _motor.InputWeapon(0); } else if (Fighting.WeaponToUse - 1 < _motor.Weapons.Length) { _motor.InputWeapon(Fighting.WeaponToUse); } else { _motor.InputWeapon(_motor.Weapons.Length); } _situation.IsAllowedToBeAggressive = true; /// /// POTENTIALLY FORGET ABOUT THE ENEMY. CHECK IF CAN BE AGGRESSIVE. /// if (_situation.Threat != null) { if (_situation.Threat.IsAlive) { if (!_situation.CanSeeTheThreat && _situation.NoThreatTimer > View.EnemySustainTime) { _situation.RemoveEnemyState(); } else { if (_registeredGroup != null) { _registeredGroup.MarkAsPotentialAggressive(this); _situation.IsAllowedToBeAggressive = _registeredGroup.IsAggressive(this); } } } else { _situation.RemoveEnemyState(); } } /// /// AIM AND FIRE /// if (_situation.Threat != null && (_situation.CanSeeTheThreat || _situation.NoThreatTimer < View.SightSustainTime)) { // Process grenade throwing if (_situation.ThrownGrenadeCount < Grenades.GrenadeCount) { var doThrow = false; if (_situation.IsAllowedToBeAggressive) { if (_hasThrowFirstGrenade) { if (_grenadeTimer < Grenades.Interval) { _grenadeTimer += Time.deltaTime; } else { doThrow = true; } } else { if (_grenadeTimer < Grenades.FirstGrenadeDelay) { _grenadeTimer += Time.deltaTime; } else { doThrow = true; } } } if (doThrow && _motor.PotentialGrenade != null) { if (_grenadeCheckTimer <= float.Epsilon) { GrenadeDescription desc; desc.Gravity = _motor.Grenade.Gravity; desc.Duration = _motor.PotentialGrenade.Timer; desc.Bounciness = _motor.PotentialGrenade.Bounciness; var length = GrenadePath.Calculate(GrenadePath.Origin(_motor, Util.AngleOfVector(_situation.ThreatGroundPosition - transform.position)), _situation.ThreatGroundPosition, _motor.Grenade.MaxVelocity, desc, _grenadePath, _motor.Grenade.Step); if (Vector3.Distance(_grenadePath[length - 1], _situation.ThreatGroundPosition) > Grenades.MaxRadius || Vector3.Distance(_grenadePath[length - 1], _situation.CurrentPosition) < Grenades.AvoidDistance) { _grenadeCheckTimer = Grenades.CheckInterval; } else { _motor.InputThrowGrenade(_grenadePath, length, _motor.Grenade.Step); _situation.ThrownGrenadeCount++; _grenadeTimer = 0; _hasThrowFirstGrenade = true; } } else { _grenadeCheckTimer -= Time.deltaTime; } } else { _grenadeCheckTimer = 0; } } // Position in world space that is best fit to aim at. var perfectTarget = _situation.ThreatGroundPosition + (_situation.ThreatTopPosition - _situation.ThreatGroundPosition) * 0.7f; if (_situation.IsThreatInCover) { var dot = Vector3.Dot(_situation.ThreatCoverForward, (_situation.ThreatGroundPosition - transform.position).normalized); if (dot < -0.1f) { perfectTarget = _situation.ThreatStandingTopPosition; } } // Make the AI look at the target. { var vector = perfectTarget - _motor.transform.position; vector.y = 0; if (vector.magnitude < 2) { _motor.SetLookTarget(perfectTarget + vector * 2); } else { _motor.SetLookTarget(perfectTarget); } _motor.SetBodyLookTarget(perfectTarget); } // Aim the gun at the inprecise target. var targetRadius = Fighting.TargetRadius.Get(Vector3.Distance(_situation.ThreatGroundPosition, transform.position)); _motor.SetFireTarget(perfectTarget + new Vector3(UnityEngine.Random.Range(-1, 1) * targetRadius, UnityEngine.Random.Range(-1, 1) * targetRadius, UnityEngine.Random.Range(-1, 1) * targetRadius)); // We want AI too look at the corner when standing near it. if (_motor.IsInTallCover) { if (_situation.TargetDirection < 0) { _motor.InputStandLeft(); } else if (_situation.TargetDirection > 0) { _motor.InputStandRight(); } } // Fire { var isAllowedToFire = _situation.IsAllowedToBeAggressive; if (Behaviour.OnlyFireWhenSeen || !_situation.IsAllowedToBeAggressive) { var up = Camera.main.WorldToViewportPoint(transform.position); var down = Camera.main.WorldToViewportPoint(transform.position + Vector3.up * 2); if ((up.x < 0 || up.y < 0 || up.x > 1 || up.y > 1 || up.z < 0) && (down.x < 0 || down.y < 0 || down.x > 1 || down.y > 1 || down.z < 0)) { isAllowedToFire = false; } else { isAllowedToFire = true; } } if (isAllowedToFire) { var burst = Behaviour.IsFightingUsingCovers ? CoveredFightingBursts : CoveredApproachBursts; var isDumb = _state == AIState.fireInCover && (_stateTime <= burst.IntroDuration || _stateTime >= burst.IntroDuration + burst.Duration); var isWalking = _state != AIState.takeCover && _state != AIState.approach && _state != AIState.retreat; // Check if can fire right now. if (Behaviour.CanFire && (!isWalking || _walkingBurstWait < WalkingBursts.Duration) && (!_situation.IsTargetCoverGood || (_state != AIState.hideInCover && (_state != AIState.takeCover || _motor.Cover == null)))) { var aimPoint = transform.position; if (_motor.IsInTallCover && _situation.IsTargetCoverGood) { if (_situation.TargetDirection < 0) { aimPoint = _motor.Cover.LeftCorner(transform.position.y, _motor.CoverSettings.CornerOffset.x); } else { aimPoint = _motor.Cover.RightCorner(transform.position.y, _motor.CoverSettings.CornerOffset.x); } } // See if the motor can hit the standing enemy after potentially peeking. if (AIUtil.IsInSight(this, aimPoint, _situation.ThreatStandingTopPosition)) { if (_state == AIState.fireInCover) { _motor.InputAim(); } if (!isDumb) { _motor.InputFire(); } } } } _lookPointTimer = 0; } } else { _motor.SetFireTarget(_situation.ThreatGroundPosition); _motor.SetLookTarget(_situation.ThreatGroundPosition); _motor.SetBodyLookTarget(_situation.ThreatGroundPosition); lookAround(_situation.ThreatGroundPosition - _motor.transform.position); } } else { _motor.InputWeapon(0); if (_state == AIState.patrol || _state == AIState.patrolPause) { Vector3 forward; if (Waypoints.Length > 1) { var next = Waypoints[_situation.PatrolPoint]; forward = next.Position - transform.position; if (forward.magnitude < 0.5f) { var previous = _situation.PatrolPoint == 0 ? Waypoints[Waypoints.Length - 1] : Waypoints[_situation.PatrolPoint - 1]; var line = next.Position - previous.Position; if (Vector2.Dot(line, forward) > 0) { forward = line; } } } else { forward = transform.forward; } lookAround(forward); } else { _lookPointTimer = 0; } } var positionToMoveTo = transform.position; /// /// STATE MANAGEMENT /// { var updateSituationInDetail = false; { // Update the update delay timer. _updateTimer -= Time.deltaTime; if (_updateTimer < 0) { _updateTimer = Fighting.ReactionTime; updateSituationInDetail = true; } } // Update standing burst timer. { _walkingBurstWait += Time.deltaTime; if (_walkingBurstWait > WalkingBursts.Duration + WalkingBursts.Wait) { _walkingBurstWait = 0; } } { _situation.Update(this, _previousSituation, updateSituationInDetail); if (!isStateStillValid(_state, _stateTime, _situation, _situation)) { updateSituation(calcState(_situation, _state, _stateTime)); } } } /// /// WALK /// if (!_motor.IsInCornerAimState) { if (_state == AIState.takeCover || _state == AIState.approach || _state == AIState.investigate || _state == AIState.retreat || _state == AIState.patrol || _state == AIState.avoidGrenade) { var toTarget = _situation.TargetPosition - transform.position; // If patrolling, set the body to rotate towards the target. if (_state == AIState.patrol) { if (toTarget.magnitude > 0.1f) { var normalized = toTarget.normalized; var position = transform.position + normalized * 4; var dot = Vector3.Dot(toTarget.normalized, _motor.transform.forward); if (Waypoints[_situation.PatrolPoint].Run) { _motor.SetBodyLookTarget(position, 8); } else { if (dot > 0) { _motor.SetBodyLookTarget(position, 1.6f); } else if (dot > -0.5f) { _motor.SetBodyLookTarget(position, 2.5f); } else { _motor.SetBodyLookTarget(position, 4f); } } } } // Move the last meter without using the agent. if (toTarget.magnitude > 1.0f) { var vectorToAgent = _agent.nextPosition - transform.position; var distanceToAgent = vectorToAgent.magnitude; if (distanceToAgent > float.Epsilon) { vectorToAgent /= distanceToAgent; } // If agent moved too far, reset it. if (distanceToAgent > 1f) { updateAgent(_situation.TargetPosition); } float walkSpeed; if (_state == AIState.patrol && !Patrol.IsAlwaysRunning && (Waypoints.Length == 0 || !Waypoints[_situation.PatrolPoint].Run)) { walkSpeed = 0.5f; } else { walkSpeed = 1.0f; } var point = _currentPathIndex + 1 < _pathLength ? _path[_currentPathIndex + 1] : _situation.TargetPosition; var vectorToPoint = point - transform.position; var distanceToPoint = vectorToPoint.magnitude; if (distanceToPoint > float.Epsilon) { vectorToPoint /= distanceToPoint; } if (distanceToPoint < 0.1f && _currentPathIndex + 1 < _pathLength) { _currentPathIndex++; } _agent.speed = 0.1f; _motor.InputMovement(new CharacterMovement(vectorToPoint, walkSpeed)); } else { if (_state == AIState.takeCover) { _motor.InputImmediateCoverSearch(); _motor.InputTakeCover(); } if (toTarget.magnitude > 0.05f) { _motor.InputMovement(new CharacterMovement(toTarget.normalized, 0.5f)); } else { _motor.transform.position = _situation.TargetPosition; } } } } }
/// <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); } }
private void Update() { _isMoving = false; if (_obstacle != null && _obstacle.enabled) { _obstacle.enabled = false; } if (_motor == null || !_motor.IsAlive) { if (_wasMoving) { Message("OnStopMoving"); _wasMoving = false; } _mode = Mode.none; return; } if (_isCrouching) { _motor.InputCrouch(); } if (_isAvoidingMover) { if (_avoidingTimer > float.Epsilon) { _avoidingTimer -= Time.deltaTime; move(_avoidDirection, 1, false); } else { _isAvoidingMover = false; } } if (_mode == Mode.none) { if (_wasMoving) { Message("OnStopMoving"); _wasMoving = false; } checkIncomingCollisions(Vector3.zero, 1); return; } if (DebugDestination) { Debug.DrawLine(transform.position, _target, Color.blue); } if (DebugPath) { for (int i = 0; i < _pathLength - 1; i++) { if (i == _currentPathIndex) { Debug.DrawLine(_pathPoints[i], _pathPoints[i + 1], Color.cyan); Debug.DrawLine(_pathPoints[i + 1], _pathPoints[i + 1] + Vector3.up, Color.cyan); } else { Debug.DrawLine(_pathPoints[i], _pathPoints[i + 1], Color.green); } } } var vector = _target - transform.position; vector.y = 0; var direction = vector.normalized; var side = Vector3.Cross(direction, Vector3.up); if (_futureCircleDirection == 0) { _circleWait = 0; } switch (_mode) { case Mode.inDirection: if (!checkIncomingCollisions(_direction, _speed)) { if (canMoveInDirection(_direction)) { move(_direction, _speed, true); } _inDirectionLeft -= Time.deltaTime; if (_inDirectionLeft <= float.Epsilon) { _mode = Mode.none; } } break; case Mode.toPosition: var vectorToPath = Vector3.zero; var isCloseToThePath = false; var distanceToPath = 0f; if (_currentPathIndex <= _pathLength - 1) { vectorToPath = Util.VectorToSegment(transform.position, _pathPoints[_currentPathIndex], _pathPoints[_currentPathIndex + 1]); distanceToPath = vectorToPath.magnitude; isCloseToThePath = distanceToPath < 0.5f; } if (!isCloseToThePath) { updatePath(); } var isLastStepOnPartialPath = _currentPathIndex >= _pathLength - 2 && _path.status == NavMeshPathStatus.PathPartial; if (_path.status != NavMeshPathStatus.PathInvalid && !_hasCheckedIfReachable) { if (_pathLength == 0 || Vector3.Distance(_pathPoints[_pathLength - 1], _positionToCheckIfReachable) >= 0.2f) { Message("OnPositionUnreachable", _positionToCheckIfReachable); } _hasCheckedIfReachable = true; } if (vector.magnitude >= 0.3f && !isLastStepOnPartialPath) { if (_path.status == NavMeshPathStatus.PathInvalid || _pathLength == 0) { updatePath(); } var point = _currentPathIndex + 1 < _pathLength ? _pathPoints[_currentPathIndex + 1] : _target; if (!AIUtil.IsPositionOnNavMesh(point)) { updatePath(); } direction = point - transform.position; direction.y = 0; var distanceToPoint = direction.magnitude; if (distanceToPoint > float.Epsilon) { direction /= distanceToPoint; } if (!checkIncomingCollisions(direction, _speed)) { if (distanceToPoint < 0.2f && _currentPathIndex + 1 < _pathLength) { var index = _currentPathIndex; if (distanceToPoint > 0.07f && _currentPathIndex + 2 < _pathLength) { if (Vector3.Dot(point - transform.position, _pathPoints[_currentPathIndex + 2] - transform.position) <= 0.1f) { _currentPathIndex++; } } else { _currentPathIndex++; } } if (distanceToPath > 0.12f) { direction = (direction + vectorToPath).normalized; } if (Vector3.Dot(direction, _direction) < 0.9f) { updateDirection(direction, false); } move(direction, _speed, false); } } else { if (vector.magnitude > 0.03f) { if (!checkIncomingCollisions(direction, _speed)) { if (vector.magnitude < 0.2f) { _motor.InputImmediateCoverSearch(); transform.position = Util.Lerp(transform.position, _target, 6); } else { if (_motor.IsInCover) { move(direction, 1.0f, false); } else { move(direction, 0.5f, false); } } } } else { _motor.transform.position = _target; _mode = Mode.none; } } break; case Mode.fromPosition: _pathLength = 0; if (!checkIncomingCollisions(-direction, _speed)) { direction = -direction; if (canMoveInDirection(direction)) { _motor.InputMovement(new CharacterMovement(direction, 1.0f)); _isMoving = true; } else { if (_side == 0) { if (Random.Range(0, 10) < 5 && _motor.IsFreeToMove(side, 0.5f, 0.25f)) { _side = 1; } else { _side = -1; } } if (!canMoveInDirection(side * _side)) { if (!_motor.IsFreeToMove(-side * _side, 0.5f, 0.25f)) { Message("OnMoveFromFail"); } else { _side = -_side; } } move(side * _side, 1.0f, true); } updateDirection(direction, false); if (_isRunningAwayTemp) { _runningAwayFramesLeft--; if (_runningAwayFramesLeft <= 0) { ToStopMoving(); } } } break; case Mode.circle: _pathLength = 0; if (_futureCircleDirection != 0) { if (_circleWait > float.Epsilon) { _circleWait -= Time.deltaTime; } if (_circleWait < float.Epsilon) { _circleWait = 0; _side = _futureCircleDirection; _futureCircleDirection = 0; } } if (_futureCircleDirection == 0) { if (_side == 0) { if (Random.Range(0, 10) < 5 && canMoveInDirection(side)) { _side = 1; } else { _side = -1; } } if (!canMoveInDirection(side * _side)) { if (!canMoveInDirection(-side * _side)) { Message("OnCircleFail"); } else { _futureCircleDirection = -_side; _circleWait = CircleDelay; } } direction = side * _side; if (!checkIncomingCollisions(direction, _speed)) { move(direction, 1.0f, true); updateDirection(direction, false); } } break; } if (_isMoving && !_wasMoving) { Message("OnMoving"); } else if (!_isMoving && _wasMoving) { Message("OnStopMoving"); } _wasMoving = _isMoving; }
private bool isValidCover(Cover cover, Vector3 position, int direction, bool checkPath, bool avoidPivot = true) { if (cover == _unreachableCover) { return(false); } if (_isKeepingCloseTo && Vector3.Distance(position, _keepCloseTo.Position) > _keepCloseTo.Distance) { return(false); } if (!_hasPivot) { if (!AIUtil.IsCoverPositionFree(cover, position, 1, _actor)) { return(false); } return(true); } if (!_hasCachedEnemies) { _cachedEnemyCount = 0; _hasCachedEnemies = true; var totalActorCount = AIUtil.FindActors(position, MinDefenselessDistance, _actor); if (totalActorCount > 0) { var enemyCount = 0; for (int i = 0; i < totalActorCount; i++) { if (AIUtil.Actors[i].Side != _actor.Side) { enemyCount++; } } if (enemyCount > 0) { if (_cachedEnemies == null || _cachedEnemies.Length < enemyCount) { _cachedEnemies = new Actor[enemyCount]; } var index = 0; for (int i = 0; i < totalActorCount; i++) { if (AIUtil.Actors[i].Side != _actor.Side) { _cachedEnemies[index++] = AIUtil.Actors[i]; } } _cachedEnemyCount = index; } } } for (int i = 0; i < _cachedEnemyCount; i++) { var enemy = _cachedEnemies[i]; if (enemy.Side != _actor.Side) { var enemyPosition = enemy.transform.position; var distance = Vector3.Distance(position, enemyPosition); if (distance < AvoidDistance) { return(false); } if (!AIUtil.IsGoodAngle(MaxTallCoverThreatAngle, MaxLowCoverThreatAngle, cover, position, enemyPosition, cover.IsTall)) { return(false); } } } if (_isPivotThreat) { var distance = Vector3.Distance(position, _pivotPosition); if (_hasMaxPivotDistance && distance > _maxPivotDistance) { return(false); } var aimPosition = position; if (AIUtil.IsObstructed(aimPosition + (_actor.StandingTopPosition - transform.position), _pivotPosition + Vector3.up * 2)) { return(false); } } else { var distance = Vector3.Distance(position, _pivotPosition); if (_hasMaxPivotDistance && distance > _maxPivotDistance) { return(false); } if (!AIUtil.IsGoodAngle(MaxDefenseAngle, MaxDefenseAngle, cover, _pivotPosition, position, cover.IsTall)) { return(false); } } if (!AIUtil.IsCoverPositionFree(cover, position, 1, _actor)) { return(false); } if (checkPath) { if (NavMesh.CalculatePath(transform.position, position, 1, _path)) { if (avoidPivot) { var count = _path.GetCornersNonAlloc(_corners); for (int i = 0; i < count; i++) { var a = i == 0 ? transform.position : _corners[i - 1]; var b = _corners[i]; var closest = Util.FindClosestToPath(a, b, _pivotPosition); if (Vector3.Distance(closest, _pivotPosition) < AvoidDistance) { return(false); } } } } else { return(false); } } return(true); }
private bool verify(Vector3 position) { var distance = Vector3.Distance(transform.position, position); return((_cover != null && AIUtil.IsInSight(_actor, position + Vector3.up * VerifyHeight, _verifyDistance, FieldOfView)) || distance < VerifyRadius || (distance < _verifyDistance && _verifyDistance < VerifyRadius)); }
/// <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"); } } } }
/// <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; } }
/// <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++; } } }
private void Update() { if (!_actor.IsAlive) { return; } _wait -= Time.deltaTime; if (_wait > float.Epsilon) { _oldVisible.Clear(); _oldVisibleHash.Clear(); for (int i = 0; i < _visible.Count; i++) { var actor = _visible[i]; if (actor != null) { _oldVisible.Add(actor); _oldVisibleHash.Add(actor); } } for (int i = 0; i < _oldVisible.Count; i++) { var actor = _oldVisible[i]; if (!actor.IsAlive) { _visible.Remove(actor); _visibleHash.Remove(actor); if (!_seenDeadHash.Contains(actor)) { _seenDeadHash.Add(actor); Message("OnSeeDeath", actor); } } } return; } _wait = Random.Range(UpdateDelay * 0.8f, UpdateDelay * 1.2f); _oldVisible.Clear(); _oldVisibleHash.Clear(); for (int i = 0; i < _visible.Count; i++) { var actor = _visible[i]; if (actor != null) { _oldVisible.Add(actor); _oldVisibleHash.Add(actor); } } _visible.Clear(); _visibleHash.Clear(); var count = AIUtil.FindActorsIncludingDead(_actor.TopPosition, Distance, _actor); for (int i = 0; i < count; i++) { var actor = AIUtil.Actors[i]; if (CheckVisibility(actor)) { if (actor.IsAlive) { _visible.Add(actor); _visibleHash.Add(actor); } else { if (!_seenDeadHash.Contains(actor)) { _seenDeadHash.Add(actor); Message("OnSeeDeath", actor); } // A hack, should fix in some other way else if (_brain != null && _brain.Threat == actor) { _brain.OnSeeDeath(actor); } } } } for (int i = 0; i < _oldVisible.Count; i++) { var actor = _oldVisible[i]; if (!_visibleHash.Contains(actor)) { Message("OnUnseeActor", actor); } } for (int i = 0; i < _visible.Count; i++) { var actor = _visible[i]; if (!_oldVisibleHash.Contains(actor)) { Message("OnSeeActor", actor); } } }
private void Update() { if ((Disabler != null && Disabler.activeSelf) || EventSystem.current.IsPointerOverGameObject()) { hideMarker(); hideOutline(ref _targetOutline); hideSphere(); if (Target == null) { hideOutline(ref _performerOutline); } else { showOutline(ref _performerOutline, Target, SelectColor); } return; } var camera = _camera; if (camera == null) { camera = Camera.main; } if (camera == null) { return; } var mouse = Input.mousePosition; mouse.z = camera.nearClipPlane; var near = camera.ScreenToWorldPoint(mouse); mouse.z = camera.farClipPlane; var far = camera.ScreenToWorldPoint(mouse); var hit = Util.GetClosestNonActorHit(near, far, 1); var targetActor = AIUtil.FindClosestActorIncludingDead(hit, 0.7f); var isMouseDown = Input.GetMouseButtonDown(0); if (Target == null) { if (targetActor != null && targetActor.Side == Side && targetActor.IsAlive) { showOutline(ref _performerOutline, targetActor, PickColor); if (isMouseDown) { Target = targetActor; isMouseDown = false; } } else { hideOutline(ref _performerOutline); } } else if (Target != null) { showOutline(ref _performerOutline, Target, SelectColor); } else { hideOutline(ref _performerOutline); } AIActions targetActions = null; if (Target != null) { targetActions = Target.GetComponent <AIActions>(); } if (targetActions) { var target = hit; AIUtil.GetClosestStandablePosition(ref target); var isActor = targetActor != null; var isSelf = isActor && targetActor == Target; var isAlly = isActor && targetActor.Side == Target.Side; var isEnemy = isActor && targetActor.Side != Target.Side; var isDead = isActor && !targetActor.IsAlive; var isLocation = Vector3.Distance(target, hit) < 0.5f; AIAction action = null; var isTargetingActor = false; if (_forcedAction != null) { if (_forcedAction.CanTargetGround && isLocation) { action = _forcedAction; } else if ((!isDead || !_forcedAction.ShouldIgnoreDead) && ((isSelf && _forcedAction.CanTargetSelf) || (isAlly && _forcedAction.CanTargetAlly) || (isEnemy && _forcedAction.CanTargetEnemy))) { action = _forcedAction; isTargetingActor = true; } } else { for (int i = 0; i < targetActions.Actions.Length; i++) { var a = targetActions.Actions[i]; if (a.CanTargetGround && isLocation) { action = a; break; } else if ((!isDead || !a.ShouldIgnoreDead) && ((isSelf && a.CanTargetSelf) || (isAlly && a.CanTargetAlly) || (isEnemy && a.CanTargetEnemy))) { action = a; isTargetingActor = true; break; } } } if (action != null) { if (isTargetingActor) { if (isSelf) { hideOutline(ref _performerOutline); } showOutline(ref _targetOutline, targetActor, action.UIColor); hideMarker(); hideSphere(); } else { if (action.UIRadius > 0.001f) { showSphere(target, action.UIColor, action.UIRadius); hideOutline(ref _targetOutline); hideMarker(); } else { showMarker(target, action.UIColor); hideOutline(ref _targetOutline); hideSphere(); } } } if (isMouseDown) { isMouseDown = false; if (action != null) { if (action.CanTargetGround) { targetActions.Execute(action, target); } else { targetActions.Execute(action, targetActor); } if (_forcedAction != null) { _forcedAction = null; } } Target = null; } else if (Input.GetMouseButtonDown(1)) { Target = null; } } else { hideOutline(ref _targetOutline); hideMarker(); hideSphere(); } }
private void Update() { if (!_actor.IsAlive) { return; } var wantsToAlwaysAim = AlwaysAim && _motor.Cover == null; if (wantsToAlwaysAim) { Equip(_motor); } if (_motor.Gun == null) { return; } if (_isReloading) { _isReloading = !_motor.IsGunReady; } else if (_motor.Gun != null && _motor.Gun.IsClipEmpty) { _isReloading = true; _motor.InputReload(); } var isObstructed = false; if (_isFiring && _isAimingAtAPosition) { var origin = transform.position; if (_motor.Cover != null && _motor.Cover.IsTall) { if (_motor.CoverDirection > 0 && _motor.IsNearRightCorner) { origin = _motor.Cover.RightCorner(transform.position.y, _motor.CoverSettings.CornerOffset.x); } else if (_motor.CoverDirection < 0 && _motor.IsNearLeftCorner) { origin = _motor.Cover.LeftCorner(transform.position.y, _motor.CoverSettings.CornerOffset.x); } } var start = origin + Vector3.up * 2; var distance = Vector3.Distance(start, _aim); if (distance > 1.5f) { isObstructed = AIUtil.IsObstructed(start, _aim, distance - 1.5f); } } if (_motor.Cover == null && (_isAiming || wantsToAlwaysAim) && !_isReloading) { _motor.InputAim(); } if (_isFiring && !_isReloading && !isObstructed) { var cycleDuration = _motor.Cover != null ? CoverBursts.CycleDuration : Bursts.CycleDuration; if (_fireCycle >= cycleDuration) { if (_isFiringABurst) { _gunBurstsWereCalculatedFor = _motor.Gun; _burstBulletCount = _bulletCountAtStart - _motor.Gun.Clip; } _isFiringABurst = false; _fireCycle -= cycleDuration; if (_gunBurstsWereCalculatedFor == _motor.Gun && _motor.Gun.Clip < _burstBulletCount) { _isReloading = true; _motor.InputReload(); } } if (_motor.Cover != null) { if (_fireCycle >= CoverBursts.Wait) { _motor.InputAim(); if (_fireCycle >= CoverBursts.Wait + CoverBursts.IntroDuration && _fireCycle < CoverBursts.CycleDuration - CoverBursts.OutroDuration) { if (!_isFiringABurst) { _bulletCountAtStart = _motor.Gun.Clip; _isFiringABurst = true; } if (_motor.IsGunReady) { _motor.InputFireOnCondition(_actor.Side); } } } } else { _motor.InputAim(); if (_fireCycle >= Bursts.Wait) { if (!_isFiringABurst) { _bulletCountAtStart = _motor.Gun.Clip; _isFiringABurst = true; } if (_motor.IsGunReady) { _motor.InputFireOnCondition(_actor.Side); } } } _fireCycle += Time.deltaTime; } else { _fireCycle = 0; _isFiringABurst = false; } }
private bool checkIncomingCollisions(Vector3 ownMovement, float speed) { var count = AIUtil.FindActors(transform.position, ObstructionRadius, _actor); var ownMovementMagnitude = ownMovement.magnitude; var hasOwnMovement = ownMovementMagnitude > 0.25f; var ownDirection = ownMovement / ownMovementMagnitude; var closestDot = 0f; var closestVector = Vector3.zero; var closestDirection = Vector3.zero; var closestPosition = Vector3.zero; var isClosestPrimary = false; var isClosestStatic = false; var hasClosest = false; for (int i = 0; i < count; i++) { var other = AIUtil.Actors[i]; if (other.Side != _actor.Side && other.IsAggressive && _actor.IsAggressive) { continue; } Vector3 velocity; float magnitude; var movement = _movements.ContainsKey(other) ? _movements[other] : null; var isStatic = false; if (movement != null && movement._isAvoidingMover) { velocity = movement._avoidDirection; magnitude = 1; } else { velocity = other.Motor.MovementDirection; magnitude = velocity.magnitude; if (magnitude < 0.1f) { var body = other.Body; if (body == null) { continue; } velocity = body.velocity; magnitude = velocity.magnitude; if (magnitude < 0.1f) { if (hasOwnMovement) { isStatic = true; velocity = -ownMovement; magnitude = 1; } else { continue; } } } } var direction = velocity / magnitude; if (hasOwnMovement && Vector3.Dot(ownDirection, direction) > -0.5f) { continue; } var vector = (transform.position - other.transform.position).normalized; var dot = Vector3.Dot(direction, vector); if (dot < 0.7f) { continue; } var isPrimary = (movement == null ? true : !movement._isAvoidingMover) && !isStatic; if (!hasClosest || (isClosestStatic && !isStatic) || (isPrimary && !isClosestPrimary) || (isPrimary && dot > closestDot) || (!isPrimary && !isClosestPrimary && dot > closestDot)) { hasClosest = true; isClosestPrimary = isPrimary; isClosestStatic = isStatic; closestPosition = other.transform.position; closestDirection = direction; closestVector = vector; closestDot = dot; } } if (hasClosest) { if (!isClosestPrimary && !isClosestStatic && !hasOwnMovement && canMoveInDirection(closestVector)) { return(avoid(closestVector, speed)); } var point = Util.FindClosestToPath(closestPosition, closestPosition + closestDirection * 100, transform.position); var vector = transform.position - point; var distance = vector.magnitude; if (distance < 0.1f) { var right = Vector3.Cross(closestVector, Vector3.up); var left = -right; if (hasOwnMovement && isClosestStatic) { right = (right + ownMovement).normalized; left = (left + ownMovement).normalized; } if (canMoveInDirection(right)) { return(avoid(right, speed)); } if (canMoveInDirection(left)) { return(avoid(left, speed)); } } else { var direction = vector / distance; if (hasOwnMovement && isClosestStatic) { direction = (direction + ownMovement).normalized; } if (canMoveInDirection(direction)) { return(avoid(direction, speed)); } } if (isClosestPrimary && !isClosestStatic && !hasOwnMovement && canMoveInDirection(closestVector)) { return(avoid(closestVector, speed)); } } return(false); }
private void Update() { if (_autoWait > float.Epsilon) { _autoWait -= Time.deltaTime; } if (_checkWait > float.Epsilon) { _checkWait -= Time.deltaTime; } if (_coverTimer > float.Epsilon) { _coverTimer -= Time.deltaTime; } if (_active != null && (!_active.Update() || (!_active.HasNoTimeout && (Time.timeSinceLevelLoad - _actionStart) > Timeout))) { end(); } if (_actor.Cover == null) { _isInCover = false; _coverTimer = 0; } else if (!_isInCover) { _coverTimer = CoverDelay; _isInCover = true; } if (_active == null && _autoWait <= float.Epsilon && _checkWait <= float.Epsilon && _brain.enabled && _brain.State != FighterState.process && _actor.IsAlive && _coverTimer <= float.Epsilon && (!WaitForCoverActions || !_isTakingCover)) { _checkWait = 0.5f; _nearbyActors.Clear(); { var count = AIUtil.FindActorsIncludingDead(transform.position, AutoDistance); for (int i = 0; i < count; i++) { _nearbyActors.Add(AIUtil.Actors[i]); } } var canUseAttackAction = !AutoAttackOnlyFromCover || _actor.Cover != null; AIAction bestSingleAction = null; Actor bestSingleActionTarget = null; /////////////////////////////////////////////////////// // // CONSIDER MULTIPLE TARGET ACTIONS // /////////////////////////////////////////////////////// AIAction bestMultipleAction = null; int bestMultipleActionTargetCount = 0; for (int i = 0; i < Actions.Length; i++) { var action = Actions[i]; if (action.Wait > float.Epsilon || !action.Auto || action.NeedsSingleTargetActor || action.NeedsTargetLocation || action.NeedsOnlySelf) { continue; } int count = 0; for (int j = 0; j < Actors.Count; j++) { var actor = Actors.Get(j); if (!actor.IsAlive && action.ShouldIgnoreDead) { continue; } if (actor.Side == _actor.Side) { if (((actor == _actor && action.CanTargetSelf) || action.CanTargetAlly) && action.IsNeededFor(actor)) { count++; } } else if (canUseAttackAction && action.CanTargetEnemy && action.IsNeededFor(actor) && (!AutoAttackOnlyFromCover || !action.WillMoveForActor(actor))) { count++; } } if (count > bestMultipleActionTargetCount) { bestMultipleAction = action; bestMultipleActionTargetCount = count; } } if (bestMultipleAction != null) { _autoWait = AutoCooldown; Execute(bestMultipleAction); return; } /////////////////////////////////////////////////////// // // CONSIDER SELF ACTIONS // /////////////////////////////////////////////////////// for (int i = 0; i < Actions.Length; i++) { var action = Actions[i]; if (action.Wait > float.Epsilon || !action.Auto || !action.NeedsOnlySelf) { continue; } if (action.CanTargetSelf && action.IsNeededFor(_actor)) { if (_actor == Priority) { _autoWait = AutoCooldown; Execute(action, _actor); return; } bestSingleAction = action; bestSingleActionTarget = _actor; break; } } /////////////////////////////////////////////////////// // // CONSIDER SINGLE TARGET ACTIONS // /////////////////////////////////////////////////////// for (int i = 0; i < Actions.Length; i++) { var action = Actions[i]; if (action.Wait > float.Epsilon || !action.Auto || !action.NeedsSingleTargetActor) { continue; } Actor best = null; var bestDistance = 0f; var isBestSelf = false; for (int j = 0; j < _nearbyActors.Count; j++) { var actor = _nearbyActors[j]; if (!actor.IsAlive && action.ShouldIgnoreDead) { continue; } if (actor == _actor) { if (action.CanTargetSelf && action.IsNeededFor(actor)) { if (actor == Priority) { _autoWait = AutoCooldown; Execute(action, actor); return; } if (best == null) { best = actor; bestDistance = 0; isBestSelf = true; } } } else if (actor.Side == _actor.Side) { if (action.CanTargetAlly && action.IsNeededFor(actor)) { if (actor == Priority) { _autoWait = AutoCooldown; Execute(action, actor); return; } var distance = Vector3.Distance(transform.position, actor.transform.position); if (best == null || isBestSelf || distance < bestDistance) { best = actor; bestDistance = distance; isBestSelf = false; } } } else if (canUseAttackAction && action.CanTargetEnemy && action.IsNeededFor(actor) && (!AutoAttackOnlyFromCover || !action.WillMoveForActor(actor))) { if (actor == Priority) { _autoWait = AutoCooldown; Execute(action, actor); return; } var distance = Vector3.Distance(transform.position, actor.transform.position); if (best == null || isBestSelf || distance < bestDistance) { best = actor; bestDistance = distance; isBestSelf = false; } } } if (best != null) { bestSingleAction = action; bestSingleActionTarget = best; break; } } /////////////////////////////////////////////////////// // // CONSIDER AREA ACTIONS // /////////////////////////////////////////////////////// for (int i = 0; i < Actions.Length; i++) { var action = Actions[i]; if (action.Wait > float.Epsilon || !action.Auto || !action.NeedsTargetLocation) { continue; } _possibleActionTargets.Clear(); for (int j = 0; j < _nearbyActors.Count; j++) { var actor = _nearbyActors[j]; if (!actor.IsAlive && action.ShouldIgnoreDead) { continue; } if (actor == _actor) { if (action.CanTargetSelf && action.IsNeededFor(actor)) { _possibleActionTargets.Add(actor); } } else if (actor.Side == _actor.Side) { if (action.CanTargetAlly && action.IsNeededFor(actor)) { _possibleActionTargets.Add(actor); } } else if (canUseAttackAction && action.CanTargetEnemy && action.IsNeededFor(actor)) { _possibleActionTargets.Add(actor); } } if (_possibleActionTargets.Count == 0 || (_possibleActionTargets.Count == 1 && bestSingleAction != null)) { continue; } if (action.UIRadius > float.Epsilon) { var radius = action.UIRadius; var steps = (int)(2 * AutoDistance / radius); var hasBest = false; var bestPosition = Vector3.zero; var bestDistance = 0f; var bestActorCount = 0; var bestHasPriority = false; for (int x = -steps / 2; x < steps / 2; x++) { for (int y = -steps / 2; y < steps / 2; y++) { var position = transform.position + new Vector3(x * action.UIRadius, 0, y * radius); var distance = Vector3.Distance(position, transform.position); if (distance > AutoDistance) { continue; } if (AutoAttackOnlyFromCover && action.CanTargetEnemy && action.WillMoveForPosition(position)) { continue; } var actorCount = 0; var hasPriority = false; for (int j = 0; j < _possibleActionTargets.Count; j++) { var a = _possibleActionTargets[j]; if (Vector3.Distance(position, a.transform.position) < radius) { actorCount++; if (a == Priority) { hasPriority = true; } } } if (actorCount > 0) { var isBetter = false; if (!hasBest) { isBetter = true; } else if (hasPriority && !bestHasPriority) { isBetter = false; } else if (!bestHasPriority) { if (actorCount > bestActorCount) { isBetter = true; } else if (actorCount == bestActorCount) { isBetter = distance < bestDistance; } } if (isBetter) { hasBest = true; bestPosition = position; bestDistance = distance; bestHasPriority = hasPriority; bestActorCount = actorCount; } } } } if (hasBest && (bestSingleAction == null || bestActorCount > 1)) { _autoWait = AutoCooldown; Execute(action, bestPosition); return; } } } /////////////////////////////////////////////////////// // // EXECUTE BEST SINGLE IF NO AREA POSSIBLE // /////////////////////////////////////////////////////// if (bestSingleAction != null) { _autoWait = AutoCooldown; Execute(bestSingleAction, bestSingleActionTarget); } /////////////////////////////////////////////////////// // // CONSIDER NON-TARGET ACTIONS // /////////////////////////////////////////////////////// for (int i = 0; i < Actions.Length; i++) { var action = Actions[i]; if (action.Wait > float.Epsilon || !action.Auto || action.CanTargetAny || action.CanTargetGround || action.UIRadius > float.Epsilon || action.CanTargetMultiple || action.NeedsOnlySelf) { continue; } _autoWait = AutoCooldown; Execute(action); return; } } }
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); }
private static void debugAI(Transform transform) { #if UNITY_EDITOR var motor = transform.GetComponent <CharacterMotor>(); if (motor == null || !motor.IsAlive) { return; } var ai = transform.GetComponent <AIController>(); if (ai == null) { return; } var position = transform.position + Vector3.up * 2; var offset = -8; DebugText.Draw(ai.State.ToString(), position, 0, offset); offset += 16; DebugText.Draw(ai.StateReason.ToString(), position, 0, offset); offset += 16; if (ai.IsAlerted) { if (ai.Situation.WouldLikeToRetreat) { DebugText.Draw("Would like to retreat", position, 0, offset, Color.yellow); offset += 16; } if (motor.Target != null) { var actor = motor.Target.GetComponent <Actor>(); if (actor != null && actor.Side == ai.Actor.Side) { DebugText.Draw("Is aiming at a friend", position, 0, offset, Color.red); offset += 16; } } if (ai.Situation.IsAllowedToBeAggressive) { DebugText.Draw("Is aggressive", position, 0, offset, Color.green); offset += 16; } if (ai.IsAiming) { DebugText.Draw("Is aiming", position, 0, offset, Color.cyan); offset += 16; } if (ai.Situation.Threat != null) { if (ai.Situation.CanSeeTheThreat) { Debug.DrawLine(ai.Situation.CurrentPosition, ai.Situation.ThreatGroundPosition, Color.green); } else { Debug.DrawLine(ai.Situation.CurrentPosition, ai.Situation.ThreatGroundPosition, Color.red); } } else { Debug.DrawLine(ai.Situation.CurrentPosition, ai.Situation.ThreatGroundPosition, Color.red); } } if (AIUtil.IsMovingState(ai.State)) { Debug.DrawLine(ai.Situation.CurrentPosition, ai.Situation.TargetPosition, Color.blue); } #endif }