// OnStateExit is called when a transition ends and the state machine finishes evaluating this state override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { AIStatePursuit statePursuit = animator.GetComponent <AIStatePursuit>(); if (!statePursuit.useRootPosition) { statePursuit.ResetSpeed(); } }
private void OnTriggerStay(Collider other) { if (_stateMachine.Health.IsDead) { return; } bool enemyOrPlayerThreatInSight = false; // This is for potential threats if (other.CompareTag("Player") || other.CompareTag("Enemy")) { if (_angryAt.Count == 0) { goto NotAngryAtAnyoneInSight; } // First check if we are angry at the potential target GameObject potentialTargetObject = _angryAt.Find(potentialTarget => potentialTarget == other.gameObject); if (potentialTargetObject == null) { goto NotAngryAtAnyoneInSight; } // If we are already seeing a player or enemy and the target we just saw is a different one, then we don't do anything if (_stateMachine.CurrentTarget != null && _stateMachine.CurrentTarget.TargetTransform.gameObject != other.gameObject && (_stateMachine.CurrentTarget.Type == AITargetType.Player || _stateMachine.CurrentTarget.Type == AITargetType.Enemy)) { return; } // At this point, we are only interacting with either the target that we already have as a target or a new one (since we don't have a target at all) RaycastHit hitInfo; if (Physics.Raycast(transform.position, (other.transform.position + Vector3.up) - transform.position, out hitInfo, _sphereCollider.radius, _seeablesMask)) { if (hitInfo.transform.gameObject == other.gameObject) { float angle = Vector3.Angle(transform.forward, (other.transform.position + Vector3.up) - transform.position); if (angle <= _fieldOfView) { _stateMachine.SetTarget(other.transform, AITargetType.Player); enemyOrPlayerThreatInSight = true; } } } // Resetting target for when the player or the enemy is inside the scanner collider but not inside the field of view // If we were seeing the player or the enemy and no longer see him, then we reset the target after the tolerance time if (_stateMachine.CurrentTarget != null && _stateMachine.CurrentTarget.Type == AITargetType.Player && !enemyOrPlayerThreatInSight) { _targetOutOfSightTime += Time.deltaTime; if (_targetOutOfSightTime >= _targetOutOfSightTolerance) { _targetOutOfSightTime = 0f; _stateMachine.ResetTarget(); } } } if (enemyOrPlayerThreatInSight) { return; } NotAngryAtAnyoneInSight: if (other.CompareTag("AIVehicleDetector") && (_stateMachine.CurrentTarget == null || _stateMachine.CurrentTarget.Type != AITargetType.Vehicle) && _stateMachine.CurrentState.GetStateType() != AIState.AIStateType.Vehicle) { InteractiveVehicle interactiveVehicle = other.GetComponent <InteractiveVehicle>(); if (interactiveVehicle != null && !interactiveVehicle.Occupied) { AIStateVehicle aiStateDriving = (AIStateVehicle)(_stateMachine.GetState(AIState.AIStateType.Vehicle)); if ((aiStateDriving.WantToDrive && interactiveVehicle.DriverSeat) || (aiStateDriving.WantToBePassenger && !interactiveVehicle.DriverSeat)) { _stateMachine.SetTarget(other.transform, AITargetType.Vehicle); return; } } } if (other.CompareTag("Chair") && (_stateMachine.CurrentTarget == null || _stateMachine.CurrentTarget.Type != AITargetType.Chair) && _stateMachine.CurrentState.GetStateType() != AIState.AIStateType.Sitting) { InteractiveChair interactiveChair = other.GetComponent <InteractiveChair>(); if (interactiveChair != null && !interactiveChair.IsOccupied) { AIStateSitting aiStateDriving = (AIStateSitting)(_stateMachine.GetState(AIState.AIStateType.Sitting)); if (aiStateDriving.IsTired) { _stateMachine.SetTarget(other.transform, AITargetType.Chair); return; } } } // For social Interaction if (other.CompareTag("Enemy")) { // if we are already interacting socially, then there is nothing to do here if (_stateMachine.CurrentTarget != null && _stateMachine.CurrentTarget.Type == AITargetType.Friend) { return; } AIStateMachine otherStateMachine = other.GetComponent <AIStateMachine>(); AIStateSocialInteraction mySocialInteractionState = (AIStateSocialInteraction)(_stateMachine.GetState(AIState.AIStateType.SocialInteraction)); AIStatePursuit myPursuitState = (AIStatePursuit)(_stateMachine.GetState(AIState.AIStateType.Pursuit)); AIStateSocialInteraction friendSocialInteractionState = (AIStateSocialInteraction)(otherStateMachine.GetState(AIState.AIStateType.SocialInteraction)); AIStatePursuit friendPursuitState = (AIStatePursuit)(otherStateMachine.GetState(AIState.AIStateType.Pursuit)); if (mySocialInteractionState.IsFeelingLonely && friendSocialInteractionState.IsFeelingLonely && !otherStateMachine.Health.IsDead) { // If the friend hasn't gotten into social mode yet by setting a target of type friend, then we are the ones who are going to be making the first move if (otherStateMachine.CurrentTarget == null || otherStateMachine.CurrentTarget.Type == AITargetType.None || otherStateMachine.CurrentTarget.Type == AITargetType.NavigationPoint || otherStateMachine.CurrentTarget.Type == AITargetType.BoomBox) { myPursuitState.ShouldMakeAMove = true; mySocialInteractionState.FriendSocialInteraction = friendSocialInteractionState; // We set our pursuit mode by setting a target _stateMachine.SetTarget(other.transform, AITargetType.Friend); // the friend should instantly get into social mode friendSocialInteractionState.FriendSocialInteraction = mySocialInteractionState; otherStateMachine.SetTarget(transform, AITargetType.Friend); otherStateMachine.SwitchState(AIState.AIStateType.SocialInteraction); } } } if (other.CompareTag("BoomBox")) { // If we have a more important target: Another AI Agent as an enemy, a friend or a player, then we ignore the boombox if (_stateMachine.CurrentTarget != null && (_stateMachine.CurrentTarget.Type == AITargetType.Player || _stateMachine.CurrentTarget.Type == AITargetType.Enemy || _stateMachine.CurrentTarget.Type == AITargetType.Friend)) { return; } AIStateDancing aiStateDancing = (AIStateDancing)(_stateMachine.GetState(AIState.AIStateType.Dancing)); if (aiStateDancing.IsHungry) { _stateMachine.SetTarget(other.transform, AITargetType.BoomBox); } } }
private void Start() { // Getting cache variables _navMeshAgent = GetComponent <NavMeshAgent>(); _animator = GetComponent <Animator>(); _mouthAudioSource = GetComponent <AudioSource>(); _targetTrigger = GetComponentInChildren <TargetTrigger>(); AIDecapitation[] parts = GetComponentsInChildren <AIDecapitation>(); foreach (AIDecapitation part in parts) { _parts.Add(part); } HandleAnimatorController(); // Getting the skinned mesh renderers for the metamorphosis if (_metamorphosisMaterial != null) { SkinnedMeshRenderer[] skinnedMeshRenderers = GetComponentsInChildren <SkinnedMeshRenderer>(); foreach (SkinnedMeshRenderer meshRenderer in skinnedMeshRenderers) { _skinnedMeshRenderers.Add(meshRenderer); } } // We always have the rotation of the navmesh deactivated! // In fact, we either rotate with the root motion rotation of the turn around animation // Or we rotate it manually through code. // In sum, the navmesh is only useful for calculating the path: steertingTarget :) if (_navMeshAgent != null) { _navMeshAgent.updateRotation = false; } if (_targetTrigger != null) { _targetTrigger.RegisterTargetTrigger(this); } // Storing states AIStateIdle stateIdle = GetComponent <AIStateIdle>(); if (stateIdle != null) { _statesDictionary.Add(AIStateType.Idle, stateIdle); _statesDictionary[AIStateType.Idle].RegisterState(this); } AIStatePatrol statePatrol = GetComponent <AIStatePatrol>(); if (statePatrol != null) { _statesDictionary.Add(AIStateType.Patrol, statePatrol); _statesDictionary[AIStateType.Patrol].RegisterState(this); } AIStatePursuit statePursuit = GetComponent <AIStatePursuit>(); if (statePursuit != null) { _statesDictionary.Add(AIStateType.Pursuit, statePursuit); _statesDictionary[AIStateType.Pursuit].RegisterState(this); } AIStateAttack stateAttack = GetComponent <AIStateAttack>(); if (stateAttack != null) { _statesDictionary.Add(AIStateType.Attacking, stateAttack); _statesDictionary[AIStateType.Attacking].RegisterState(this); } AIStateAlert stateAlert = GetComponent <AIStateAlert>(); if (stateAlert != null) { _statesDictionary.Add(AIStateType.Alert, stateAlert); _statesDictionary[AIStateType.Alert].RegisterState(this); } if (_statesDictionary.ContainsKey(AIStateType.Idle) && _statesDictionary[AIStateType.Idle] != null) { ChangeState(AIStateType.Idle); } // Get all body parts snapshot Transform[] bodyParts = transform.GetComponentsInChildren <Transform>(); foreach (Transform bodyPart in bodyParts) { BodyPartSnapShot bodyPartSnapshot = new BodyPartSnapShot(); bodyPartSnapshot.transform = bodyPart; _bodyPartsSnapshots.Add(bodyPartSnapshot); } // At the beginning, we should always be able to scream _screamTimer = _screamDelay; // Getting the navmesh agent initial speed for damageBehavior (in case we aren't using root rotation) if (_navMeshAgent != null) { _initialSpeed = _navMeshAgent.speed; } _initialVehicleStoppingDistance = _vehicleStoppingDistance; }