Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We’re making changes to the Unity Runtime Fee pricing policy that we announced on September 12th. Access our latest thread for more information!
    Dismiss Notice
  3. Dismiss Notice

IK: How to constrain bones

Discussion in '2D Experimental Preview' started by Deleted User, Mar 1, 2019.

  1. Deleted User

    Deleted User


    I'm using a CCD solver on a chain of 4 bones (shoulder,upper arm, lower arm, hand). Is there an efficient way of setting rotation thresholds per bone, or will I need to create my own solver?

    I've tried throwing a script on one of the bones with a mathf.clamp on the local rotation, but it appears the solver overrides it.
  2. Sergi_Valls


    Unity Technologies

    Dec 2, 2016
    Hi, unfortunately our package does not support rotation constrains.
  3. Deleted User

    Deleted User


    I was able to work around it. The IKsolver was able cope with resetting the rotations in lateupdate on each bone based on supplied -180 to 180 clamping.

    in case anyone was wondering here's my condensed math
    Code (CSharp):
    2.     Vector3 axis = Vector3.forward
    3.     // Subtracting localRotation off
    4.     Quaternion rotation = Quaternion.Inverse(transform.localRotation) * transform.localRotation;
    6.     //Limits rotation to a single degree of freedom (along axis)
    7.     Quaternion DOF = Quaternion.FromToRotation(rotation * axis, axis) * rotation;
    9.     // Get offset from last rotation in angle-axis representation
    10.     Quaternion addRotation = DOF * Quaternion.Inverse(Quaternion.identity);
    11.     float addAngle = Quaternion.Angle(Quaternion.identity, addRotation);
    13.     Vector3 secondaryAxis = new Vector3(axis.z, axis.x, axis.y);
    14.     Vector3 cross = Vector3.Cross(secondaryAxis, axis);
    15.     if (Vector3.Dot(addRotation * secondaryAxis, cross) > 0f) addAngle = - addAngle;
    17.     // Clamp to limits
    18.     float lastAngle = Mathf.Clamp(lastAngle + addAngle, min, max);
    19.     Quaternion limitedRotation = Quaternion.AngleAxis(lastAngle, axis);
    21.     // Add localRotation back on
    22.     transform.localRotation = transform.localRotation * limitedRotation;
    tonytopper likes this.
  4. Sergi_Valls


    Unity Technologies

    Dec 2, 2016
    Hi, thanks for sharing your contribution :)
  5. Ramezov


    May 2, 2017
    Hi Keshire, I tried your solution but it did nothing in, I placed the code in LateUpdate and made sure the script attached to the bone but still have no effect, maybe the code missing something, I'm using Unity 2019.3.5f1 and 2D IK 2.0.1 package.
  6. tonytopper


    Jun 25, 2018
    Is there any new news or recommendations on this front?

    Unity's 2D IK would be a good bit more user-friendly if there was a way to limit the range of specific bone rotations.

    As a related aside, I was confused for a bit by the, IMO strangely named, "Rotation Constraint" component, thinking that was the avenue to solve this problem, but that's more of a rotation synchronizer it seems.
    DragonCoder likes this.
  7. Ted_Wikman


    Unity Technologies

    Oct 7, 2019
    @tonytopper could you walk us through your use case, and let us know how the current solution is failing you?
  8. tonytopper


    Jun 25, 2018
    Firstly, just to frame my thinking, I don't really think this is a use case problem. Just laying that out there. This is about how to make this a more creatively powerful and flexible tool.

    Ofc, I do understand that it's always nice to have the use cases though.




    I'd like to be able to limit the range of motion on that shoulder bone.

    Secondly, I think lots of folks would love to be able to flip, or even swap, sprites based on where the bone is rotated.


    I'd like to be able to flip the forearm sprite or even the hand sprite based on the rotation of the bone.

    As a side note, being able to test the IK outside of play mode is one of the things that would be great to have. I am imagining that might be trickier when going the scripting route.
    DragonCoder likes this.
  9. Ted_Wikman


    Unity Technologies

    Oct 7, 2019
    Thanks for the detailed explanation @tonytopper!
    I can see the use cases for both the range limit and swap/flip based on some bone rotation. Note that you can quite easily extend the current IK implementations (link to documentation). This way you could experiment with more custom implementations and tailor them to your project.

    Regarding your final note, you should be able to move the IK Target and see the effect in edit mode. If this is not the case, do file a bug report so that we can take a closer look.
  10. tonytopper


    Jun 25, 2018
    I do currently see the IK's effect in edit mode. Sorry for the ambiguity there, "going the scripting route" was referring to adding those features to the bones via custom code, etc, as you mentioned as well.
  11. ennasros


    Jan 8, 2021
    I know this is a bit late but I recently started playing with this package (really like it so far!).
    Here is my quick hack to add constraints to CCD. I couldn't figure it out with FABRIK -- although my my shallow research suggests that its better equipped for handling joint constraints.

    I added the following property to CCDSolver2D.cs
    Code (CSharp):
    1. private Vector2[] m_limits;
    2. /// <summary>
    3. /// Get/set joint angle limits in degrees (Vector2 for ease of bundling)
    4. /// </summary>
    5. public Vector2[] limits
    6. {
    7.     get { return m_limits; }
    8.     set { m_limits = value; }
    9. }
    And passed these limits on to the solver. I modified the CCD2D.cs solver to keep track of cumulative rotation at each joint and limited the reorientation step
    Code (CSharp):
    1. public static bool Solve(Vector3 targetPosition, Vector3 forward, int solverLimit, float tolerance, float velocity, ref Vector3[] positions, Vector2[] limits)
    2. {
    3.     int last = positions.Length - 1;
    4.     int iterations = 0;
    5.     float sqrTolerance = tolerance * tolerance;
    6.     float sqrDistanceToTarget = (targetPosition - positions[last]).sqrMagnitude;
    7.     float[] cumulativeRotation = new float[positions.Length];
    8.     while (sqrDistanceToTarget > sqrTolerance)
    9.     {
    10.         DoIteration(targetPosition, forward, last, velocity, ref positions, ref cumulativeRotation, limits);
    11.         sqrDistanceToTarget = (targetPosition - positions[last]).sqrMagnitude;
    12.         if (++iterations >= solverLimit)
    13.             break;
    14.     }
    15.     return iterations != 0;
    16. }
    18. static void DoIteration(Vector3 targetPosition, Vector3 forward, int last, float velocity, ref Vector3[] positions, ref float[] cumulativeRotation, Vector2[] limits)
    19. {
    20.     for (int i = last - 1; i >= 0; --i)
    21.     {
    22.         Vector3 toTarget = targetPosition - positions[i];
    23.         Vector3 toLast = positions[last] - positions[i];
    25.         float angle = Vector3.SignedAngle(toLast, toTarget, forward);
    26.         angle %= 360f;
    27.         angle = angle > 180f ? angle - 360f : angle;
    29.         //Debug.Log("i = " + i + ", angle before = " + angle);
    30.         float upperLimit = Mathf.Max(limits[i].x, limits[i].y);
    31.         float lowerLimit = Mathf.Min(limits[i].x, limits[i].y);
    32.         if (cumulativeRotation[i] + angle > upperLimit) { angle = upperLimit - cumulativeRotation[i]; }
    33.         else if (cumulativeRotation[i] + angle < lowerLimit) { angle = lowerLimit - cumulativeRotation[i]; }
    34.         cumulativeRotation[i] += angle;
    35.         //Debug.Log("i = " + i + ", angle after = " + angle);
    37.         angle = Mathf.Lerp(0f, angle, velocity);
    39.         Quaternion deltaRotation = Quaternion.AngleAxis(angle, forward);
    40.         for (int j = last; j > i; --j)
    41.             positions[j] = RotatePositionFrom(positions[j], positions[i], deltaRotation);
    42.     }
    43. }
    I think this only works if "Solve From Default Pose" is set to true -- at least it seems to be working with the limited testing I've done so far but I'm sure bugs will appear once I play with it a bit more :D

    Hope this helps!

    Attached Files:

    Last edited: Feb 26, 2022
    tizziocajo and tonytopper like this.