GrounderIK.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. using UnityEngine;
  2. using System.Collections;
  3. namespace RootMotion.FinalIK {
  4. /// <summary>
  5. /// Grounding for LimbIK, CCD and/or FABRIK solvers.
  6. /// </summary>
  7. [HelpURL("http://www.root-motion.com/finalikdox/html/page9.html")]
  8. [AddComponentMenu("Scripts/RootMotion.FinalIK/Grounder/Grounder IK")]
  9. public class GrounderIK: Grounder {
  10. // Open the User Manual URL
  11. [ContextMenu("User Manual")]
  12. protected override void OpenUserManual() {
  13. Application.OpenURL("http://www.root-motion.com/finalikdox/html/page9.html");
  14. }
  15. // Open the Script Reference URL
  16. [ContextMenu("Scrpt Reference")]
  17. protected override void OpenScriptReference() {
  18. Application.OpenURL("http://www.root-motion.com/finalikdox/html/class_root_motion_1_1_final_i_k_1_1_grounder_i_k.html");
  19. }
  20. #region Main Interface
  21. /// <summary>
  22. /// The leg %IK componets (can be any type of IK component).
  23. /// </summary>
  24. public IK[] legs;
  25. /// <summary>
  26. /// The pelvis transform. Common ancestor of all the legs.
  27. /// </summary>
  28. [Tooltip("The pelvis transform. Common ancestor of all the legs.")]
  29. public Transform pelvis;
  30. /// <summary>
  31. /// The root Transform of the character, with the rigidbody and the collider.
  32. /// </summary>
  33. [Tooltip("The root Transform of the character, with the rigidbody and the collider.")]
  34. public Transform characterRoot;
  35. /// <summary>
  36. /// The weight of rotating the character root to the ground normal (range: 0 - 1).
  37. /// </summary>
  38. [Tooltip("The weight of rotating the character root to the ground normal (range: 0 - 1).")]
  39. [Range(0f, 1f)]
  40. public float rootRotationWeight;
  41. /// <summary>
  42. /// The speed of rotating the character root to the ground normal (range: 0 - inf).
  43. /// </summary>
  44. [Tooltip("The speed of rotating the character root to the ground normal (range: 0 - inf).")]
  45. public float rootRotationSpeed = 5f;
  46. /// <summary>
  47. /// The maximum angle of root rotation (range: 0 - 90).
  48. /// </summary>
  49. [Tooltip("The maximum angle of root rotation (range: 0 - 90).")]
  50. public float maxRootRotationAngle = 45f;
  51. #endregion Main Interface
  52. public override void ResetPosition() {
  53. solver.Reset();
  54. }
  55. private Transform[] feet = new Transform[0];
  56. private Quaternion[] footRotations = new Quaternion[0];
  57. private Vector3 animatedPelvisLocalPosition, solvedPelvisLocalPosition;
  58. private int solvedFeet;
  59. private bool solved;
  60. private float lastWeight;
  61. private Rigidbody characterRootRigidbody;
  62. // Can we initiate the Grounding?
  63. private bool IsReadyToInitiate() {
  64. if (pelvis == null) return false;
  65. if (legs.Length == 0) return false;
  66. foreach (IK leg in legs) {
  67. if (leg == null) return false;
  68. if (leg is FullBodyBipedIK) {
  69. LogWarning("GrounderIK does not support FullBodyBipedIK, use CCDIK, FABRIK, LimbIK or TrigonometricIK instead. If you want to use FullBodyBipedIK, use the GrounderFBBIK component.");
  70. return false;
  71. }
  72. if (leg is FABRIKRoot) {
  73. LogWarning("GrounderIK does not support FABRIKRoot, use CCDIK, FABRIK, LimbIK or TrigonometricIK instead.");
  74. return false;
  75. }
  76. if (leg is AimIK) {
  77. LogWarning("GrounderIK does not support AimIK, use CCDIK, FABRIK, LimbIK or TrigonometricIK instead.");
  78. return false;
  79. }
  80. }
  81. return true;
  82. }
  83. // Weigh out the IK solvers properly when the component is disabled
  84. void OnDisable() {
  85. if (!initiated) return;
  86. for (int i = 0; i < legs.Length; i++) {
  87. if (legs[i] != null) legs[i].GetIKSolver().IKPositionWeight = 0f;
  88. }
  89. }
  90. // Initiate once we have all the required components
  91. void Update() {
  92. weight = Mathf.Clamp(weight, 0f, 1f);
  93. if (weight <= 0f) return;
  94. solved = false;
  95. if (initiated) {
  96. // Clamping values
  97. rootRotationWeight = Mathf.Clamp(rootRotationWeight, 0f, 1f);
  98. rootRotationSpeed = Mathf.Clamp(rootRotationSpeed, 0f, rootRotationSpeed);
  99. // Root rotation
  100. if (characterRoot != null && rootRotationSpeed > 0f && rootRotationWeight > 0f && solver.isGrounded) {
  101. Vector3 normal = solver.GetLegsPlaneNormal();
  102. // Root rotation weight
  103. if (rootRotationWeight < 1f) {
  104. normal = Vector3.Slerp(Vector3.up, normal, rootRotationWeight);
  105. }
  106. // Root rotation limit
  107. Quaternion upRotation = Quaternion.FromToRotation(transform.up, Vector3.up) * characterRoot.rotation;
  108. Quaternion rotationTarget = Quaternion.RotateTowards(upRotation, Quaternion.FromToRotation(transform.up, normal) * characterRoot.rotation, maxRootRotationAngle);
  109. // Rotate the root
  110. if (characterRootRigidbody == null) {
  111. characterRoot.rotation = Quaternion.Lerp(characterRoot.rotation, rotationTarget, Time.deltaTime * rootRotationSpeed);
  112. } else {
  113. characterRootRigidbody.MoveRotation(Quaternion.Lerp(characterRoot.rotation, rotationTarget, Time.deltaTime * rootRotationSpeed));
  114. }
  115. }
  116. return;
  117. }
  118. if (!IsReadyToInitiate()) return;
  119. Initiate();
  120. }
  121. private void Initiate() {
  122. // Building arrays
  123. feet = new Transform[legs.Length];
  124. footRotations = new Quaternion[legs.Length];
  125. for (int i = 0; i < feet.Length; i++) footRotations[i] = Quaternion.identity;
  126. // Gathering the last bones of the IK solvers as feet
  127. for (int i = 0; i < legs.Length; i++) {
  128. IKSolver.Point[] points = legs[i].GetIKSolver().GetPoints();
  129. feet[i] = points[points.Length - 1].transform;
  130. // Add to the update delegates of each ik solver
  131. legs[i].GetIKSolver().OnPreUpdate += OnSolverUpdate;
  132. legs[i].GetIKSolver().OnPostUpdate += OnPostSolverUpdate;
  133. }
  134. // Store the default localPosition of the pelvis
  135. animatedPelvisLocalPosition = pelvis.localPosition;
  136. // Initiate the Grounding
  137. solver.Initiate(transform, feet);
  138. for (int i = 0; i < legs.Length; i++) {
  139. if (legs [i] is LegIK) {
  140. solver.legs[i].invertFootCenter = true;
  141. }
  142. }
  143. characterRootRigidbody = characterRoot.GetComponent<Rigidbody>();
  144. initiated = true;
  145. }
  146. // Called before updating the first IK solver
  147. private void OnSolverUpdate() {
  148. if (!enabled) return;
  149. if (weight <= 0f) {
  150. if (lastWeight <= 0f) return;
  151. // Weigh out the limb solvers properly
  152. OnDisable();
  153. }
  154. lastWeight = weight;
  155. // If another IK has already solved in this frame, do nothing
  156. if (solved) return;
  157. if (OnPreGrounder != null) OnPreGrounder();
  158. // If the pelvis local position has not changed since last solved state, consider it unanimated
  159. if (pelvis.localPosition != solvedPelvisLocalPosition) animatedPelvisLocalPosition = pelvis.localPosition;
  160. else pelvis.localPosition = animatedPelvisLocalPosition;
  161. // Update the Grounding
  162. solver.Update();
  163. // Update the IKPositions and IKPositonWeights of the legs
  164. for (int i = 0; i < legs.Length; i++) SetLegIK(i);
  165. // Move the pelvis
  166. pelvis.position += solver.pelvis.IKOffset * weight;
  167. solved = true;
  168. solvedFeet = 0;
  169. if (OnPostGrounder != null) OnPostGrounder();
  170. }
  171. // Set the IK position and weight for a limb
  172. private void SetLegIK(int index) {
  173. footRotations[index] = feet[index].rotation;
  174. if (legs [index] is LegIK) {
  175. (legs[index].GetIKSolver() as IKSolverLeg).IKRotation = Quaternion.Slerp(Quaternion.identity, solver.legs[index].rotationOffset, weight) * footRotations[index];
  176. (legs[index].GetIKSolver() as IKSolverLeg).IKRotationWeight = 1f;
  177. }
  178. legs[index].GetIKSolver().IKPosition = solver.legs[index].IKPosition;
  179. legs[index].GetIKSolver().IKPositionWeight = weight;
  180. }
  181. // Rotating the feet after IK has finished
  182. private void OnPostSolverUpdate() {
  183. if (weight <= 0f) return;
  184. if (!enabled) return;
  185. // Only do this after the last IK solver has finished
  186. solvedFeet ++;
  187. if (solvedFeet < feet.Length) return;
  188. solved = false;
  189. for (int i = 0; i < feet.Length; i++) {
  190. feet[i].rotation = Quaternion.Slerp(Quaternion.identity, solver.legs[i].rotationOffset, weight) * footRotations[i];
  191. }
  192. // Store the local position of the pelvis so we know it it changes
  193. solvedPelvisLocalPosition = pelvis.localPosition;
  194. }
  195. // Cleaning up the delegates
  196. void OnDestroy() {
  197. if (initiated) {
  198. foreach (IK leg in legs) {
  199. if (leg != null) {
  200. leg.GetIKSolver().OnPreUpdate -= OnSolverUpdate;
  201. leg.GetIKSolver().OnPostUpdate -= OnPostSolverUpdate;
  202. }
  203. }
  204. }
  205. }
  206. }
  207. }