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] bool m_ConstrainRotation = true; [FormerlySerializedAs("m_RestoreDefaultPose")] [SerializeField] bool m_SolveFromDefaultPose = true; [SerializeField] [Range(0f, 1f)] float m_Weight = 1f; Plane m_Plane; List m_TargetPositions = new List(); /// /// Used to evaluate if Solver2D needs to be updated. /// float m_LastFinalWeight; /// /// Returns the number of IKChain2D in the solver. /// public int chainCount => GetChainCount(); /// /// Get Set for rotation constrain property. /// public bool constrainRotation { get => m_ConstrainRotation; set => m_ConstrainRotation = value; } /// /// Get Set for restoring default pose. /// public bool solveFromDefaultPose { get => m_SolveFromDefaultPose; set => m_SolveFromDefaultPose = value; } /// /// Returns true if the Solver2D is in a valid state. /// public bool isValid => Validate(); /// /// Returns true if all chains in the Solver has a target. /// public bool allChainsHaveTargets => HasTargets(); /// /// Get and Set Solver weights. /// public float weight { get => m_Weight; set => m_Weight = Mathf.Clamp01(value); } /// /// Validate and initialize the Solver. /// protected virtual void OnValidate() { m_Weight = Mathf.Clamp01(m_Weight); if (!isValid) Initialize(); } bool Validate() { for (var i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); if (!chain.isValid) return false; } return DoValidate(); } bool HasTargets() { for (var 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 (var i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); chain.Initialize(); } } void Prepare() { var rootTransform = GetPlaneRootTransform(); if (rootTransform != null) { m_Plane.normal = rootTransform.forward; m_Plane.distance = -Vector3.Dot(m_Plane.normal, rootTransform.position); } for (var i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); var constrainTargetRotation = constrainRotation && chain.target != null; if (m_SolveFromDefaultPose) chain.RestoreDefaultPose(constrainTargetRotation); } DoPrepare(); } void PrepareEffectorPositions() { m_TargetPositions.Clear(); for (var i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); if (chain.target) m_TargetPositions.Add(chain.target.position); } } /// /// Perform 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; var finalWeight = globalWeight * weight; var weightValueChanged = Math.Abs(finalWeight - m_LastFinalWeight) > 0.0001f; m_LastFinalWeight = finalWeight; if (finalWeight == 0f && !weightValueChanged) return; if (!isValid) return; Prepare(); if (finalWeight < 1f) StoreLocalRotations(); DoUpdateIK(positions); if (constrainRotation) { for (var i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); if (chain.target) chain.effector.rotation = chain.target.rotation; } } if (finalWeight < 1f) BlendFkToIk(finalWeight); } void StoreLocalRotations() { for (var i = 0; i < GetChainCount(); ++i) { var chain = GetChain(i); chain.StoreLocalRotations(); } } void BlendFkToIk(float finalWeight) { for (var 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() => 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() { return chainCount > 0 ? GetChain(0).rootTransform : 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); } } }