# IK: How to constrain bones

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

### 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.

### Unity Technologies

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

### 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);
12.
13.     Vector3 secondaryAxis = new Vector3(axis.z, axis.x, axis.y);
14.     Vector3 cross = Vector3.Cross(secondaryAxis, axis);
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.

### Unity Technologies

Joined:
Dec 2, 2016
Posts:
212
Hi, thanks for sharing your contribution

5. ### 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

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

### Unity Technologies

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

8. ### tonytopper

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

### Unity Technologies

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

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

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

Hope this helps!

#### Attached Files:

File size:
3.2 KB
Views:
190
• ###### CCDSolver2D.cs
File size:
4.4 KB
Views:
186
Last edited: Feb 26, 2022
tizziocajo and tonytopper like this.