Solver2D.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine.Scripting.APIUpdating;
  4. using UnityEngine.Serialization;
  5. namespace UnityEngine.U2D.IK
  6. {
  7. /// <summary>
  8. /// Abstract class for implementing a 2D IK Solver.
  9. /// </summary>
  10. [MovedFrom("UnityEngine.Experimental.U2D.IK")]
  11. public abstract class Solver2D : MonoBehaviour
  12. {
  13. [SerializeField]
  14. private bool m_ConstrainRotation = true;
  15. [FormerlySerializedAs("m_RestoreDefaultPose")]
  16. [SerializeField]
  17. private bool m_SolveFromDefaultPose = true;
  18. [SerializeField][Range(0f, 1f)]
  19. private float m_Weight = 1f;
  20. private Plane m_Plane;
  21. private List<Vector3> m_TargetPositions = new List<Vector3>();
  22. /// <summary>
  23. /// Returns the number of IKChain2D in the solver.
  24. /// </summary>
  25. public int chainCount
  26. {
  27. get { return GetChainCount(); }
  28. }
  29. /// <summary>
  30. /// Get Set for rotation constrain property.
  31. /// </summary>
  32. public bool constrainRotation
  33. {
  34. get { return m_ConstrainRotation; }
  35. set { m_ConstrainRotation = value; }
  36. }
  37. /// <summary>
  38. /// Get Set for restoring default pose.
  39. /// </summary>
  40. public bool solveFromDefaultPose
  41. {
  42. get { return m_SolveFromDefaultPose; }
  43. set { m_SolveFromDefaultPose = value; }
  44. }
  45. /// <summary>
  46. /// Returns true if the Solver2D is in a valid state.
  47. /// </summary>
  48. public bool isValid
  49. {
  50. get { return Validate(); }
  51. }
  52. /// <summary>
  53. /// Returns true if all chains in the Solver has a target.
  54. /// </summary>
  55. public bool allChainsHaveTargets
  56. {
  57. get { return HasTargets(); }
  58. }
  59. /// <summary>
  60. /// Get and Set Solver weights.
  61. /// </summary>
  62. public float weight
  63. {
  64. get { return m_Weight; }
  65. set { m_Weight = Mathf.Clamp01(value); }
  66. }
  67. private void OnEnable() {}
  68. /// <summary>
  69. /// Validate and initialize the Solver.
  70. /// </summary>
  71. protected virtual void OnValidate()
  72. {
  73. m_Weight = Mathf.Clamp01(m_Weight);
  74. if (!isValid)
  75. Initialize();
  76. }
  77. private bool Validate()
  78. {
  79. for (int i = 0; i < GetChainCount(); ++i)
  80. {
  81. var chain = GetChain(i);
  82. if (!chain.isValid)
  83. return false;
  84. }
  85. return DoValidate();
  86. }
  87. private bool HasTargets()
  88. {
  89. for (int i = 0; i < GetChainCount(); ++i)
  90. {
  91. var chain = GetChain(i);
  92. if (chain.target == null)
  93. return false;
  94. }
  95. return true;
  96. }
  97. /// <summary>
  98. /// Initializes the solver.
  99. /// </summary>
  100. public void Initialize()
  101. {
  102. DoInitialize();
  103. for (int i = 0; i < GetChainCount(); ++i)
  104. {
  105. var chain = GetChain(i);
  106. chain.Initialize();
  107. }
  108. }
  109. private void Prepare()
  110. {
  111. var rootTransform = GetPlaneRootTransform();
  112. if (rootTransform != null)
  113. {
  114. m_Plane.normal = rootTransform.forward;
  115. m_Plane.distance = -Vector3.Dot(m_Plane.normal, rootTransform.position);
  116. }
  117. for (int i = 0; i < GetChainCount(); ++i)
  118. {
  119. var chain = GetChain(i);
  120. var constrainTargetRotation = constrainRotation && chain.target != null;
  121. if (m_SolveFromDefaultPose)
  122. chain.RestoreDefaultPose(constrainTargetRotation);
  123. }
  124. DoPrepare();
  125. }
  126. private void PrepareEffectorPositions()
  127. {
  128. m_TargetPositions.Clear();
  129. for (int i = 0; i < GetChainCount(); ++i)
  130. {
  131. var chain = GetChain(i);
  132. if (chain.target)
  133. m_TargetPositions.Add(chain.target.position);
  134. }
  135. }
  136. /// <summary>
  137. /// Perfom Solver IK update.
  138. /// </summary>
  139. /// <param name="globalWeight">Weight for position solving.</param>
  140. public void UpdateIK(float globalWeight)
  141. {
  142. if(allChainsHaveTargets)
  143. {
  144. PrepareEffectorPositions();
  145. UpdateIK(m_TargetPositions, globalWeight);
  146. }
  147. }
  148. /// <summary>
  149. /// Perform Solver IK update.
  150. /// </summary>
  151. /// <param name="positions">Positions of chain.</param>
  152. /// <param name="globalWeight">Weight for position solving.</param>
  153. public void UpdateIK(List<Vector3> positions, float globalWeight)
  154. {
  155. if(positions.Count != chainCount)
  156. return;
  157. float finalWeight = globalWeight * weight;
  158. if (finalWeight == 0f)
  159. return;
  160. if (!isValid)
  161. return;
  162. Prepare();
  163. if (finalWeight < 1f)
  164. StoreLocalRotations();
  165. DoUpdateIK(positions);
  166. if (constrainRotation)
  167. {
  168. for (int i = 0; i < GetChainCount(); ++i)
  169. {
  170. var chain = GetChain(i);
  171. if (chain.target)
  172. chain.effector.rotation = chain.target.rotation;
  173. }
  174. }
  175. if (finalWeight < 1f)
  176. BlendFkToIk(finalWeight);
  177. }
  178. private void StoreLocalRotations()
  179. {
  180. for (int i = 0; i < GetChainCount(); ++i)
  181. {
  182. var chain = GetChain(i);
  183. chain.StoreLocalRotations();
  184. }
  185. }
  186. private void BlendFkToIk(float finalWeight)
  187. {
  188. for (int i = 0; i < GetChainCount(); ++i)
  189. {
  190. var chain = GetChain(i);
  191. var constrainTargetRotation = constrainRotation && chain.target != null;
  192. chain.BlendFkToIk(finalWeight, constrainTargetRotation);
  193. }
  194. }
  195. /// <summary>
  196. /// Override to return the IKChain2D at the given index.
  197. /// </summary>
  198. /// <param name="index">Index for IKChain2D.</param>
  199. /// <returns></returns>
  200. public abstract IKChain2D GetChain(int index);
  201. /// <summary>
  202. /// OVerride to return the number of chains in the Solver
  203. /// </summary>
  204. /// <returns>Integer represents IKChain2D count.</returns>
  205. protected abstract int GetChainCount();
  206. /// <summary>
  207. /// Override to perform Solver IK update
  208. /// </summary>
  209. /// <param name="effectorPositions">Position of the effectors.</param>
  210. protected abstract void DoUpdateIK(List<Vector3> effectorPositions);
  211. /// <summary>
  212. /// Override to perform custom validation.
  213. /// </summary>
  214. /// <returns>Returns true if the Solver is in a valid state. False otherwise.</returns>
  215. protected virtual bool DoValidate() { return true; }
  216. /// <summary>
  217. /// Override to perform initialize the solver
  218. /// </summary>
  219. protected virtual void DoInitialize() {}
  220. /// <summary>
  221. /// Override to prepare the solver for update
  222. /// </summary>
  223. protected virtual void DoPrepare() {}
  224. /// <summary>
  225. /// Override to return the root Unity Transform of the Solver. The default implementation returns the root
  226. /// transform of the first chain.
  227. /// </summary>
  228. /// <returns>Unity Transform that represents the root.</returns>
  229. protected virtual Transform GetPlaneRootTransform()
  230. {
  231. if (chainCount > 0)
  232. return GetChain(0).rootTransform;
  233. return null;
  234. }
  235. /// <summary>
  236. /// Convert a world position coordinate to the solver's plane space
  237. /// </summary>
  238. /// <param name="worldPosition">Vector3 representing world position</param>
  239. /// <returns>Converted position in solver's plane</returns>
  240. protected Vector3 GetPointOnSolverPlane(Vector3 worldPosition)
  241. {
  242. return GetPlaneRootTransform().InverseTransformPoint(m_Plane.ClosestPointOnPlane(worldPosition));
  243. }
  244. /// <summary>
  245. /// Convert a position from solver's plane to world coordinate
  246. /// </summary>
  247. /// <param name="planePoint">Vector3 representing a position in the Solver's plane.</param>
  248. /// <returns>Converted position to world coordinate.</returns>
  249. protected Vector3 GetWorldPositionFromSolverPlanePoint(Vector2 planePoint)
  250. {
  251. return GetPlaneRootTransform().TransformPoint(planePoint);
  252. }
  253. }
  254. }