FABRIK2D.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using UnityEngine.Scripting.APIUpdating;
  2. namespace UnityEngine.U2D.IK
  3. {
  4. /// <summary>
  5. /// Structure to store FABRIK Chain data.
  6. /// </summary>
  7. [MovedFrom("UnityEngine.Experimental.U2D.IK")]
  8. public struct FABRIKChain2D
  9. {
  10. public Vector2 first
  11. {
  12. get { return positions[0]; }
  13. }
  14. public Vector2 last
  15. {
  16. get { return positions[positions.Length - 1]; }
  17. }
  18. public Vector2 origin;
  19. public Vector2 target;
  20. public float sqrTolerance;
  21. public Vector2[] positions;
  22. public float[] lengths;
  23. public int[] subChainIndices;
  24. public Vector3[] worldPositions;
  25. }
  26. /// <summary>
  27. /// Utility for 2D Forward And Backward Reaching Inverse Kinematics (FABRIK) IK Solver.
  28. /// </summary>
  29. public static class FABRIK2D
  30. {
  31. /// <summary>
  32. /// Solve IK based on FABRIK
  33. /// </summary>
  34. /// <param name="targetPosition">Target position.</param>
  35. /// <param name="solverLimit">Solver iteration count.</param>
  36. /// <param name="tolerance">Target position's tolerance.</param>
  37. /// <param name="lengths">Length of the chains.</param>
  38. /// <param name="positions">Chain positions.</param>
  39. /// <returns>Returns true if solver successfully completes within iteration limit. False otherwise.</returns>
  40. public static bool Solve(Vector2 targetPosition, int solverLimit, float tolerance, float[] lengths, ref Vector2[] positions)
  41. {
  42. int last = positions.Length - 1;
  43. int iterations = 0;
  44. float sqrTolerance = tolerance * tolerance;
  45. float sqrDistanceToTarget = (targetPosition - positions[last]).sqrMagnitude;
  46. Vector2 originPosition = positions[0];
  47. while (sqrDistanceToTarget > sqrTolerance)
  48. {
  49. Forward(targetPosition, lengths, ref positions);
  50. Backward(originPosition, lengths, ref positions);
  51. sqrDistanceToTarget = (targetPosition - positions[last]).sqrMagnitude;
  52. if (++iterations >= solverLimit)
  53. break;
  54. }
  55. // Return whether positions have changed
  56. return iterations != 0;
  57. }
  58. public static bool SolveChain(int solverLimit, ref FABRIKChain2D[] chains)
  59. {
  60. // Do a quick validation of the end points that it has not been solved
  61. if (ValidateChain(chains))
  62. return false;
  63. // Validation failed, solve chain
  64. for (int iterations = 0; iterations < solverLimit; ++iterations)
  65. {
  66. SolveForwardsChain(0, ref chains);
  67. // Break if solution is solved
  68. if (!SolveBackwardsChain(0, ref chains))
  69. break;
  70. }
  71. return true;
  72. }
  73. static bool ValidateChain(FABRIKChain2D[] chains)
  74. {
  75. foreach (var chain in chains)
  76. {
  77. if (chain.subChainIndices.Length == 0 && (chain.target - chain.last).sqrMagnitude > chain.sqrTolerance)
  78. return false;
  79. }
  80. return true;
  81. }
  82. static void SolveForwardsChain(int idx, ref FABRIKChain2D[] chains)
  83. {
  84. var target = chains[idx].target;
  85. if (chains[idx].subChainIndices.Length > 0)
  86. {
  87. target = Vector2.zero;
  88. for (int i = 0; i < chains[idx].subChainIndices.Length; ++i)
  89. {
  90. var childIdx = chains[idx].subChainIndices[i];
  91. SolveForwardsChain(childIdx, ref chains);
  92. target += chains[childIdx].first;
  93. }
  94. target = target / chains[idx].subChainIndices.Length;
  95. }
  96. Forward(target, chains[idx].lengths, ref chains[idx].positions);
  97. }
  98. static bool SolveBackwardsChain(int idx, ref FABRIKChain2D[] chains)
  99. {
  100. bool notSolved = false;
  101. Backward(chains[idx].origin, chains[idx].lengths, ref chains[idx].positions);
  102. for (int i = 0; i < chains[idx].subChainIndices.Length; ++i)
  103. {
  104. var childIdx = chains[idx].subChainIndices[i];
  105. chains[childIdx].origin = chains[idx].last;
  106. notSolved |= SolveBackwardsChain(childIdx, ref chains);
  107. }
  108. // Check if end point has reached the target
  109. if (chains[idx].subChainIndices.Length == 0)
  110. {
  111. notSolved |= (chains[idx].target - chains[idx].last).sqrMagnitude > chains[idx].sqrTolerance;
  112. }
  113. return notSolved;
  114. }
  115. static void Forward(Vector2 targetPosition, float[] lengths, ref Vector2[] positions)
  116. {
  117. var last = positions.Length - 1;
  118. positions[last] = targetPosition;
  119. for (int i = last - 1; i >= 0; --i)
  120. {
  121. var r = positions[i + 1] - positions[i];
  122. var l = lengths[i] / r.magnitude;
  123. var position = (1f - l) * positions[i + 1] + l * positions[i];
  124. positions[i] = position;
  125. }
  126. }
  127. static void Backward(Vector2 originPosition, float[] lengths, ref Vector2[] positions)
  128. {
  129. positions[0] = originPosition;
  130. var last = positions.Length - 1;
  131. for (int i = 0; i < last; ++i)
  132. {
  133. var r = positions[i + 1] - positions[i];
  134. var l = lengths[i] / r.magnitude;
  135. var position = (1f - l) * positions[i] + l * positions[i + 1];
  136. positions[i + 1] = position;
  137. }
  138. }
  139. // For constraints
  140. static Vector2 ValidateJoint(Vector2 endPosition, Vector2 startPosition, Vector2 right, float min, float max)
  141. {
  142. var localDifference = endPosition - startPosition;
  143. var angle = Vector2.SignedAngle(right, localDifference);
  144. var validatedPosition = endPosition;
  145. if (angle < min)
  146. {
  147. var minRotation = Quaternion.Euler(0f, 0f, min);
  148. validatedPosition = startPosition + (Vector2)(minRotation * right * localDifference.magnitude);
  149. }
  150. else if (angle > max)
  151. {
  152. var maxRotation = Quaternion.Euler(0f, 0f, max);
  153. validatedPosition = startPosition + (Vector2)(maxRotation * right * localDifference.magnitude);
  154. }
  155. return validatedPosition;
  156. }
  157. }
  158. }