using System; using System.Collections.Generic; using UnityEngine.Scripting.APIUpdating; using UnityEngine.Serialization; namespace UnityEngine.U2D.IK { /// /// Abstract class for implementing a 2D IK Solver. /// [MovedFrom("UnityEngine.Experimental.U2D.IK")] public abstract class Solver2D : MonoBehaviour { [SerializeField] private bool m_ConstrainRotation = true; [FormerlySerializedAs("m_RestoreDefaultPose")] [SerializeField] private bool m_SolveFromDefaultPose = true; [SerializeField][Range(0f, 1f)] private float m_Weight = 1f; private Plane m_Plane; private List m_TargetPositions = new List(); /// /// Returns the number of IKChain2D in the solver. /// public int chainCount { get { return GetChainCount(); } } /// /// Get Set for rotation constrain property. /// public bool constrainRotation { get { return m_ConstrainRotation; } set { m_ConstrainRotation = value; } } /// /// Get Set for restoring default pose. /// public bool solveFromDefaultPose { get { return m_SolveFromDefaultPose; } set { m_SolveFromDefaultPose = value; } } /// /// Returns true if the Solver2D is in a valid state. /// public bool isValid { get { return Validate(); } } /// /// Returns true if all chains in the Solver has a target. /// public bool allChainsHaveTargets { get { return HasTargets(); } } /// /// Get and Set Solver weights. /// public float weight { get { return m_Weight; } set { m_Weight = Mathf.Clamp01(value); } } private void OnEnable() {} /// /// Validate and initialize the Solver. /// protected virtual void OnValidate() { m_Weight = Mathf.Clamp01(m_Weight); if (!isValid) Initialize(); } private bool Validate() { for (int i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); if (!chain.isValid) return false; } return DoValidate(); } private bool HasTargets() { for (int i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); if (chain.target == null) return false; } return true; } /// /// Initializes the solver. /// public void Initialize() { DoInitialize(); for (int i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); chain.Initialize(); } } private void Prepare() { var rootTransform = GetPlaneRootTransform(); if (rootTransform != null) { m_Plane.normal = rootTransform.forward; m_Plane.distance = -Vector3.Dot(m_Plane.normal, rootTransform.position); } for (int i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); var constrainTargetRotation = constrainRotation && chain.target != null; if (m_SolveFromDefaultPose) chain.RestoreDefaultPose(constrainTargetRotation); } DoPrepare(); } private void PrepareEffectorPositions() { m_TargetPositions.Clear(); for (int i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); if (chain.target) m_TargetPositions.Add(chain.target.position); } } /// /// Perfom Solver IK update. /// /// Weight for position solving. public void UpdateIK(float globalWeight) { if(allChainsHaveTargets) { PrepareEffectorPositions(); UpdateIK(m_TargetPositions, globalWeight); } } /// /// Perform Solver IK update. /// /// Positions of chain. /// Weight for position solving. public void UpdateIK(List positions, float globalWeight) { if(positions.Count != chainCount) return; float finalWeight = globalWeight * weight; if (finalWeight == 0f) return; if (!isValid) return; Prepare(); if (finalWeight < 1f) StoreLocalRotations(); DoUpdateIK(positions); if (constrainRotation) { for (int i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); if (chain.target) chain.effector.rotation = chain.target.rotation; } } if (finalWeight < 1f) BlendFkToIk(finalWeight); } private void StoreLocalRotations() { for (int i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); chain.StoreLocalRotations(); } } private void BlendFkToIk(float finalWeight) { for (int i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); var constrainTargetRotation = constrainRotation && chain.target != null; chain.BlendFkToIk(finalWeight, constrainTargetRotation); } } /// /// Override to return the IKChain2D at the given index. /// /// Index for IKChain2D. /// public abstract IKChain2D GetChain(int index); /// /// OVerride to return the number of chains in the Solver /// /// Integer represents IKChain2D count. protected abstract int GetChainCount(); /// /// Override to perform Solver IK update /// /// Position of the effectors. protected abstract void DoUpdateIK(List effectorPositions); /// /// Override to perform custom validation. /// /// Returns true if the Solver is in a valid state. False otherwise. protected virtual bool DoValidate() { return true; } /// /// Override to perform initialize the solver /// protected virtual void DoInitialize() {} /// /// Override to prepare the solver for update /// protected virtual void DoPrepare() {} /// /// Override to return the root Unity Transform of the Solver. The default implementation returns the root /// transform of the first chain. /// /// Unity Transform that represents the root. protected virtual Transform GetPlaneRootTransform() { if (chainCount > 0) return GetChain(0).rootTransform; return null; } /// /// Convert a world position coordinate to the solver's plane space /// /// Vector3 representing world position /// Converted position in solver's plane protected Vector3 GetPointOnSolverPlane(Vector3 worldPosition) { return GetPlaneRootTransform().InverseTransformPoint(m_Plane.ClosestPointOnPlane(worldPosition)); } /// /// Convert a position from solver's plane to world coordinate /// /// Vector3 representing a position in the Solver's plane. /// Converted position to world coordinate. protected Vector3 GetWorldPositionFromSolverPlanePoint(Vector2 planePoint) { return GetPlaneRootTransform().TransformPoint(planePoint); } } }