// ---------------------------------------------------------------- // Name : Reanimate (coroutine) // Desc : Starts the reanimation procedure // ---------------------------------------------------------------- protected IEnumerator Reanimate() { // Only reanimate if we are in a ragdoll state if (_boneControlType != AIBoneControlType.Ragdoll || _animator == null) { yield break; } // Wait for the desired number of seconds before initiating the reanimation process yield return(new WaitForSeconds(_reanimationWaitTime)); // Record time at start of reanimation procedure _ragdollEndTime = Time.time; // Set rigidbodies back to being kinematic foreach (Rigidbody body in _bodyParts) { body.isKinematic = true; } // Put us in reanimation mode _boneControlType = AIBoneControlType.RagdollToAnim; // Record postions and rotations of all bones prior to reanimation foreach (BodyPartSnapshot snapShot in _bodyPartSnapShots) { snapShot.position = snapShot.transform.position; snapShot.rotation = snapShot.transform.rotation; } // Record the ragdolls head and feet position _ragdollHeadPosition = _animator.GetBoneTransform(HumanBodyBones.Head).position; _ragdollFeetPosition = (_animator.GetBoneTransform(HumanBodyBones.LeftFoot).position + _animator.GetBoneTransform(HumanBodyBones.RightFoot).position) * 0.5f; _ragdollHipPosition = _rootBone.position; // Enable Animator _animator.enabled = true; if (_rootBone != null) { float forwardTest; switch (_rootBoneAlignment) { case AIBoneAlignmentType.ZAxis: forwardTest = _rootBone.forward.y; break; case AIBoneAlignmentType.ZAxisInverted: forwardTest = -_rootBone.forward.y; break; case AIBoneAlignmentType.YAxis: forwardTest = _rootBone.up.y; break; case AIBoneAlignmentType.YAxisInverted: forwardTest = -_rootBone.up.y; break; case AIBoneAlignmentType.XAxis: forwardTest = _rootBone.right.y; break; case AIBoneAlignmentType.XAxisInverted: forwardTest = -_rootBone.right.y; break; default: forwardTest = _rootBone.forward.y; break; } // Set the trigger in the animator if (forwardTest >= 0) { _animator.SetTrigger(_reanimateFromBackHash); } else { _animator.SetTrigger(_reanimateFromFrontHash); } } }
// --------------------------------------------------------------- // Name : LateUpdate // Desc : Called by Unity at the end of every frame update. Used // here to perform reanimation. // --------------------------------------------------------------- protected virtual void LateUpdate() { if (_boneControlType == AIBoneControlType.RagdollToAnim) { if (Time.time <= _ragdollEndTime + _mecanimTransitionTime) { Vector3 animatedToRagdoll = _ragdollHipPosition - _rootBone.position; Vector3 newRootPosition = transform.position + animatedToRagdoll; RaycastHit[] hits = Physics.RaycastAll(newRootPosition + (Vector3.up * 0.25f), Vector3.down, float.MaxValue, _geometryLayers); newRootPosition.y = float.MinValue; foreach (RaycastHit hit in hits) { if (!hit.transform.IsChildOf(transform)) { newRootPosition.y = Mathf.Max(hit.point.y, newRootPosition.y); } } NavMeshHit navMeshHit; Vector3 baseOffset = Vector3.zero; if (_navAgent) { baseOffset.y = _navAgent.baseOffset; } if (NavMesh.SamplePosition(newRootPosition, out navMeshHit, 25.0f, NavMesh.AllAreas)) { transform.position = navMeshHit.position + baseOffset; } else { transform.position = newRootPosition + baseOffset; } Vector3 ragdollDirection = _ragdollHeadPosition - _ragdollFeetPosition; ragdollDirection.y = 0.0f; Vector3 meanFeetPosition = 0.5f * (_animator.GetBoneTransform(HumanBodyBones.LeftFoot).position + _animator.GetBoneTransform(HumanBodyBones.RightFoot).position); Vector3 animatedDirection = _animator.GetBoneTransform(HumanBodyBones.Head).position - meanFeetPosition; animatedDirection.y = 0.0f; //Try to match the rotations. Note that we can only rotate around Y axis, as the animated characted must stay upright, //hence setting the y components of the vectors to zero. transform.rotation *= Quaternion.FromToRotation(animatedDirection.normalized, ragdollDirection.normalized); } // Calculate Interpolation value float blendAmount = Mathf.Clamp01((Time.time - _ragdollEndTime - _mecanimTransitionTime) / _reanimationBlendTime); // Calculate blended bone positions by interplating between ragdoll bone snapshots and animated bone positions foreach (BodyPartSnapshot snapshot in _bodyPartSnapShots) { if (snapshot.transform == _rootBone) { snapshot.transform.position = Vector3.Lerp(snapshot.position, snapshot.transform.position, blendAmount); } snapshot.transform.rotation = Quaternion.Slerp(snapshot.rotation, snapshot.transform.rotation, blendAmount); } // Conditional to exit reanimation mode if (blendAmount == 1.0f) { _boneControlType = AIBoneControlType.Animated; if (_navAgent) { _navAgent.enabled = true; } if (_collider) { _collider.enabled = true; } AIState newState = null; if (_states.TryGetValue(AIStateType.Alerted, out newState)) { if (_currentState != null) { _currentState.OnExitState(); } newState.OnEnterState(); _currentState = newState; _currentStateType = AIStateType.Alerted; } } } }
//Viene chiamato dopo l'update e quando l'Animator è già aggiornato protected virtual void LateUpdate() { if (_boneControlType == AIBoneControlType.RagdollToAnim) { if (Time.time <= _ragdollEndTime + _mecanimTransitionTime) { Vector3 animatedToRagdoll = _ragdollHipPosition - _rootBone.position; Vector3 newRootPosition = transform.position + animatedToRagdoll; RaycastHit[] hits = Physics.RaycastAll(newRootPosition + (Vector3.up * 0.25f), Vector3.down, float.MaxValue, _geometryLayers); newRootPosition.y = float.MinValue; foreach (RaycastHit hit in hits) { if (!hit.transform.IsChildOf(transform)) { newRootPosition.y = Mathf.Max(hit.point.y, newRootPosition.y); } } NavMeshHit navMeshHit; Vector3 baseOffset = Vector3.zero; if (_navAgent) { baseOffset.y = _navAgent.baseOffset; } if (NavMesh.SamplePosition(newRootPosition, out navMeshHit, 25.0f, NavMesh.AllAreas)) { transform.position = navMeshHit.position + baseOffset; } else { transform.position = newRootPosition + baseOffset; } Vector3 ragdollDirection = _ragdollHeadPosition - _ragdollFeetPosition; ragdollDirection.y = 0.0f; Vector3 meanFeetPosition = 0.5f * (_animator.GetBoneTransform(HumanBodyBones.LeftFoot).position + _animator.GetBoneTransform(HumanBodyBones.RightFoot).position); Vector3 animatedDirection = _animator.GetBoneTransform(HumanBodyBones.Head).position - meanFeetPosition; animatedDirection.y = 0.0f; // Dato che il modello deve sempre rimanere in verticale // Setta l'asse Y dei vectors a 0 cercando di matchare le rotazioni transform.rotation *= Quaternion.FromToRotation(animatedDirection.normalized, ragdollDirection.normalized); } //Calcolo valore Interpolazione float blendAmount = Mathf.Clamp01((Time.time - _ragdollEndTime - _mecanimTransitionTime) / _reanimationBlendTime); foreach (BodyPartSnapshot snapshot in _bodyPartSnapShots) { if (snapshot.transform == _rootBone) { snapshot.transform.position = Vector3.Lerp(snapshot.position, snapshot.transform.position, blendAmount); } snapshot.transform.rotation = Quaternion.Slerp(snapshot.rotation, snapshot.transform.rotation, blendAmount); } // Per uscire dalla modalità di rianimazione if (blendAmount == 1.0f) { _boneControlType = AIBoneControlType.Animated; if (_navAgent) { _navAgent.enabled = true; } if (_collider) { _collider.enabled = true; } AIState newState = null; if (_states.TryGetValue(AIStateType.Alerted, out newState)) { if (_currentState != null) { _currentState.OnExitState(); } newState.OnEnterState(); _currentState = newState; _currentStateType = AIStateType.Alerted; } } } }
// ------------------------------------------------------------------- // Name : TakeDamage // Desc : Processes the zombie's reaction to being dealt damage // -------------------------------------------------------------------- public override void TakeDamage(Vector3 position, Vector3 force, int damage, Rigidbody bodyPart, CharacterManager characterManager, int hitDirection = 0) { if (GameSceneManager.instance != null && GameSceneManager.instance.bloodParticles != null) { ParticleSystem sys = GameSceneManager.instance.bloodParticles; if (sys) { sys.transform.position = position; var settings = sys.main; settings.simulationSpace = ParticleSystemSimulationSpace.World; sys.Emit(60); } } float hitStrength = force.magnitude; if (_boneControlType == AIBoneControlType.Ragdoll) { if (bodyPart != null) { if (hitStrength > 1.0f) { bodyPart.AddForce(force, ForceMode.Impulse); } if (bodyPart.CompareTag("Head")) { _health = Mathf.Max(_health - damage, 0); } else if (bodyPart.CompareTag("Upper Body")) { _upperBodyDamage += damage; } else if (bodyPart.CompareTag("Lower Body")) { _lowerBodyDamage += damage; } UpdateAnimatorDamage(); if (_health > 0) { if (_reanimationCoroutine != null) { StopCoroutine(_reanimationCoroutine); } _reanimationCoroutine = Reanimate(); StartCoroutine(_reanimationCoroutine); } } return; } // Get local space position of attacker Vector3 attackerLocPos = transform.InverseTransformPoint(characterManager.transform.position); bool shouldRagdoll = (hitStrength > 1.0f); if (bodyPart != null) { if (bodyPart.CompareTag("Head")) { _health = Mathf.Max(_health - damage, 0); if (health == 0) { shouldRagdoll = true; } } else if (bodyPart.CompareTag("Upper Body")) { _upperBodyDamage += damage; UpdateAnimatorDamage(); } else if (bodyPart.CompareTag("Lower Body")) { _lowerBodyDamage += damage; UpdateAnimatorDamage(); shouldRagdoll = true; } } if (_boneControlType != AIBoneControlType.Animated || isCrawling || IsLayerActive("Cinematic") || attackerLocPos.z < 0) { shouldRagdoll = true; } if (!shouldRagdoll) { float angle = 0.0f; if (hitDirection == 0) { Vector3 vecToHit = (position - transform.position).normalized; angle = AIState.FindSignedAngle(vecToHit, transform.forward); } int hitType = 0; if (bodyPart.gameObject.CompareTag("Head")) { if (angle < -10 || hitDirection == -1) { hitType = 1; } else if (angle > 10 || hitDirection == 1) { hitType = 3; } else { hitType = 2; } } else if (bodyPart.gameObject.CompareTag("Upper Body")) { if (angle < -20 || hitDirection == -1) { hitType = 4; } else if (angle > 20 || hitDirection == 1) { hitType = 6; } else { hitType = 5; } } if (_animator) { _animator.SetInteger(_hitTypeHash, hitType); _animator.SetTrigger(_hitTriggerHash); } return; } else { if (_currentState) { _currentState.OnExitState(); _currentState = null; _currentStateType = AIStateType.None; } if (_navAgent) { _navAgent.enabled = false; } if (_animator) { _animator.enabled = false; } if (_collider) { _collider.enabled = false; } // Mute Audio While Ragdoll is happening if (_layeredAudioSource != null) { _layeredAudioSource.Mute(true); } inMeleeRange = false; foreach (Rigidbody body in _bodyParts) { if (body) { body.isKinematic = false; } } if (hitStrength > 1.0f) { if (bodyPart != null) { bodyPart.AddForce(force, ForceMode.Impulse); } } _boneControlType = AIBoneControlType.Ragdoll; if (_health > 0) { if (_reanimationCoroutine != null) { StopCoroutine(_reanimationCoroutine); } _reanimationCoroutine = Reanimate(); StartCoroutine(_reanimationCoroutine); } } }
protected virtual IEnumerator Reanimate() { if (_boneControlType != AIBoneControlType.Ragdoll || _animator == null) { yield break; } yield return(new WaitForSeconds(_reanimationWaitTime)); _ragdollEndTime = Time.time; foreach (Rigidbody body in _bodyParts) { body.isKinematic = true; } _boneControlType = AIBoneControlType.RagdollToAnim; foreach (BodyPartSnapshot snapshot in _bodyPartSnapshots) { snapshot.position = snapshot.transform.position; snapshot.rotation = snapshot.transform.rotation; } // Record ragdolls head and feet position _ragdollHeadPosition = _animator.GetBoneTransform(HumanBodyBones.Head).position; _ragdollFeetPosition = (_animator.GetBoneTransform(HumanBodyBones.LeftFoot).position + _animator.GetBoneTransform(HumanBodyBones.RightFoot).position) * .5f; _ragdollHipPosition = _rootBone.position; _animator.enabled = true; if (_rootBone != null) { float forwardTest; switch (_rootBoneAlignment) { case AIBoneAlignmentType.ZAxis: forwardTest = _rootBone.forward.y; break; case AIBoneAlignmentType.ZAxisInverted: forwardTest = -_rootBone.forward.y; break; case AIBoneAlignmentType.YAxis: forwardTest = _rootBone.up.y; break; case AIBoneAlignmentType.YAxisInverted: forwardTest = -_rootBone.up.y; break; case AIBoneAlignmentType.XAxis: forwardTest = _rootBone.right.y; break; case AIBoneAlignmentType.XAxisInverted: forwardTest = -_rootBone.right.y; break; default: forwardTest = _rootBone.forward.y; break; } _animator.SetTrigger(forwardTest >= 0 ? _reanimateFromFrontHash : _reanimateFromBackHash); } }
protected IEnumerator Reanimate() { //Esegui Reanimate solo se si trova in Ragdoll State if (_boneControlType != AIBoneControlType.Ragdoll || _animator == null) { yield break; } // Attendi N secondi prima di eseguire il processo di rianimazione yield return(new WaitForSeconds(_reanimationWaitTime)); // Salva quanto tempo passa da quando parte il processo di rianimazione _ragdollEndTime = Time.time; // Risetto i rigibodies a kinematic foreach (Rigidbody body in _bodyParts) { body.isKinematic = true; } // Inizia la mode reanimation _boneControlType = AIBoneControlType.RagdollToAnim; //Registra posizione e rotazione dei ragdoll bones prima che della rianimazione foreach (BodyPartSnapshot snapshot in _bodyPartSnapShots) { snapshot.position = snapshot.transform.position; snapshot.rotation = snapshot.transform.rotation; } //Registra posizione di testa e piedi del ragdoll _ragdollHeadPosition = _animator.GetBoneTransform(HumanBodyBones.Head).position; _ragdollFeetPosition = (_animator.GetBoneTransform(HumanBodyBones.LeftFoot).position + _animator.GetBoneTransform(HumanBodyBones.RightFoot).position) * 0.5f; _ragdollHipPosition = _rootBone.position; _animator.enabled = true; if (_rootBone != null) { float forwardTest; switch (_rootBoneAlignment) { case AIBoneAlignmentType.XAxis: forwardTest = _rootBone.right.y; break; case AIBoneAlignmentType.YAxis: forwardTest = _rootBone.up.y; break; case AIBoneAlignmentType.ZAxis: forwardTest = _rootBone.forward.y; break; case AIBoneAlignmentType.XAxisInverted: forwardTest = -_rootBone.right.y; break; case AIBoneAlignmentType.YAxisInverted: forwardTest = -_rootBone.up.y; break; case AIBoneAlignmentType.ZAxisInverted: forwardTest = -_rootBone.forward.y; break; default: forwardTest = _rootBone.forward.y; break; } if (forwardTest >= 0) { _animator.SetTrigger(_reanimateFromBackHash); } else { _animator.SetTrigger(_reanimateFromFrontHash); } } }
public override void TakeDamage(Vector3 position, Vector3 force, int damage, // The body part that got hit Rigidbody bodyPart, // To tell the damage system wchich player hit the zombie CharacterManager characterManager, int hitDirection = 0) { base.TakeDamage(position, force, damage, bodyPart, characterManager, hitDirection); if (GameSceneManager.instance != null && GameSceneManager.instance.bloodParticles != null) { ParticleSystem system = GameSceneManager.instance.bloodParticles; system.transform.position = position; system.simulationSpace = ParticleSystemSimulationSpace.World; system.Emit(60); } float hitStrength = force.magnitude; float prevHealth = _health; if (_boneControlType == AIBoneControlType.Ragdoll) { if (bodyPart != null) { if (Time.time > _nextRagdollSoundTime && _ragdollColletion != null && _health > 0) { AudioClip clip = _ragdollColletion[1]; if (clip) { _nextRagdollSoundTime = Time.time + clip.length; AudioManager.instance.PlayOneShotSound(_ragdollColletion.audioGroup, clip, position, _ragdollColletion.volume, _ragdollColletion.spatialBlend, _ragdollColletion.priority); } } if (hitStrength > 1) { bodyPart.AddForce(force, ForceMode.Impulse); } if (bodyPart.CompareTag("Head")) { _health = Mathf.Max(_health - damage, 0); } else if (bodyPart.CompareTag("Upper Body")) { _upperBodyDamage += damage; } else if (bodyPart.CompareTag("Lower Body")) { _lowerBodyDamage += damage; } UpdateAnimatorDamage(); if (_health > 0) { // Reanimate zombie if (_reanimationCoroutine != null) { StopCoroutine(_reanimationCoroutine); } _reanimationCoroutine = Reanimate(); StartCoroutine(_reanimationCoroutine); } } return; } // Get local space position of attacker Vector3 attackerLocPos = transform.InverseTransformPoint(characterManager.transform.position); // Get local space position of hit Vector3 hitLocPos = transform.InverseTransformPoint(position); // We ragdoll if the hit strength is superior to 1 bool shouldRagdoll = hitStrength > 1f; if (bodyPart != null) { if (bodyPart.CompareTag("Head")) { _health = Mathf.Max(_health - damage, 0); if (_health == 0) { // We ragdoll if the zombie is dead: health = 0 shouldRagdoll = true; } } else if (bodyPart.CompareTag("Upper Body")) { _upperBodyDamage += damage; UpdateAnimatorDamage(); } else if (bodyPart.CompareTag("Lower Body")) { _lowerBodyDamage += damage; UpdateAnimatorDamage(); // When we do a leg shot, the zombie will always ragdoll shouldRagdoll = true; } } // We ragdoll if we are playing a cinematic (eating for example), // if we are getting attacked from behind, // if we are not in the animated state (meaning that we are trying to reanimate), // if we are crawling if (_boneControlType != AIBoneControlType.Animated || isCrawling || IsLayerActive("Cinematic") || attackerLocPos.z < 0) { shouldRagdoll = true; } if (!shouldRagdoll) { // We try to play one of hit animations float angle = 0f; if (hitDirection == 0) { Vector3 vecToHit = (position - transform.position).normalized; angle = AIState.FindSignedAngle(vecToHit, transform.forward); } int hitType = 0; if (bodyPart.CompareTag("Head")) { if (angle < -10 || hitDirection == -1) { hitType = 1; } else if (angle > 10 || hitDirection == 1) { hitType = 3; } else { hitType = 2; } } else if (bodyPart.gameObject.CompareTag("Upper Body")) { if (angle < -20 || hitDirection == -1) { hitType = 4; } else if (angle > 20 || hitDirection == 1) { hitType = 6; } else { hitType = 5; } } if (_animator) { _animator.SetInteger(_hitTypeHash, hitType); _animator.SetTrigger(_hitTriggerHash); } } else { // We are ragdolling // We exit the current state when we are ragdolling to avoid any moving or other actions. if (_currentState) { _currentState.OnExitState(); _currentState = null; _currentStateType = AIStateType.None; } if (_navAgent) { _navAgent.enabled = false; } if (_animator) { _animator.enabled = false; } if (_collider) { _collider.enabled = false; } // Mute audio while ragdoll is happening if (_layeredAudioSource != null) { _layeredAudioSource.Mute(true); } if (Time.time > _nextRagdollSoundTime && _ragdollColletion != null && prevHealth > 0) { AudioClip clip = _ragdollColletion[0]; if (clip != null) { _nextRagdollSoundTime = Time.time + clip.length; AudioManager.instance.PlayOneShotSound(_ragdollColletion.audioGroup, clip, position, _ragdollColletion.volume, _ragdollColletion.spatialBlend, _ragdollColletion.priority); } } inMeleeRange = false; // Transform all the rigid bodies to non kinematic so that they could start applying their physics foreach (Rigidbody body in _bodyParts) { if (body != null) { body.isKinematic = false; } } // Add force to the part that got hit in the hit direction if (hitStrength > 1f) { bodyPart.AddForce(force, ForceMode.Impulse); } _boneControlType = AIBoneControlType.Ragdoll; if (_health > 0) { // Reanimate zombie if (_reanimationCoroutine != null) { StopCoroutine(_reanimationCoroutine); } _reanimationCoroutine = Reanimate(); StartCoroutine(_reanimationCoroutine); } } }
protected virtual void LateUpdate() { if (_boneControlType == AIBoneControlType.RagdollToAnim) { // The first time the late update function is gonna be called, the animator won't be playing the right animation after we set the triggers in the previous frame // So we wait for a period of time since the last time the zombie was ragdolled: if (Time.time <= _ragdollEndTime + _mecanimTransitionTime) { // The ragdoll hip may be in a different position from that understood and set by the animator. Vector3 animatedToRagdoll = _ragdollHipPosition - _rootBone.position; // Sliding our transform along the previous vector to the location of the ragdoll hip. // We this, we only get the right x and z position of the character. Vector3 newRootPosition = transform.position + animatedToRagdoll; RaycastHit[] hits = Physics.RaycastAll(newRootPosition, Vector3.down, float.MaxValue, _geometryLayers); //newRootPosition.y = float.MinValue; foreach (RaycastHit hit in hits) { if (!hit.transform.IsChildOf(transform)) { //newRootPosition.y = Mathf.Max(newRootPosition.y, hit.point.y); } } NavMeshHit navMeshHit; Vector3 baseOffset = Vector3.zero; if (_navAgent) { baseOffset.y = _navAgent.baseOffset; } if (NavMesh.SamplePosition(newRootPosition, out navMeshHit, 2f, NavMesh.AllAreas)) { transform.position = navMeshHit.position + baseOffset; } else { transform.position = newRootPosition + baseOffset; } Vector3 ragdollDirection = _ragdollHeadPosition - _ragdollFeetPosition; ragdollDirection.y = 0; Vector3 meanFeetPosition = .5f * (_animator.GetBoneTransform(HumanBodyBones.RightFoot).position + _animator.GetBoneTransform(HumanBodyBones.LeftFoot).position); Vector3 animatedDirection = _animator.GetBoneTransform(HumanBodyBones.Head).position - meanFeetPosition; animatedDirection.y = 0f; transform.rotation *= Quaternion.FromToRotation(animatedDirection.normalized, ragdollDirection.normalized); } float blendAmount = Mathf.Clamp01((Time.time - _ragdollEndTime - _mecanimTransitionTime) / _reanimationBlendTime); // Calculate blended bone positions by interpolating between ragdoll bone snapshots and animated bone positions foreach (BodyPartSnapshot snapshot in _bodyPartSnapshots) { if (snapshot.transform == _rootBone) { snapshot.transform.position = Vector3.Lerp(snapshot.position, snapshot.transform.position, blendAmount); } // All other child bones are gonna automatically move with the parent bone. // They are just gonna be rotated by the animator snapshot.transform.rotation = Quaternion.Slerp(snapshot.rotation, snapshot.transform.rotation, blendAmount); } // Conditional to exit reanimation code if (blendAmount >= 1) { _boneControlType = AIBoneControlType.Animated; if (_navAgent) { _navAgent.enabled = true; } if (_collider) { _collider.enabled = true; } AIState newState = null; if (_states.TryGetValue(AIStateType.Alerted, out newState)) { if (_currentState != null) { _currentState.OnExitState(); } newState.OnEnterState(); _currentState = newState; _currentStateType = AIStateType.Alerted; } } } }
//called at the end of every frame protected virtual void LateUpdate() { if (_boneControlType == AIBoneControlType.RAGDOLLTOANIM) { //Aligning the parent obj to the zombie if (Time.time <= _ragdollEndTime + _mecanimTransitionTime) { Vector3 animatedToRagdoll = _ragdollHipPosition - _rootBone.position; Vector3 newRootPosition = transform.position + animatedToRagdoll; //Vector3.up makes sure the ray isnt cast from under the floor RaycastHit[] hits = Physics.RaycastAll(newRootPosition + (Vector3.up * 0.25f), Vector3.down, float.MaxValue, _geometryLayers); newRootPosition.y = float.MinValue; foreach (RaycastHit hit in hits) { //make sure the object is not attached to the zombie if (!hit.transform.IsChildOf(transform)) { //if the current y is larger that the current one we have set assign it newRootPosition.y = Mathf.Max(hit.point.y, newRootPosition.y); } } //make sure point is on the nav mesh, use this to pass in a position and find the closest point on the nav mesh NavMeshHit navMeshHit; Vector3 baseOffset = Vector3.zero; if (_navAgent) { baseOffset.y = _navAgent.baseOffset; } if (NavMesh.SamplePosition(newRootPosition, out navMeshHit, 2.0f, NavMesh.AllAreas)) { //if it can find an area on the nav mesh transform.position = navMeshHit.position + baseOffset; } else { //else set this as our new transform transform.position = newRootPosition + baseOffset; } Vector3 ragdollDirection = _ragdollHeadPosition - _ragdollFeetPosition; //set y to 0 ragdollDirection.y = 0.0f; //mid point between feet Vector3 meanFeetPosition = 0.5f * (_animator.GetBoneTransform(HumanBodyBones.LeftFoot).position + _animator.GetBoneTransform(HumanBodyBones.RightFoot).position); Vector3 animatedDirection = _animator.GetBoneTransform(HumanBodyBones.Head).position - meanFeetPosition; //set y to 0 animatedDirection.y = 0; //try to match rotation, rotating only around Y as animated character mys stay upright //this is y components are set to 0 above transform.rotation *= Quaternion.FromToRotation(animatedDirection.normalized, ragdollDirection.normalized); } //calcutae interpolation t value float blendAmount = Mathf.Clamp01((Time.time - _ragdollEndTime - _mecanimTransitionTime) / _reanimationBlendTime); //calculate blended bone transforms by interplating between ragdoll bone snapshots and animator positions foreach (BodyPartSnapshot snapshot in _bodyPartSnapShots) { //hip bone from ragdoll and animated version my have transformed so it has to be moved, all other bones are childed so only rotation need to be changed if (snapshot.transform == _rootBone) { snapshot.transform.position = Vector3.Lerp(snapshot.position, snapshot.transform.position, blendAmount); } snapshot.transform.rotation = Quaternion.Slerp(snapshot.rotation, snapshot.transform.rotation, blendAmount); } //hand control back to animator if (blendAmount == 1.0f) { _boneControlType = AIBoneControlType.ANIMATED; if (_navAgent) { _navAgent.enabled = true; } if (_collider) { _collider.enabled = true; } AIState newState = null; if (_states.TryGetValue(AIStateType.ALERTED, out newState)) { if (_currentState != null) { _currentState.OnExitState(); } newState.OnEnterState(); _currentState = newState; _currentStateType = AIStateType.ALERTED; } } } }
protected IEnumerator Reanimate() { //Only reanimate in ragdoll state if (_boneControlType != AIBoneControlType.RAGDOLL || _animator == null) { yield break; } //wait for desired time yield return(new WaitForSeconds(_reanimationWaitTime)); //record time at start of reanimation _ragdollEndTime = Time.time; //set rbs in body to kinematic so animator can take hold foreach (Rigidbody body in _bodyParts) { body.isKinematic = true; } //put into reanimation mode _boneControlType = AIBoneControlType.RAGDOLLTOANIM; //record pos and rot of all bones before reanimation foreach (BodyPartSnapshot snapShot in _bodyPartSnapShots) { snapShot.position = snapShot.transform.position; snapShot.rotation = snapShot.transform.rotation; } //record ragdoll head and feet position _ragdollHeadPosition = _animator.GetBoneTransform(HumanBodyBones.Head).position; _ragdollFeetPosition = (_animator.GetBoneTransform(HumanBodyBones.LeftFoot).position + _animator.GetBoneTransform(HumanBodyBones.RightFoot).position) * 0.5f; _ragdollHipPosition = _rootBone.position; //enable animator _animator.enabled = true; if (_rootBone != null) { //figure out which animation to play based on orientation float forwardTest; switch (_rootBoneAlignment) { //forward axis of model(used on models that are orientated wrong when imported) case AIBoneAlignmentType.ZAXIS: forwardTest = _rootBone.forward.y; break; case AIBoneAlignmentType.ZAXISINVERTED: forwardTest = -_rootBone.forward.y; break; case AIBoneAlignmentType.YAXIS: forwardTest = _rootBone.up.y; break; case AIBoneAlignmentType.YAXISINVERTED: forwardTest = -_rootBone.up.y; break; case AIBoneAlignmentType.XAXIS: forwardTest = _rootBone.right.y; break; case AIBoneAlignmentType.XAXISINVERTED: forwardTest = -_rootBone.right.y; break; default: forwardTest = _rootBone.forward.y; break; } if (forwardTest >= 0) { _animator.SetTrigger(_reanimateFromBackHash); } else { _animator.SetTrigger(_reanimateFromFrontHash); } } }
//function overview //Check if we have ragdolled the zombie, if not send info to animator, if ragdolled and still alive then reanimate public override void TakeDamage(Vector3 position, Vector3 force, int damage, Rigidbody bodyPart, CharacterManager characterManager, int hitDirection = 0) { if (GameSceneManager.instance != null && GameSceneManager.instance.bloodParticles != null) { ParticleSystem sys = GameSceneManager.instance.bloodParticles; sys.transform.position = position; var settings = sys.main; settings.simulationSpace = ParticleSystemSimulationSpace.World; sys.Emit(60); } float hitStrength = force.magnitude; //if ragdolled if (_boneControlType == AIBoneControlType.RAGDOLL) { if (bodyPart != null) { if (hitStrength > 1.0f) { bodyPart.AddForce(force, ForceMode.Impulse); } if (bodyPart.CompareTag("Head")) { _health = Mathf.Max(_health - damage, 0); } else if (bodyPart.CompareTag("Upper Body")) { _upperBodyDamage += damage; } else if (bodyPart.CompareTag("Lower Body")) { _lowerBodyDamage += damage; } UpdateAnimatorDamage(); if (_health > 0) { if (_reanimationCoroutine != null) { StopCoroutine(_reanimationCoroutine); } _reanimationCoroutine = Reanimate(); StartCoroutine(_reanimationCoroutine); } } //return because already in a ragdoll state return; } //characters position relative to the zombie Vector3 attackerLocalPos = transform.InverseTransformPoint(characterManager.transform.position); //local position of the hit point Vector3 hitLocPos = transform.InverseTransformPoint(position); bool shouldRagdoll = (hitStrength > 1.0f); //what happens in normal play if the zombie is hit if (bodyPart != null) { if (bodyPart.CompareTag("Head")) { _health = Mathf.Max(_health - damage, 0); if (health == 0) { shouldRagdoll = true; } } else if (bodyPart.CompareTag("Upper Body")) { _upperBodyDamage += damage; UpdateAnimatorDamage(); } else if (bodyPart.CompareTag("Lower Body")) { _lowerBodyDamage += damage; UpdateAnimatorDamage(); shouldRagdoll = true; } } //should rag doll: if animated, if crawling, if in cinematic layer, if attack came from behind if (_boneControlType != AIBoneControlType.ANIMATED || isCrawling || cinematicEnabled || attackerLocalPos.z < 0) { shouldRagdoll = true; } //should NOT ragdoll: play animation instead if (!shouldRagdoll) { float angle = 0.0f; if (hitDirection == 0) { Vector3 vecToHit = (position - transform.position).normalized; angle = AIState.FindSignedAngle(vecToHit, transform.forward); } //decide animation to play, hit direction is used for melee weapons int hitType = 0; if (bodyPart.gameObject.CompareTag("Head")) { if (angle < -10 || hitDirection == -1) { hitType = 1; } else if (angle > 10 || hitDirection == 1) { hitType = 3; } else { hitType = 2; } } else if (bodyPart.gameObject.CompareTag("Upper Body")) { if (angle < -20 || hitDirection == -1) { hitType = 4; } else if (angle > 20 || hitDirection == 1) { hitType = 6; } else { hitType = 5; } } //put in more hit animation parameters here //TODO try with rb reacting to forces if (_animator) { _animator.SetInteger(_hitTypeHash, hitType); _animator.SetTrigger(_hitTriggerHash); } return; } else { //stops the machine from calling a state, shuts off AIStateMachine if (_currentState) { _currentState.OnExitState(); _currentState = null; _currentStateType = AIStateType.NONE; } if (_navAgent) { _navAgent.enabled = false; } if (_animator) { _animator.enabled = false; } if (_collider) { _collider.enabled = false; } inMeleeRange = false; foreach (Rigidbody body in _bodyParts) { if (body) { //ragdoll body body.isKinematic = false; } } if (hitStrength > 1.0f) { bodyPart.AddForce(force, ForceMode.Impulse); } _boneControlType = AIBoneControlType.RAGDOLL; if (_health > 0) { if (_reanimationCoroutine != null) { StopCoroutine(_reanimationCoroutine); } _reanimationCoroutine = Reanimate(); StartCoroutine(_reanimationCoroutine); } } }