123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- using UnityEngine;
- using System.Collections;
- using RootMotion.FinalIK;
- namespace RootMotion.FinalIK {
- /// <summary>
- /// Ragdoll Utility controls switching characters in and out of ragdoll mode. It also enables you to use IK effects on top of ragdoll simulation.
- /// </summary>
- public class RagdollUtility : MonoBehaviour {
- #region Main Interface
- [Tooltip("If you have multiple IK components, then this should be the one that solves last each frame.")]
- /// <summary>
- /// If you have multiple IK components, then this should be the one that solves last each frame.
- /// </summary>
- public IK ik;
- [Tooltip("How long does it take to blend from ragdoll to animation?")]
- /// <summary>
- /// How long does it take to blend from ragdoll to animation?
- /// </summary>
- public float ragdollToAnimationTime = 0.2f;
- [Tooltip("If true, IK can be used on top of physical ragdoll simulation.")]
- /// <summary>
- /// If true, IK can be used on top of physical ragdoll simulation.
- /// </summary>
- public bool applyIkOnRagdoll;
- [Tooltip("How much velocity transfer from animation to ragdoll?")]
- /// <summary>
- /// How much velocity transfer from animation to ragdoll?
- /// </summary>
- public float applyVelocity = 1f;
- [Tooltip("How much angular velocity to transfer from animation to ragdoll?")]
- /// <summary>
- /// How much angular velocity to transfer from animation to ragdoll?
- /// </summary>
- public float applyAngularVelocity = 1f;
- /// <summary>
- /// Switches to ragdoll.
- /// </summary>
- public void EnableRagdoll() {
- if (isRagdoll) return;
-
- StopAllCoroutines();
- enableRagdollFlag = true;
- }
- /// <summary>
- /// Blends back to animation.
- /// </summary>
- public void DisableRagdoll() {
- if (!isRagdoll) return;
- StoreLocalState();
- StopAllCoroutines();
- StartCoroutine(DisableRagdollSmooth());
- }
- #endregion Main Interface
- // The rigidbodies and their associates
- 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;
- // Constructor
- public Rigidbone (Rigidbody r) {
- this.r = r;
- t = r.transform;
- joint = t.GetComponent<Joint>();
- collider = t.GetComponent<Collider>();
- if (joint != null) {
- c = joint.connectedBody;
- updateAnchor = c != null;
- }
-
- lastPosition = t.position;
- lastRotation = t.rotation;
- }
- // Store position and rotation deltas
- public void RecordVelocity() {
- deltaPosition = t.position - lastPosition;
- lastPosition = t.position;
- deltaRotation = RootMotion.QuaTools.FromToRotation(lastRotation, t.rotation);
- lastRotation = t.rotation;
- deltaTime = Time.deltaTime;
- }
- // Go to ragdoll
- public void WakeUp(float velocityWeight, float angularVelocityWeight) {
- // Joint anchors need to be updated when there are animated bones in between ragdoll bones
- if (updateAnchor) {
- joint.connectedAnchor = t.InverseTransformPoint(c.position);
- }
- r.isKinematic = false;
- // Transfer velocity from animation
- if (velocityWeight != 0f) {
- r.velocity = (deltaPosition / deltaTime) * velocityWeight;
- }
- // Transfer angular velocity from animation
- if (angularVelocityWeight != 0f) {
- float angle = 0f;
- Vector3 axis = Vector3.zero;
- deltaRotation.ToAngleAxis(out angle, out axis);
- angle *= Mathf.Deg2Rad;
- angle /= deltaTime;
- axis *= angle * angularVelocityWeight;
- r.angularVelocity = Vector3.ClampMagnitude(axis, r.maxAngularVelocity);
- }
- r.WakeUp();
- }
- }
- // All child Transforms of the root.
- public class Child {
- public Transform t;
- public Vector3 localPosition;
- public Quaternion localRotation;
- // Constructor
- public Child(Transform transform) {
- t = transform;
- localPosition = t.localPosition;
- localRotation = t.localRotation;
- }
- // Force to the last stored local state
- public void FixTransform(float weight) {
- if (weight <= 0f) return;
-
- if (weight >= 1f) {
- t.localPosition = localPosition;
- t.localRotation = localRotation;
- return;
- }
-
- t.localPosition = Vector3.Lerp(t.localPosition, localPosition, weight);
- t.localRotation = Quaternion.Lerp(t.localRotation, localRotation, weight);
- }
- // Remember the local state, that is the local position and rotation of the transform
- public void StoreLocalState() {
- localPosition = t.localPosition;
- localRotation = t.localRotation;
- }
- }
- 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];
- // Find all necessary components and initiate
- public void Start() {
- animator = GetComponent<Animator>();
- allIKComponents = (IK[])GetComponentsInChildren<IK>();
- disabledIKComponents = new bool[allIKComponents.Length];
- fixTransforms = new bool[allIKComponents.Length];
- if (ik != null) ik.GetIKSolver().OnPostUpdate += AfterLastIK;
- // Gather all the rigidbodies and their associates
- Rigidbody[] rigidbodies = (Rigidbody[])GetComponentsInChildren<Rigidbody>();
- int firstIndex = rigidbodies[0].gameObject == gameObject? 1: 0;
- rigidbones = new Rigidbone[firstIndex == 0? rigidbodies.Length: rigidbodies.Length - 1];
- for (int i = 0; i < rigidbones.Length; i++) {
- rigidbones[i] = new Rigidbone(rigidbodies[i + firstIndex]);
- }
- // Find all the child Transforms
- Transform[] C = (Transform[])GetComponentsInChildren<Transform>();
- children = new Child[C.Length - 1];
- for (int i = 0; i < children.Length; i++) {
- children[i] = new Child(C[i + 1]);
- }
- }
- // Smoothly blends out of Ragdoll
- private IEnumerator DisableRagdollSmooth() {
- // ...make all rigidbodies kinematic
- for (int i = 0; i < rigidbones.Length; i++) {
- rigidbones[i].r.isKinematic = true;
- }
- // Reset IK components
- for (int i = 0; i < allIKComponents.Length; i++) {
- allIKComponents[i].fixTransforms = fixTransforms[i];
- if (disabledIKComponents[i]) allIKComponents[i].enabled = true;
- }
- // Animator has not updated yet.
- animator.updateMode = animatorUpdateMode;
- animator.enabled = true;
- // Blend back to animation
- while (ragdollWeight > 0f) {
- ragdollWeight = Mathf.SmoothDamp(ragdollWeight, 0f, ref ragdollWeightV, ragdollToAnimationTime);
- if (ragdollWeight < 0.001f) ragdollWeight = 0f;
-
- yield return null;
- }
-
- yield return null;
- }
- void Update() {
- if (!isRagdoll) return;
- // Disable IK components if applyIKOnRagdoll has been set to false while in ragdoll.
- if (!applyIkOnRagdoll) {
- bool disableIK = false;
- for (int i = 0; i < allIKComponents.Length; i++) {
- if (allIKComponents[i].enabled) {
- disableIK = true;
- break;
- }
- }
- if (disableIK) {
- for (int i = 0; i < allIKComponents.Length; i++) disabledIKComponents[i] = false;
- }
- for (int i = 0; i < allIKComponents.Length; i++) {
- if (allIKComponents[i].enabled) {
- allIKComponents[i].enabled = false;
- disabledIKComponents[i] = true;
- }
- }
- } else {
- // Enable IK components if applyIKOnRagdoll has been set to true while in ragdoll
- bool enableIK = false;
- for (int i = 0; i < allIKComponents.Length; i++) {
- if (disabledIKComponents[i]) {
- enableIK = true;
- break;
- }
- }
- if (enableIK) {
- for (int i = 0; i < allIKComponents.Length; i++) {
- if (disabledIKComponents[i]) {
- allIKComponents[i].enabled = true;
- }
- }
- for (int i = 0; i < allIKComponents.Length; i++) disabledIKComponents[i] = false;
- }
- }
- }
- void FixedUpdate() {
- // When in ragdoll, move the bones to where they were after the last physics simulation, so IK won't screw up the physics
- if (isRagdoll && applyIkOnRagdoll) FixTransforms(1f);
- fixedFrame = true;
- }
- void LateUpdate() {
- // When Mecanim has animated...
- if (animator.updateMode != AnimatorUpdateMode.AnimatePhysics || (animator.updateMode == AnimatorUpdateMode.AnimatePhysics && fixedFrame)) {
- AfterAnimation();
- }
- fixedFrame = false;
- // No IK so the final pose of the character is the current pose
- if (!ikUsed) OnFinalPose();
- }
- // Called by the last IK component after it has updated
- private void AfterLastIK() {
- // We should have the final pose of the character
- if (ikUsed) OnFinalPose();
- }
- // When animation has been applied by Mecanim
- private void AfterAnimation() {
- if (isRagdoll) {
- // If is ragdoll, no animation has been applied, but we need to remember the pose after the physics step just the same
- StoreLocalState();
- } else {
- // Blending from ragdoll to animation. When ragdollWeight is zero, nothing happens here
- FixTransforms(ragdollWeight);
- }
- }
- // When we have the final pose of the character for this frame
- private void OnFinalPose() {
- if (!isRagdoll) RecordVelocities();
- if (enableRagdollFlag) RagdollEnabler();
- }
- // Switching to ragdoll
- private void RagdollEnabler() {
- // Remember the last animated pose
- StoreLocalState();
- // Disable IK components if necessary
- for (int i = 0; i < allIKComponents.Length; i++) disabledIKComponents[i] = false;
- if (!applyIkOnRagdoll) {
- for (int i = 0; i < allIKComponents.Length; i++) {
- if (allIKComponents[i].enabled) {
- allIKComponents[i].enabled = false;
- disabledIKComponents[i] = true;
- }
- }
- }
- // Switch Animator update mode to AnimatePhysics, so IK is updated in the fixed time step
- animatorUpdateMode = animator.updateMode;
- animator.updateMode = AnimatorUpdateMode.AnimatePhysics;
- // Disable the Animator so it won't overwrite physics
- animator.enabled = false;
-
- for (int i = 0; i < rigidbones.Length; i++) rigidbones[i].WakeUp(applyVelocity, applyAngularVelocity);
- // Remember some variables so we can revert to them when coming back from ragdoll
- for (int i = 0; i < fixTransforms.Length; i++) {
- fixTransforms[i] = allIKComponents[i].fixTransforms;
- allIKComponents[i].fixTransforms = false;
- }
- ragdollWeight = 1f;
- ragdollWeightV = 0f;
- enableRagdollFlag = false;
- }
- // Is the character currently in ragdoll mode?
- private bool isRagdoll { get { return !rigidbones[0].r.isKinematic && !animator.enabled; }}
- // Store position and rotation deltas for all the rigidbodies
- private void RecordVelocities() {
- foreach (Rigidbone r in rigidbones) r.RecordVelocity();
- }
- // Is there any IK components acting on the character?
- private bool ikUsed {
- get {
- if (ik == null) return false;
- if (ik.enabled && ik.GetIKSolver().IKPositionWeight > 0) return true;
- foreach (IK k in allIKComponents) {
- if (k.enabled && k.GetIKSolver().IKPositionWeight > 0) return true;
- }
- return false;
- }
- }
- // Stored the current pose of the character
- private void StoreLocalState() {
- foreach (Child c in children) c.StoreLocalState();
- }
- // Interpolate the character to the last stored pose (see StoreLocalState)
- private void FixTransforms(float weight) {
- foreach (Child c in children) c.FixTransform(weight);
- }
- // Cleaning up the delegates
- void OnDestroy() {
- if (ik != null) {
- ik.GetIKSolver().OnPostUpdate -= AfterLastIK;
- }
- }
- }
- }
|