IKSolverAim.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. using UnityEngine;
  2. using System.Collections;
  3. using System;
  4. namespace RootMotion.FinalIK {
  5. /// <summary>
  6. /// Rotates a hierarchy of bones to make a Transform aim at a target.
  7. /// If there are problems with continuity and the solver get's jumpy, make sure to keep IKPosition at a safe distance from the transform and try decreasing solver and bone weights.
  8. /// </summary>
  9. [System.Serializable]
  10. public class IKSolverAim : IKSolverHeuristic {
  11. #region Main Interface
  12. /// <summary>
  13. /// The transform that we want to aim at IKPosition.
  14. /// </summary>
  15. public Transform transform;
  16. /// <summary>
  17. /// The local axis of the Transform that you want to be aimed at IKPosition.
  18. /// </summary>
  19. public Vector3 axis = Vector3.forward;
  20. /// <summary>
  21. /// Keeps that axis of the Aim Transform directed at the polePosition.
  22. /// </summary>
  23. public Vector3 poleAxis = Vector3.up;
  24. /// <summary>
  25. /// The position in world space to keep the pole axis of the Aim Transform directed at.
  26. /// </summary>
  27. public Vector3 polePosition;
  28. /// <summary>
  29. /// The weight of the Pole.
  30. /// </summary>
  31. [Range(0f, 1f)]
  32. public float poleWeight;
  33. /// <summary>
  34. /// If assigned, will automatically set polePosition to the position of this Transform.
  35. /// </summary>
  36. public Transform poleTarget;
  37. /// <summary>
  38. /// Clamping rotation of the solver. 0 is free rotation, 1 is completely clamped to transform axis.
  39. /// </summary>
  40. [Range(0f, 1f)]
  41. public float clampWeight = 0.1f;
  42. /// <summary>
  43. /// Number of sine smoothing iterations applied to clamping to make it smoother.
  44. /// </summary>
  45. [Range(0, 2)]
  46. public int clampSmoothing = 2;
  47. /// <summary>
  48. /// Gets the angular offset.
  49. /// </summary>
  50. public float GetAngle() {
  51. return Vector3.Angle(transformAxis, IKPosition - transform.position);
  52. }
  53. /// <summary>
  54. /// Gets the Axis of the AimTransform is world space.
  55. /// </summary>
  56. public Vector3 transformAxis {
  57. get {
  58. return transform.rotation * axis;
  59. }
  60. }
  61. /// <summary>
  62. /// Gets the Pole Axis of the AimTransform is world space.
  63. /// </summary>
  64. public Vector3 transformPoleAxis {
  65. get {
  66. return transform.rotation * poleAxis;
  67. }
  68. }
  69. /// <summary>
  70. /// Called before each iteration of the solver.
  71. /// </summary>
  72. public IterationDelegate OnPreIteration;
  73. #endregion Main Interface
  74. protected override void OnInitiate() {
  75. if ((firstInitiation || !Application.isPlaying) && transform != null) {
  76. IKPosition = transform.position + transformAxis * 3f;
  77. polePosition = transform.position + transformPoleAxis * 3f;
  78. }
  79. // Disable Rotation Limits from updating to take control of their execution order
  80. for (int i = 0; i < bones.Length; i++) {
  81. if (bones[i].rotationLimit != null) bones[i].rotationLimit.Disable();
  82. }
  83. step = 1f / (float)bones.Length;
  84. if (Application.isPlaying) axis = axis.normalized;
  85. }
  86. protected override void OnUpdate() {
  87. if (axis == Vector3.zero) {
  88. if (!Warning.logged) LogWarning("IKSolverAim axis is Vector3.zero.");
  89. return;
  90. }
  91. if (poleAxis == Vector3.zero && poleWeight > 0f) {
  92. if (!Warning.logged) LogWarning("IKSolverAim poleAxis is Vector3.zero.");
  93. return;
  94. }
  95. if (target != null) IKPosition = target.position;
  96. if (poleTarget != null) polePosition = poleTarget.position;
  97. if (XY) IKPosition.z = bones[0].transform.position.z;
  98. // Clamping weights
  99. if (IKPositionWeight <= 0) return;
  100. IKPositionWeight = Mathf.Clamp(IKPositionWeight, 0f, 1f);
  101. // Rotation Limit on the Aim Transform
  102. if (transform != lastTransform) {
  103. transformLimit = transform.GetComponent<RotationLimit>();
  104. if (transformLimit != null) transformLimit.enabled = false;
  105. lastTransform = transform;
  106. }
  107. if (transformLimit != null) transformLimit.Apply();
  108. // In case transform becomes unassigned in runtime
  109. if (transform == null) {
  110. if (!Warning.logged) LogWarning("Aim Transform unassigned in Aim IK solver. Please Assign a Transform (lineal descendant to the last bone in the spine) that you want to be aimed at IKPosition");
  111. return;
  112. }
  113. clampWeight = Mathf.Clamp(clampWeight, 0f, 1f);
  114. clampedIKPosition = GetClampedIKPosition();
  115. Vector3 dir = clampedIKPosition - transform.position;
  116. dir = Vector3.Slerp(transformAxis * dir.magnitude, dir, IKPositionWeight);
  117. clampedIKPosition = transform.position + dir;
  118. // Iterating the solver
  119. for (int i = 0; i < maxIterations; i++) {
  120. // Optimizations
  121. if (i >= 1 && tolerance > 0 && GetAngle() < tolerance) break;
  122. lastLocalDirection = localDirection;
  123. if (OnPreIteration != null) OnPreIteration(i);
  124. Solve();
  125. }
  126. lastLocalDirection = localDirection;
  127. }
  128. protected override int minBones { get { return 1; }}
  129. private float step;
  130. private Vector3 clampedIKPosition;
  131. private RotationLimit transformLimit;
  132. private Transform lastTransform;
  133. /*
  134. * Solving the hierarchy
  135. * */
  136. private void Solve() {
  137. // Rotating bones to get closer to target.
  138. for (int i = 0; i < bones.Length - 1; i++) RotateToTarget(clampedIKPosition, bones[i], step * (i + 1) * IKPositionWeight * bones[i].weight);
  139. RotateToTarget(clampedIKPosition, bones[bones.Length - 1], IKPositionWeight * bones[bones.Length - 1].weight);
  140. }
  141. /*
  142. * Clamping the IKPosition to legal range
  143. * */
  144. private Vector3 GetClampedIKPosition() {
  145. if (clampWeight <= 0f) return IKPosition;
  146. if (clampWeight >= 1f) return transform.position + transformAxis * (IKPosition - transform.position).magnitude;
  147. // Getting the dot product of IK direction and transformAxis
  148. //float dot = (Vector3.Dot(transformAxis, (IKPosition - transform.position).normalized) + 1) * 0.5f;
  149. float angle = Vector3.Angle(transformAxis, (IKPosition - transform.position));
  150. float dot = 1f - (angle / 180f);
  151. // Clamping the target
  152. float targetClampMlp = clampWeight > 0? Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f): 1f;
  153. // Calculating the clamp multiplier
  154. float clampMlp = clampWeight > 0? Mathf.Clamp(dot / clampWeight, 0f, 1f): 1f;
  155. for (int i = 0; i < clampSmoothing; i++) {
  156. float sinF = clampMlp * Mathf.PI * 0.5f;
  157. clampMlp = Mathf.Sin(sinF);
  158. }
  159. // Slerping the IK direction (don't use Lerp here, it breaks it)
  160. return transform.position + Vector3.Slerp(transformAxis * 10f, IKPosition - transform.position, clampMlp * targetClampMlp);
  161. }
  162. /*
  163. * Rotating bone to get transform aim closer to target
  164. * */
  165. private void RotateToTarget(Vector3 targetPosition, IKSolver.Bone bone, float weight) {
  166. // Swing
  167. if (XY) {
  168. if (weight >= 0f) {
  169. Vector3 dir = transformAxis;
  170. Vector3 targetDir = targetPosition - transform.position;
  171. float angleDir = Mathf.Atan2(dir.x, dir.y) * Mathf.Rad2Deg;
  172. float angleTarget = Mathf.Atan2(targetDir.x, targetDir.y) * Mathf.Rad2Deg;
  173. bone.transform.rotation = Quaternion.AngleAxis(Mathf.DeltaAngle(angleDir, angleTarget), Vector3.back) * bone.transform.rotation;
  174. }
  175. } else {
  176. if (weight >= 0f) {
  177. Quaternion rotationOffset = Quaternion.FromToRotation(transformAxis, targetPosition - transform.position);
  178. if (weight >= 1f) {
  179. bone.transform.rotation = rotationOffset * bone.transform.rotation;
  180. } else {
  181. bone.transform.rotation = Quaternion.Lerp(Quaternion.identity, rotationOffset, weight) * bone.transform.rotation;
  182. }
  183. }
  184. // Pole
  185. if (poleWeight > 0f) {
  186. Vector3 poleDirection = polePosition - transform.position;
  187. // Ortho-normalize to transform axis to make this a twisting only operation
  188. Vector3 poleDirOrtho = poleDirection;
  189. Vector3 normal = transformAxis;
  190. Vector3.OrthoNormalize(ref normal, ref poleDirOrtho);
  191. Quaternion toPole = Quaternion.FromToRotation(transformPoleAxis, poleDirOrtho);
  192. bone.transform.rotation = Quaternion.Lerp(Quaternion.identity, toPole, weight * poleWeight) * bone.transform.rotation;
  193. }
  194. }
  195. if (useRotationLimits && bone.rotationLimit != null) bone.rotationLimit.Apply();
  196. }
  197. /*
  198. * Gets the direction from last bone's forward in first bone's local space.
  199. * */
  200. protected override Vector3 localDirection {
  201. get {
  202. return bones[0].transform.InverseTransformDirection(bones[bones.Length - 1].transform.forward);
  203. }
  204. }
  205. }
  206. }