using System; using UnityEngine.Scripting.APIUpdating; using UnityEngine.Serialization; namespace UnityEngine.U2D.IK { /// <summary> /// Class for storing data for a 2D IK Chain. /// </summary> [MovedFrom("UnityEngine.Experimental.U2D.IK")] [Serializable] public class IKChain2D { [SerializeField] [FormerlySerializedAs("m_Target")] Transform m_EffectorTransform; [SerializeField] [FormerlySerializedAs("m_Effector")] Transform m_TargetTransform; [SerializeField] int m_TransformCount; [SerializeField] Transform[] m_Transforms; [SerializeField] Quaternion[] m_DefaultLocalRotations; [SerializeField] Quaternion[] m_StoredLocalRotations; /// <summary> /// Lengths of IK Chain. /// </summary> /// <returns>Array of lengths in the IK Chain.</returns> protected float[] m_Lengths; /// <summary> /// Get and set the transform used as the IK Effector. /// </summary> public Transform effector { get => m_EffectorTransform; set => m_EffectorTransform = value; } /// <summary> /// Get and set the transform used as the IK Target. /// </summary> public Transform target { get => m_TargetTransform; set => m_TargetTransform = value; } /// <summary> /// Get the transforms that are used in the IK Chain. /// </summary> public Transform[] transforms => m_Transforms; /// <summary> /// Get the root transform for the IK Chain. /// </summary> public Transform rootTransform { get { if (m_Transforms != null && transformCount > 0 && m_Transforms.Length == transformCount) return m_Transforms[0]; return null; } } Transform lastTransform { get { if (m_Transforms != null && transformCount > 0 && m_Transforms.Length == transformCount) return m_Transforms[transformCount - 1]; return null; } } /// <summary> /// Get and Set the number of transforms in the IK Chain. /// </summary> public int transformCount { get => m_TransformCount; set => m_TransformCount = Mathf.Max(0, value); } /// <summary> /// Returns true if the IK Chain is valid. False otherwise. /// </summary> public bool isValid => Validate(); /// <summary> /// Gets the length of the IK Chain. /// </summary> public float[] lengths { get { if (isValid) { PrepareLengths(); return m_Lengths; } return null; } } bool Validate() { if (effector == null) return false; if (transformCount == 0) return false; if (m_Transforms == null || m_Transforms.Length != transformCount) return false; if (m_DefaultLocalRotations == null || m_DefaultLocalRotations.Length != transformCount) return false; if (m_StoredLocalRotations == null || m_StoredLocalRotations.Length != transformCount) return false; if (rootTransform == null) return false; if (lastTransform != effector) return false; return !target || !IKUtility.IsDescendentOf(target, rootTransform); } /// <summary> /// Initialize the IK Chain. /// </summary> public void Initialize() { if (effector == null || transformCount == 0 || IKUtility.GetAncestorCount(effector) < transformCount - 1) return; m_Transforms = new Transform[transformCount]; m_DefaultLocalRotations = new Quaternion[transformCount]; m_StoredLocalRotations = new Quaternion[transformCount]; var currentTransform = effector; var index = transformCount - 1; while (currentTransform && index >= 0) { m_Transforms[index] = currentTransform; m_DefaultLocalRotations[index] = currentTransform.localRotation; currentTransform = currentTransform.parent; --index; } } void PrepareLengths() { var currentTransform = effector; var index = transformCount - 1; if (m_Lengths == null || m_Lengths.Length != transformCount - 1) m_Lengths = new float[transformCount - 1]; while (currentTransform && index >= 0) { if (currentTransform.parent && index > 0) m_Lengths[index - 1] = (currentTransform.position - currentTransform.parent.position).magnitude; currentTransform = currentTransform.parent; --index; } } /// <summary> /// Restores the IK Chain to it's default pose. /// </summary> /// <param name="targetRotationIsConstrained">True to constrain the target rotation. False otherwise.</param> public void RestoreDefaultPose(bool targetRotationIsConstrained) { var count = targetRotationIsConstrained ? transformCount : transformCount - 1; for (var i = 0; i < count; ++i) m_Transforms[i].localRotation = m_DefaultLocalRotations[i]; } /// <summary> /// Explicitly stores the local rotation. /// </summary> public void StoreLocalRotations() { for (var i = 0; i < m_Transforms.Length; ++i) m_StoredLocalRotations[i] = m_Transforms[i].localRotation; } /// <summary> /// Blend between Forward Kinematics and Inverse Kinematics. /// </summary> /// <param name="finalWeight">Weight for blend</param> /// <param name="targetRotationIsConstrained">True to constrain target rotation. False otherwise.</param> public void BlendFkToIk(float finalWeight, bool targetRotationIsConstrained) { var count = targetRotationIsConstrained ? transformCount : transformCount - 1; for (var i = 0; i < count; ++i) m_Transforms[i].localRotation = Quaternion.Slerp(m_StoredLocalRotations[i], m_Transforms[i].localRotation, finalWeight); } } }