Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Bug Flip bug with inverse kinematics (Limb Solver 2D)

Discussion in '2D' started by Crokett, Nov 24, 2023.

  1. Crokett

    Crokett

    Joined:
    May 14, 2021
    Posts:
    48
    Hello! I'm setting up an arm with inverse kinematics in my Unity 2D project but I'm running into a problem that I think is due to a bug in the LimbSolver2D component.

    The problem I am encountering is that the X axis rotation of the first bone changes from 0 to -180 when my target is positioned in certain specific positions causing a flip glith of the arm.



    Some of the target positions in which this flip occurs:

    Vector3(-0.832000017,8.32999992,0)
    Vector3(-0.587000012,5.82000017,0)
    Vector3(-0.615999997,6.08500004,0)

    Has anyone encountered this problem before?

     

    Attached Files:

    Last edited: Nov 24, 2023
  2. MarekUnity

    MarekUnity

    Unity Technologies

    Joined:
    Jan 6, 2017
    Posts:
    210
    Which version of the animation package are you using (you can check that in the package manager)?

    Would you be able to share your setup so I can investigate better what's happening? A prefab would be enough!
     
    Crokett likes this.
  3. Crokett

    Crokett

    Joined:
    May 14, 2021
    Posts:
    48
    Hi!
    I am using version 9.0.4 of the 2D Animation package.

    I think I have already found the problem, in the DoUpdateIK function of limbSolver it uses Quaternion.FromToRotation(), this function sometimes returns a value of 180º for the euleraxis X, which causes my gameobject to flip.

    I have created a function that calculates the rotation for the vector direction but only for the Z axis and it seems that the problem is solved with a customization of limbSolver.


    Code (CSharp):
    1.  
    2. /// <summary>
    3. /// Updates the IK and sets the chain's transform positions.
    4. /// </summary>
    5. /// <param name="targetPositions">List of target positions.</param>
    6. protected override void DoUpdateIK(List<Vector3> targetPositions)
    7. {
    8.     var targetPosition = targetPositions[0];
    9.     var upperLimb = m_Chain.transforms[0];
    10.     var lowerLimb = m_Chain.transforms[1];
    11.     var effector = m_Chain.effector;
    12.  
    13.     var targetLocalPosition2D = (Vector2)upperLimb.InverseTransformPoint(targetPosition);
    14.     targetPosition = upperLimb.TransformPoint(targetLocalPosition2D);
    15.     Debug.Log(Quaternion.FromToRotation(Vector3.right, new Vector2(-2f, -4f)).eulerAngles);
    16.  
    17.     if (targetLocalPosition2D.sqrMagnitude > 0f && Limb.Solve(targetPosition, m_Lengths, m_Positions, ref m_Angles)) {
    18.         var upperLimbRotation = FromToZRotatiton(targetLocalPosition2D) * Quaternion.FromToRotation((Vector2)lowerLimb.localPosition, Vector3.right);
    19.         upperLimbRotation *= Quaternion.AngleAxis((flip ? -1f : 1f) * m_Angles[0], Vector3.forward);
    20.         upperLimb.localRotation *= upperLimbRotation;
    21.  
    22.         var loweLimbRotation = FromToZRotatiton(lowerLimb.InverseTransformPoint(targetPosition)) * Quaternion.FromToRotation((Vector2)effector.localPosition, Vector3.right);
    23.         m_Chain.transforms[1].localRotation *= loweLimbRotation;
    24.     }
    25. }
    26.  
    27. /// <summary>
    28. /// Calculates the rotation around the Z axis based on the direction of the provided vector
    29. /// </summary>
    30. /// <param name="direction"></param>
    31. /// <returns></returns>
    32. protected Quaternion FromToZRotatiton(Vector3 direction)
    33. {
    34.     return Quaternion.Euler(0f, 0f, Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg);
    35. }
    36.  
    What surprises me a lot is that no one has noticed this problem before. To verify it, it is as simple as running these lines with some example values.

    Debug.Log(Quaternion.FromToRotation(Vector3.right, new Vector2(-30f, -23f)).eulerAngles);

    In this first case you can check how to obtain an appropriate rotation value (0.00, 0.00, 243.43)

    Debug.Log(Quaternion.FromToRotation(Vector3.right, new Vector2(-0.5f, -0f)).eulerAngles);

    However, in this other case you can see how Quaternion.FromToRotation returns a value for the other axis that causes a flip

    Thanks for your interest
     
    Last edited: Feb 22, 2024
  4. MarekUnity

    MarekUnity

    Unity Technologies

    Joined:
    Jan 6, 2017
    Posts:
    210
  5. Crokett

    Crokett

    Joined:
    May 14, 2021
    Posts:
    48
    You're welcome and thanks for registering the Bug. It's a small detail but it can be easily solved!