Wrong angle calculation with relative joints 2D

When angle -180+ meets -180- relative joint 2d goes around.
It can be seen clearly in the gif

Joint stores a target angular offset and use it to solve the constraint.

The problem is because the Transform.rotation is actually stored with quaternion for 3D.
Since 2d physics only handle with the single float as rotation, Unity did it by converting the quaternion rotation to euler angle and extract the z axis as 2d rotation.
The converting only occurs when transform “sync” to rigidbody, and the sync happens iftransform was changed.

The converting could make the angle of rigidbody 2d lose if the angle is out of -180 or 180 which causes this problem.

For example, there are two rigidbodies A and B with rotation 180 and 90 and a joint to constraint them with -90 angular offset, meaning (B’s angle - A’s angle) = -90.
Then A rotates to 181 degree by changes rotation in transform (modified in editor).
Since the transform changed, physics 2d would sync the transform to rigidbody.
Pass through converting from quanternion to euler, the 181 degree becomes -179 degree.
Now A is -179, B is 90, and the joint’s target angular offset is -90 still. physics engine will calculate as B should rotate to -269 to fit constraint.

The idea is if you want to use joints which considered angular, you should not directly modified the rigidbody’s transform to prevent from syncing.
Unity did something I don’t know inside the physics engine to make the rigidbody somehow don’t wrap into -180 ~ +180 degree if there is any joint considered of angle. But it will mess up if transform sync to rigidbody.

2 Likes

Thanks for the answer! Just tried it with AddTorque and rigidbody.rotation and everything works fine, but I think I’d got this issue in my game project where all rigidbodies controlled via rigidbody.rotation. But there are more complex chaints of joints, I’ll try to localize it futher.

Yes the simple rule is that you should never modify the Transform to move anything related to physics. No exceptions. If you have an argument to do so then you’re into undefined and potentially unstable territory. The solver will do its best but you’re fighting against it at that point.

The primary role of the Rigidbody2D is to be the authority on position and rotation (pose) and writes that to the Transform. Modifying the Transform bypasses it and instantly updates (Teleports) the body. We have to then jump through hoops trying to catch up to this distruptive change. Joints are particular sensitive to this.

You should always move using the API provided on the Rigidbody2D but understand, physics wants to move with linear and angular velocity. Setting an instant position/rotation via the Rigidbody2D is still teleporting. To ensure you’re using velocity, you can use MovePosition an MoveRotation.

1 Like