using System; using System.Collections; using UnityEngine; namespace RootMotion.FinalIK { public class RagdollUtility : MonoBehaviour { public class Rigidbone { public Rigidbody r; public Transform t; public Collider collider; public Joint joint; public Rigidbody c; public bool updateAnchor; public Vector3 deltaPosition; public Quaternion deltaRotation; public float deltaTime; public Vector3 lastPosition; public Quaternion lastRotation; public Rigidbone(Rigidbody r) { this.r = r; t = r.transform; joint = t.GetComponent(); collider = t.GetComponent(); if (joint != null) { c = joint.connectedBody; updateAnchor = c != null; } lastPosition = t.position; lastRotation = t.rotation; } public void RecordVelocity() { deltaPosition = t.position - lastPosition; lastPosition = t.position; deltaRotation = QuaTools.FromToRotation(lastRotation, t.rotation); lastRotation = t.rotation; deltaTime = Time.deltaTime; } public void WakeUp(float velocityWeight, float angularVelocityWeight) { if (updateAnchor) { joint.connectedAnchor = t.InverseTransformPoint(c.position); } r.isKinematic = false; if (velocityWeight != 0f) { r.linearVelocity = deltaPosition / deltaTime * velocityWeight; } if (angularVelocityWeight != 0f) { float angle = 0f; Vector3 axis = Vector3.zero; deltaRotation.ToAngleAxis(out angle, out axis); angle *= MathF.PI / 180f; angle /= deltaTime; axis *= angle * angularVelocityWeight; r.angularVelocity = Vector3.ClampMagnitude(axis, r.maxAngularVelocity); } r.WakeUp(); } } public class Child { public Transform t; public Vector3 localPosition; public Quaternion localRotation; public Child(Transform transform) { t = transform; localPosition = t.localPosition; localRotation = t.localRotation; } public void FixTransform(float weight) { if (!(weight <= 0f)) { if (weight >= 1f) { t.localPosition = localPosition; t.localRotation = localRotation; } else { t.localPosition = Vector3.Lerp(t.localPosition, localPosition, weight); t.localRotation = Quaternion.Lerp(t.localRotation, localRotation, weight); } } } public void StoreLocalState() { localPosition = t.localPosition; localRotation = t.localRotation; } } [Tooltip("If you have multiple IK components, then this should be the one that solves last each frame.")] public IK ik; [Tooltip("How long does it take to blend from ragdoll to animation?")] public float ragdollToAnimationTime = 0.2f; [Tooltip("If true, IK can be used on top of physical ragdoll simulation.")] public bool applyIkOnRagdoll; [Tooltip("How much velocity transfer from animation to ragdoll?")] public float applyVelocity = 1f; [Tooltip("How much angular velocity to transfer from animation to ragdoll?")] public float applyAngularVelocity = 1f; private Animator animator; private Rigidbone[] rigidbones = new Rigidbone[0]; private Child[] children = new Child[0]; private bool enableRagdollFlag; private AnimatorUpdateMode animatorUpdateMode; private IK[] allIKComponents = new IK[0]; private bool[] fixTransforms = new bool[0]; private float ragdollWeight; private float ragdollWeightV; private bool fixedFrame; private bool[] disabledIKComponents = new bool[0]; private bool animatorDisabled; private bool isRagdoll { get { if (!rigidbones[0].r.isKinematic) { return !animator.enabled; } return false; } } private bool ikUsed { get { if (ik == null) { return false; } if (ik.enabled && ik.GetIKSolver().IKPositionWeight > 0f) { return true; } IK[] array = allIKComponents; foreach (IK iK in array) { if (iK.enabled && iK.GetIKSolver().IKPositionWeight > 0f) { return true; } } return false; } } public void EnableRagdoll() { if (!isRagdoll) { StopAllCoroutines(); enableRagdollFlag = true; } } public void DisableRagdoll() { if (isRagdoll) { StoreLocalState(); StopAllCoroutines(); StartCoroutine(DisableRagdollSmooth()); } } public void Start() { animator = GetComponent(); allIKComponents = GetComponentsInChildren(); disabledIKComponents = new bool[allIKComponents.Length]; fixTransforms = new bool[allIKComponents.Length]; if (ik != null) { IKSolver iKSolver = ik.GetIKSolver(); iKSolver.OnPostUpdate = (IKSolver.UpdateDelegate)Delegate.Combine(iKSolver.OnPostUpdate, new IKSolver.UpdateDelegate(AfterLastIK)); } Rigidbody[] componentsInChildren = GetComponentsInChildren(); int num = ((componentsInChildren[0].gameObject == base.gameObject) ? 1 : 0); rigidbones = new Rigidbone[(num == 0) ? componentsInChildren.Length : (componentsInChildren.Length - 1)]; for (int i = 0; i < rigidbones.Length; i++) { rigidbones[i] = new Rigidbone(componentsInChildren[i + num]); } Transform[] componentsInChildren2 = GetComponentsInChildren(); children = new Child[componentsInChildren2.Length - 1]; for (int j = 0; j < children.Length; j++) { children[j] = new Child(componentsInChildren2[j + 1]); } } private IEnumerator DisableRagdollSmooth() { for (int i = 0; i < rigidbones.Length; i++) { rigidbones[i].r.isKinematic = true; } for (int j = 0; j < allIKComponents.Length; j++) { allIKComponents[j].fixTransforms = fixTransforms[j]; if (disabledIKComponents[j]) { allIKComponents[j].enabled = true; } } animator.updateMode = animatorUpdateMode; if (animatorDisabled) { animator.enabled = true; animatorDisabled = false; } while (ragdollWeight > 0f) { ragdollWeight = Mathf.SmoothDamp(ragdollWeight, 0f, ref ragdollWeightV, ragdollToAnimationTime); if (ragdollWeight < 0.001f) { ragdollWeight = 0f; } yield return null; } yield return null; } public void Update() { if (!isRagdoll) { return; } if (!applyIkOnRagdoll) { bool flag = false; for (int i = 0; i < allIKComponents.Length; i++) { if (allIKComponents[i].enabled) { flag = true; break; } } if (flag) { for (int j = 0; j < allIKComponents.Length; j++) { disabledIKComponents[j] = false; } } for (int k = 0; k < allIKComponents.Length; k++) { if (allIKComponents[k].enabled) { allIKComponents[k].enabled = false; disabledIKComponents[k] = true; } } return; } bool flag2 = false; for (int l = 0; l < allIKComponents.Length; l++) { if (disabledIKComponents[l]) { flag2 = true; break; } } if (!flag2) { return; } for (int m = 0; m < allIKComponents.Length; m++) { if (disabledIKComponents[m]) { allIKComponents[m].enabled = true; } } for (int n = 0; n < allIKComponents.Length; n++) { disabledIKComponents[n] = false; } } public void FixedUpdate() { if (isRagdoll && applyIkOnRagdoll) { FixTransforms(1f); } fixedFrame = true; } public void LateUpdate() { if (animator.updateMode != AnimatorUpdateMode.Fixed || (animator.updateMode == AnimatorUpdateMode.Fixed && fixedFrame)) { AfterAnimation(); } fixedFrame = false; if (!ikUsed) { OnFinalPose(); } } private void AfterLastIK() { if (ikUsed) { OnFinalPose(); } } private void AfterAnimation() { if (isRagdoll) { StoreLocalState(); } else { FixTransforms(ragdollWeight); } } private void OnFinalPose() { if (!isRagdoll) { RecordVelocities(); } if (enableRagdollFlag) { RagdollEnabler(); } } private void RagdollEnabler() { StoreLocalState(); for (int i = 0; i < allIKComponents.Length; i++) { disabledIKComponents[i] = false; } if (!applyIkOnRagdoll) { for (int j = 0; j < allIKComponents.Length; j++) { if (allIKComponents[j].enabled) { allIKComponents[j].enabled = false; disabledIKComponents[j] = true; } } } animatorUpdateMode = animator.updateMode; animator.updateMode = AnimatorUpdateMode.Fixed; if (animator.enabled) { animator.enabled = false; animatorDisabled = true; } for (int k = 0; k < rigidbones.Length; k++) { rigidbones[k].WakeUp(applyVelocity, applyAngularVelocity); } for (int l = 0; l < fixTransforms.Length; l++) { fixTransforms[l] = allIKComponents[l].fixTransforms; allIKComponents[l].fixTransforms = false; } ragdollWeight = 1f; ragdollWeightV = 0f; enableRagdollFlag = false; } private void RecordVelocities() { Rigidbone[] array = rigidbones; for (int i = 0; i < array.Length; i++) { array[i].RecordVelocity(); } } private void StoreLocalState() { Child[] array = children; for (int i = 0; i < array.Length; i++) { array[i].StoreLocalState(); } } private void FixTransforms(float weight) { Child[] array = children; for (int i = 0; i < array.Length; i++) { array[i].FixTransform(weight); } } private void OnDestroy() { if (ik != null) { IKSolver iKSolver = ik.GetIKSolver(); iKSolver.OnPostUpdate = (IKSolver.UpdateDelegate)Delegate.Remove(iKSolver.OnPostUpdate, new IKSolver.UpdateDelegate(AfterLastIK)); } } } }