RagdollUtility.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. using UnityEngine;
  2. using System.Collections;
  3. using RootMotion.FinalIK;
  4. namespace RootMotion.FinalIK {
  5. /// <summary>
  6. /// Ragdoll Utility controls switching characters in and out of ragdoll mode. It also enables you to use IK effects on top of ragdoll simulation.
  7. /// </summary>
  8. public class RagdollUtility : MonoBehaviour {
  9. #region Main Interface
  10. [Tooltip("If you have multiple IK components, then this should be the one that solves last each frame.")]
  11. /// <summary>
  12. /// If you have multiple IK components, then this should be the one that solves last each frame.
  13. /// </summary>
  14. public IK ik;
  15. [Tooltip("How long does it take to blend from ragdoll to animation?")]
  16. /// <summary>
  17. /// How long does it take to blend from ragdoll to animation?
  18. /// </summary>
  19. public float ragdollToAnimationTime = 0.2f;
  20. [Tooltip("If true, IK can be used on top of physical ragdoll simulation.")]
  21. /// <summary>
  22. /// If true, IK can be used on top of physical ragdoll simulation.
  23. /// </summary>
  24. public bool applyIkOnRagdoll;
  25. [Tooltip("How much velocity transfer from animation to ragdoll?")]
  26. /// <summary>
  27. /// How much velocity transfer from animation to ragdoll?
  28. /// </summary>
  29. public float applyVelocity = 1f;
  30. [Tooltip("How much angular velocity to transfer from animation to ragdoll?")]
  31. /// <summary>
  32. /// How much angular velocity to transfer from animation to ragdoll?
  33. /// </summary>
  34. public float applyAngularVelocity = 1f;
  35. /// <summary>
  36. /// Switches to ragdoll.
  37. /// </summary>
  38. public void EnableRagdoll() {
  39. if (isRagdoll) return;
  40. StopAllCoroutines();
  41. enableRagdollFlag = true;
  42. }
  43. /// <summary>
  44. /// Blends back to animation.
  45. /// </summary>
  46. public void DisableRagdoll() {
  47. if (!isRagdoll) return;
  48. StoreLocalState();
  49. StopAllCoroutines();
  50. StartCoroutine(DisableRagdollSmooth());
  51. }
  52. #endregion Main Interface
  53. // The rigidbodies and their associates
  54. public class Rigidbone {
  55. public Rigidbody r;
  56. public Transform t;
  57. public Collider collider;
  58. public Joint joint;
  59. public Rigidbody c;
  60. public bool updateAnchor;
  61. public Vector3 deltaPosition;
  62. public Quaternion deltaRotation;
  63. public float deltaTime;
  64. public Vector3 lastPosition;
  65. public Quaternion lastRotation;
  66. // Constructor
  67. public Rigidbone (Rigidbody r) {
  68. this.r = r;
  69. t = r.transform;
  70. joint = t.GetComponent<Joint>();
  71. collider = t.GetComponent<Collider>();
  72. if (joint != null) {
  73. c = joint.connectedBody;
  74. updateAnchor = c != null;
  75. }
  76. lastPosition = t.position;
  77. lastRotation = t.rotation;
  78. }
  79. // Store position and rotation deltas
  80. public void RecordVelocity() {
  81. deltaPosition = t.position - lastPosition;
  82. lastPosition = t.position;
  83. deltaRotation = RootMotion.QuaTools.FromToRotation(lastRotation, t.rotation);
  84. lastRotation = t.rotation;
  85. deltaTime = Time.deltaTime;
  86. }
  87. // Go to ragdoll
  88. public void WakeUp(float velocityWeight, float angularVelocityWeight) {
  89. // Joint anchors need to be updated when there are animated bones in between ragdoll bones
  90. if (updateAnchor) {
  91. joint.connectedAnchor = t.InverseTransformPoint(c.position);
  92. }
  93. r.isKinematic = false;
  94. // Transfer velocity from animation
  95. if (velocityWeight != 0f) {
  96. r.velocity = (deltaPosition / deltaTime) * velocityWeight;
  97. }
  98. // Transfer angular velocity from animation
  99. if (angularVelocityWeight != 0f) {
  100. float angle = 0f;
  101. Vector3 axis = Vector3.zero;
  102. deltaRotation.ToAngleAxis(out angle, out axis);
  103. angle *= Mathf.Deg2Rad;
  104. angle /= deltaTime;
  105. axis *= angle * angularVelocityWeight;
  106. r.angularVelocity = Vector3.ClampMagnitude(axis, r.maxAngularVelocity);
  107. }
  108. r.WakeUp();
  109. }
  110. }
  111. // All child Transforms of the root.
  112. public class Child {
  113. public Transform t;
  114. public Vector3 localPosition;
  115. public Quaternion localRotation;
  116. // Constructor
  117. public Child(Transform transform) {
  118. t = transform;
  119. localPosition = t.localPosition;
  120. localRotation = t.localRotation;
  121. }
  122. // Force to the last stored local state
  123. public void FixTransform(float weight) {
  124. if (weight <= 0f) return;
  125. if (weight >= 1f) {
  126. t.localPosition = localPosition;
  127. t.localRotation = localRotation;
  128. return;
  129. }
  130. t.localPosition = Vector3.Lerp(t.localPosition, localPosition, weight);
  131. t.localRotation = Quaternion.Lerp(t.localRotation, localRotation, weight);
  132. }
  133. // Remember the local state, that is the local position and rotation of the transform
  134. public void StoreLocalState() {
  135. localPosition = t.localPosition;
  136. localRotation = t.localRotation;
  137. }
  138. }
  139. private Animator animator;
  140. private Rigidbone[] rigidbones = new Rigidbone[0];
  141. private Child[] children = new Child[0];
  142. private bool enableRagdollFlag;
  143. private AnimatorUpdateMode animatorUpdateMode;
  144. private IK[] allIKComponents = new IK[0];
  145. private bool[] fixTransforms = new bool[0];
  146. private float ragdollWeight;
  147. private float ragdollWeightV;
  148. private bool fixedFrame;
  149. private bool[] disabledIKComponents = new bool[0];
  150. // Find all necessary components and initiate
  151. public void Start() {
  152. animator = GetComponent<Animator>();
  153. allIKComponents = (IK[])GetComponentsInChildren<IK>();
  154. disabledIKComponents = new bool[allIKComponents.Length];
  155. fixTransforms = new bool[allIKComponents.Length];
  156. if (ik != null) ik.GetIKSolver().OnPostUpdate += AfterLastIK;
  157. // Gather all the rigidbodies and their associates
  158. Rigidbody[] rigidbodies = (Rigidbody[])GetComponentsInChildren<Rigidbody>();
  159. int firstIndex = rigidbodies[0].gameObject == gameObject? 1: 0;
  160. rigidbones = new Rigidbone[firstIndex == 0? rigidbodies.Length: rigidbodies.Length - 1];
  161. for (int i = 0; i < rigidbones.Length; i++) {
  162. rigidbones[i] = new Rigidbone(rigidbodies[i + firstIndex]);
  163. }
  164. // Find all the child Transforms
  165. Transform[] C = (Transform[])GetComponentsInChildren<Transform>();
  166. children = new Child[C.Length - 1];
  167. for (int i = 0; i < children.Length; i++) {
  168. children[i] = new Child(C[i + 1]);
  169. }
  170. }
  171. // Smoothly blends out of Ragdoll
  172. private IEnumerator DisableRagdollSmooth() {
  173. // ...make all rigidbodies kinematic
  174. for (int i = 0; i < rigidbones.Length; i++) {
  175. rigidbones[i].r.isKinematic = true;
  176. }
  177. // Reset IK components
  178. for (int i = 0; i < allIKComponents.Length; i++) {
  179. allIKComponents[i].fixTransforms = fixTransforms[i];
  180. if (disabledIKComponents[i]) allIKComponents[i].enabled = true;
  181. }
  182. // Animator has not updated yet.
  183. animator.updateMode = animatorUpdateMode;
  184. animator.enabled = true;
  185. // Blend back to animation
  186. while (ragdollWeight > 0f) {
  187. ragdollWeight = Mathf.SmoothDamp(ragdollWeight, 0f, ref ragdollWeightV, ragdollToAnimationTime);
  188. if (ragdollWeight < 0.001f) ragdollWeight = 0f;
  189. yield return null;
  190. }
  191. yield return null;
  192. }
  193. void Update() {
  194. if (!isRagdoll) return;
  195. // Disable IK components if applyIKOnRagdoll has been set to false while in ragdoll.
  196. if (!applyIkOnRagdoll) {
  197. bool disableIK = false;
  198. for (int i = 0; i < allIKComponents.Length; i++) {
  199. if (allIKComponents[i].enabled) {
  200. disableIK = true;
  201. break;
  202. }
  203. }
  204. if (disableIK) {
  205. for (int i = 0; i < allIKComponents.Length; i++) disabledIKComponents[i] = false;
  206. }
  207. for (int i = 0; i < allIKComponents.Length; i++) {
  208. if (allIKComponents[i].enabled) {
  209. allIKComponents[i].enabled = false;
  210. disabledIKComponents[i] = true;
  211. }
  212. }
  213. } else {
  214. // Enable IK components if applyIKOnRagdoll has been set to true while in ragdoll
  215. bool enableIK = false;
  216. for (int i = 0; i < allIKComponents.Length; i++) {
  217. if (disabledIKComponents[i]) {
  218. enableIK = true;
  219. break;
  220. }
  221. }
  222. if (enableIK) {
  223. for (int i = 0; i < allIKComponents.Length; i++) {
  224. if (disabledIKComponents[i]) {
  225. allIKComponents[i].enabled = true;
  226. }
  227. }
  228. for (int i = 0; i < allIKComponents.Length; i++) disabledIKComponents[i] = false;
  229. }
  230. }
  231. }
  232. void FixedUpdate() {
  233. // When in ragdoll, move the bones to where they were after the last physics simulation, so IK won't screw up the physics
  234. if (isRagdoll && applyIkOnRagdoll) FixTransforms(1f);
  235. fixedFrame = true;
  236. }
  237. void LateUpdate() {
  238. // When Mecanim has animated...
  239. if (animator.updateMode != AnimatorUpdateMode.AnimatePhysics || (animator.updateMode == AnimatorUpdateMode.AnimatePhysics && fixedFrame)) {
  240. AfterAnimation();
  241. }
  242. fixedFrame = false;
  243. // No IK so the final pose of the character is the current pose
  244. if (!ikUsed) OnFinalPose();
  245. }
  246. // Called by the last IK component after it has updated
  247. private void AfterLastIK() {
  248. // We should have the final pose of the character
  249. if (ikUsed) OnFinalPose();
  250. }
  251. // When animation has been applied by Mecanim
  252. private void AfterAnimation() {
  253. if (isRagdoll) {
  254. // If is ragdoll, no animation has been applied, but we need to remember the pose after the physics step just the same
  255. StoreLocalState();
  256. } else {
  257. // Blending from ragdoll to animation. When ragdollWeight is zero, nothing happens here
  258. FixTransforms(ragdollWeight);
  259. }
  260. }
  261. // When we have the final pose of the character for this frame
  262. private void OnFinalPose() {
  263. if (!isRagdoll) RecordVelocities();
  264. if (enableRagdollFlag) RagdollEnabler();
  265. }
  266. // Switching to ragdoll
  267. private void RagdollEnabler() {
  268. // Remember the last animated pose
  269. StoreLocalState();
  270. // Disable IK components if necessary
  271. for (int i = 0; i < allIKComponents.Length; i++) disabledIKComponents[i] = false;
  272. if (!applyIkOnRagdoll) {
  273. for (int i = 0; i < allIKComponents.Length; i++) {
  274. if (allIKComponents[i].enabled) {
  275. allIKComponents[i].enabled = false;
  276. disabledIKComponents[i] = true;
  277. }
  278. }
  279. }
  280. // Switch Animator update mode to AnimatePhysics, so IK is updated in the fixed time step
  281. animatorUpdateMode = animator.updateMode;
  282. animator.updateMode = AnimatorUpdateMode.AnimatePhysics;
  283. // Disable the Animator so it won't overwrite physics
  284. animator.enabled = false;
  285. for (int i = 0; i < rigidbones.Length; i++) rigidbones[i].WakeUp(applyVelocity, applyAngularVelocity);
  286. // Remember some variables so we can revert to them when coming back from ragdoll
  287. for (int i = 0; i < fixTransforms.Length; i++) {
  288. fixTransforms[i] = allIKComponents[i].fixTransforms;
  289. allIKComponents[i].fixTransforms = false;
  290. }
  291. ragdollWeight = 1f;
  292. ragdollWeightV = 0f;
  293. enableRagdollFlag = false;
  294. }
  295. // Is the character currently in ragdoll mode?
  296. private bool isRagdoll { get { return !rigidbones[0].r.isKinematic && !animator.enabled; }}
  297. // Store position and rotation deltas for all the rigidbodies
  298. private void RecordVelocities() {
  299. foreach (Rigidbone r in rigidbones) r.RecordVelocity();
  300. }
  301. // Is there any IK components acting on the character?
  302. private bool ikUsed {
  303. get {
  304. if (ik == null) return false;
  305. if (ik.enabled && ik.GetIKSolver().IKPositionWeight > 0) return true;
  306. foreach (IK k in allIKComponents) {
  307. if (k.enabled && k.GetIKSolver().IKPositionWeight > 0) return true;
  308. }
  309. return false;
  310. }
  311. }
  312. // Stored the current pose of the character
  313. private void StoreLocalState() {
  314. foreach (Child c in children) c.StoreLocalState();
  315. }
  316. // Interpolate the character to the last stored pose (see StoreLocalState)
  317. private void FixTransforms(float weight) {
  318. foreach (Child c in children) c.FixTransform(weight);
  319. }
  320. // Cleaning up the delegates
  321. void OnDestroy() {
  322. if (ik != null) {
  323. ik.GetIKSolver().OnPostUpdate -= AfterLastIK;
  324. }
  325. }
  326. }
  327. }