RotationLimit.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. using UnityEngine;
  2. using System.Collections;
  3. namespace RootMotion.FinalIK
  4. {
  5. /// <summary>
  6. /// The base abstract class for all Rotation limits. Contains common functionality and static helper methods
  7. /// </summary>
  8. public abstract class RotationLimit : MonoBehaviour
  9. {
  10. #region Main Interface
  11. /// <summary>
  12. /// The main axis of the rotation limit.
  13. /// </summary>
  14. public Vector3 axis = Vector3.forward;
  15. /// <summary>
  16. /// Map the zero rotation point to the current local rotation of this gameobject.
  17. /// </summary>
  18. public void SetDefaultLocalRotation()
  19. {
  20. defaultLocalRotation = transform.localRotation;
  21. defaultLocalRotationSet = true;
  22. defaultLocalRotationOverride = false;
  23. }
  24. /// <summary>
  25. /// Map the zero rotation point to the specified rotation.
  26. /// </summary>
  27. public void SetDefaultLocalRotation(Quaternion localRotation)
  28. {
  29. defaultLocalRotation = localRotation;
  30. defaultLocalRotationSet = true;
  31. defaultLocalRotationOverride = true;
  32. }
  33. /// <summary>
  34. /// Returns the limited local rotation.
  35. /// </summary>
  36. public Quaternion GetLimitedLocalRotation(Quaternion localRotation, out bool changed)
  37. {
  38. // Making sure the Rotation Limit is initiated
  39. if (!initiated) Awake();
  40. // Subtracting defaultLocalRotation
  41. Quaternion rotation = Quaternion.Inverse(defaultLocalRotation) * localRotation;
  42. Quaternion limitedRotation = LimitRotation(rotation);
  43. #if UNITY_2018_3_OR_NEWER
  44. limitedRotation = Quaternion.Normalize(limitedRotation);
  45. #endif
  46. changed = limitedRotation != rotation;
  47. if (!changed) return localRotation;
  48. // Apply defaultLocalRotation back on
  49. return defaultLocalRotation * limitedRotation;
  50. }
  51. /// <summary>
  52. /// Apply the rotation limit to transform.localRotation. Returns true if the limit has changed the rotation.
  53. /// </summary>
  54. public bool Apply()
  55. {
  56. bool changed = false;
  57. transform.localRotation = GetLimitedLocalRotation(transform.localRotation, out changed);
  58. return changed;
  59. }
  60. /// <summary>
  61. /// Disable this instance making sure it is initiated. Use this if you intend to manually control the updating of this Rotation Limit.
  62. /// </summary>
  63. public void Disable()
  64. {
  65. if (initiated)
  66. {
  67. enabled = false;
  68. return;
  69. }
  70. Awake();
  71. enabled = false;
  72. }
  73. #endregion Main Interface
  74. /*
  75. * An arbitrary secondary axis that we get by simply switching the axes
  76. * */
  77. public Vector3 secondaryAxis { get { return new Vector3(axis.y, axis.z, axis.x); } }
  78. /*
  79. * Cross product of axis and secondaryAxis
  80. * */
  81. public Vector3 crossAxis { get { return Vector3.Cross(axis, secondaryAxis); } }
  82. /*
  83. * The default local rotation of the gameobject. By default stored in Awake.
  84. * */
  85. [HideInInspector] public Quaternion defaultLocalRotation;
  86. public bool defaultLocalRotationOverride { get; private set; }
  87. protected abstract Quaternion LimitRotation(Quaternion rotation);
  88. private bool initiated;
  89. private bool applicationQuit;
  90. private bool defaultLocalRotationSet;
  91. /*
  92. * Initiating the Rotation Limit
  93. * */
  94. void Awake()
  95. {
  96. // Store the local rotation to map the zero rotation point to the current rotation
  97. if (!defaultLocalRotationSet) SetDefaultLocalRotation();
  98. if (axis == Vector3.zero) Debug.LogError("Axis is Vector3.zero.");
  99. initiated = true;
  100. }
  101. /*
  102. * Using LateUpdate here because you most probably want to apply the limits after animation.
  103. * If you need precise control over the execution order, disable this script and call Apply() whenever you need
  104. * */
  105. void LateUpdate()
  106. {
  107. Apply();
  108. }
  109. /*
  110. * Logs the warning if no other warning has beed logged in this session.
  111. * */
  112. public void LogWarning(string message)
  113. {
  114. Warning.Log(message, transform);
  115. }
  116. #region Static helper methods for all Rotation Limits
  117. /*
  118. * Limits rotation to a single degree of freedom (along axis)
  119. * */
  120. protected static Quaternion Limit1DOF(Quaternion rotation, Vector3 axis)
  121. {
  122. return Quaternion.FromToRotation(rotation * axis, axis) * rotation;
  123. }
  124. /*
  125. * Applies uniform twist limit to the rotation
  126. * */
  127. protected static Quaternion LimitTwist(Quaternion rotation, Vector3 axis, Vector3 orthoAxis, float twistLimit)
  128. {
  129. twistLimit = Mathf.Clamp(twistLimit, 0, 180);
  130. if (twistLimit >= 180) return rotation;
  131. Vector3 normal = rotation * axis;
  132. Vector3 orthoTangent = orthoAxis;
  133. Vector3.OrthoNormalize(ref normal, ref orthoTangent);
  134. Vector3 rotatedOrthoTangent = rotation * orthoAxis;
  135. Vector3.OrthoNormalize(ref normal, ref rotatedOrthoTangent);
  136. Quaternion fixedRotation = Quaternion.FromToRotation(rotatedOrthoTangent, orthoTangent) * rotation;
  137. if (twistLimit <= 0) return fixedRotation;
  138. // Rotate from zero twist to free twist by the limited angle
  139. return Quaternion.RotateTowards(fixedRotation, rotation, twistLimit);
  140. }
  141. /*
  142. * Returns the angle between two vectors on a plane with the specified normal
  143. * */
  144. protected static float GetOrthogonalAngle(Vector3 v1, Vector3 v2, Vector3 normal)
  145. {
  146. Vector3.OrthoNormalize(ref normal, ref v1);
  147. Vector3.OrthoNormalize(ref normal, ref v2);
  148. return Vector3.Angle(v1, v2);
  149. }
  150. #endregion
  151. }
  152. }