How to get a Joint's current Rotation/angle?

Hello, i know so far how to make a ConfigurableJoint move to target Rotation:

public void moveConfJoint (ConfigurableJoint joint, Quaternion rotation)
{
        joint.targetRotation=rotation;
	
    JointDrive drive= new JointDrive();
    drive.positionSpring = 150;
	
    drive.mode = JointDriveMode.Position;
    drive.maximumForce=4;
    
    joint.angularXDrive = drive;
    joint.angularYZDrive = drive;
	
}

but with time it will also move with physics, so at a certain point id like to get its current Rotation, something like " Quaternion q = joint.getCurrentRotation; " or something like that, so i can get it’s current state/Rotation, how can i do this? i know that HingeJoints have the “joint.angle” attribute, so what is the equivalent for Configurable Joints?

None of the existing solutions worked for me with non-default axis orientations, especially one that weren’t aligned with a local axis.

The joint axis represents the right or X vector in local space [relative to the GameObject]. The secondaryAxis is sort of up, except nothing is forcing it to be orthogonal to the primary axis, so those vectors need to be normalized and made orthogonal first. You can get the forward direction by taking the cross-product of the result of those two. That’s enough to generate a Quaternion or Matrix for the joint axis [see Quaternion.LookRotation() call in the attached code].

The second important thing is that rotations are relative to the initial rotation at the start of the simulation; and offsets are in joint axis space relative to the connectedAnchor in the space of the connectedBody.

Here’s what I was able to come-up with, pared down to some minimal code that exposes Rotation and Position properties for a single ConfigurableJoint. Playing with the joint axis in the inspector while it’s running sometimes causes it to reset the rotation, which won’t be caught here. And, quaternions to Euler angles is always a bit questionable. I tried some more complex solutions there, but in the end this seemed to be as accurate, especially for acute angles.

This is usable as-is, but I was mostly trying to provide the core conversions. If you’re working with multiple joints on a single object or chains of objects, you obviously may want to just incorporate these conversions into your own code.

using UnityEngine;

public class JointValues : MonoBehaviour
{
    [field: SerializeField]
    public ConfigurableJoint Joint { get; private set; }

    [field: SerializeField]
    public Vector3 Position { get; private set; }

    [field: SerializeField]
    public Vector3 Rotation { get; private set; }

    private Quaternion _initialAttachedRotation;

    private void OnValidate()
    {
        if (Joint == null) Joint = GetComponent<ConfigurableJoint>();
    }

    private void Start()
    {
        _initialAttachedRotation = WorldToJointMatrix().rotation * Joint.connectedBody.transform.rotation;
    }

    private void Update()
    {
        Matrix4x4 worldToJointMatrix = WorldToJointMatrix();

        Quaternion rotationOffset =
            (worldToJointMatrix.rotation * Joint.connectedBody.transform.rotation) *
            Quaternion.Inverse(_initialAttachedRotation);

        Vector3 rotation = rotationOffset.eulerAngles;
        Rotation = new Vector3(
            (rotation.x + 180f) % 360f - 180f,
            (rotation.y + 180f) % 360f - 180f,
            (rotation.z + 180f) % 360f - 180f
        );

        Position = worldToJointMatrix.MultiplyPoint3x4(
            Joint.connectedBody.transform.TransformPoint(Joint.connectedAnchor)
        );
    }

    private Matrix4x4 WorldToJointMatrix()
    {
        if (Joint == null) return transform.worldToLocalMatrix;

        Vector3 axis = Joint.axis;
        Vector3 secondaryAxis = Joint.secondaryAxis;
        Vector3.OrthoNormalize(ref axis, ref secondaryAxis);

        return (transform.localToWorldMatrix * Matrix4x4.TRS(
            Joint.anchor,
            Quaternion.LookRotation(
                Vector3.Cross(axis, secondaryAxis),
                secondaryAxis
            ),
            Vector3.one
        )).inverse;
    }
}

public Quaternion getJointRotation(ConfigurableJoint joint)
{
return(Quaternion.FromToRotation(joint.axis, joint.connectedBody.transform.rotation.eulerAngles));
}

【The tutorial with the video and the code】

For short answer with key points:

  1. MUST get and keep the initial local rotation of the joint in its connected body space.
  2. Change the target rotation from the coordinate system of the joint axis space to the coordinate system of the joint local space
  3. The target rotation of the joint is actually represented in the “Inversed” form. Must do inverse.

By above information and the concept of quaternion, you can compute the current rotation of the joint from “World Space” into “Joint Axis Space” to get the value of “joint.CurrentRotation”

Must aware the third key point, it took me a long time to find out there exists a hidden inverse done by Unity Joint. Not knowing this may lead to the confused when you think the math sould be logically correct.