Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

IK: How to constrain bones

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

  1. Deleted User

    Deleted User

    Guest

    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

    Sergi_Valls

    Unity Technologies

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

    Deleted User

    Guest

    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):
    1.    
    2.     Vector3 axis = Vector3.forward
    3.     // Subtracting localRotation off
    4.     Quaternion rotation = Quaternion.Inverse(transform.localRotation) * transform.localRotation;
    5.  
    6.     //Limits rotation to a single degree of freedom (along axis)
    7.     Quaternion DOF = Quaternion.FromToRotation(rotation * axis, axis) * rotation;
    8.  
    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);
    12.  
    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;
    16.      
    17.     // Clamp to limits
    18.     float lastAngle = Mathf.Clamp(lastAngle + addAngle, min, max);
    19.     Quaternion limitedRotation = Quaternion.AngleAxis(lastAngle, axis);
    20.  
    21.     // Add localRotation back on
    22.     transform.localRotation = transform.localRotation * limitedRotation;
    23.  
     
    tonytopper likes this.
  4. Sergi_Valls

    Sergi_Valls

    Unity Technologies

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

    Ramezov

    Joined:
    May 2, 2017
    Posts:
    25
    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

    tonytopper

    Joined:
    Jun 25, 2018
    Posts:
    226
    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

    Ted_Wikman

    Unity Technologies

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

    tonytopper

    Joined:
    Jun 25, 2018
    Posts:
    226
    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.

    upload_2021-12-6_12-51-39.png

    upload_2021-12-6_12-52-15.png

    upload_2021-12-6_12-53-0.png

    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.

    upload_2021-12-6_12-55-47.png

    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

    Ted_Wikman

    Unity Technologies

    Joined:
    Oct 7, 2019
    Posts:
    916
    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

    tonytopper

    Joined:
    Jun 25, 2018
    Posts:
    226
    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

    ennasros

    Joined:
    Jan 8, 2021
    Posts:
    1
    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. }
    17.  
    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];
    24.  
    25.         float angle = Vector3.SignedAngle(toLast, toTarget, forward);
    26.         angle %= 360f;
    27.         angle = angle > 180f ? angle - 360f : angle;
    28.  
    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);
    36.  
    37.         angle = Mathf.Lerp(0f, angle, velocity);
    38.  
    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.
  12. tonytopper

    tonytopper

    Joined:
    Jun 25, 2018
    Posts:
    226
    I am coming back to this on a different front.

    I see that Unity.Animations.Rigging package has IK constraints now. Will those constraints work with the 2D IK components or do they only work with the IK that's in Unity.Animations.Rigging?

    I have a SkinnedMeshRenderer that I wired with an IKManger2D and it's sort of working the way I want to but I'd like to put some rotation constraints on it.

    I am pretty far down the 2D IK road, creating some complex chains via scripting, so reworking it might be painful. It was nice to be able to using the Unity.Animations.Rigging package to now see my bones, though I wish they could be turned off in the Gizmos menu like other things off this nature.

    upload_2024-2-2_15-31-47.png
     
  13. joeyiglesias83

    joeyiglesias83

    Joined:
    Nov 25, 2023
    Posts:
    7
    Unity: "That's a great idea! Run with it." ;)