暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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