HingeJoint stops working properly after disable/enable.

Greetings,

It is as the title says, the hinge joints stop working properly after they have been disabled and enabled. The best I can describe it is the limits are setting themselves to something other than what I have defined. However, when watching them in the inspector this does not appear to be the case.

The “DoorSwing” is being activated in my game pause script, using the following code:

`

void SetPause(bool pause)
{
	state = 0;
	isPaused = pause;  
    Screen.showCursor = pause; 
    
	foreach (GameObject go in objects)
	{
		if (go != gameObject && go != null)
			go.SetActive(!pause);
	}
}

`

Here is a video of the problem. - Warning: I left my music on, might be loud.

I don’t think this is the problem is with the hinge door class, however, I could be wrong… here is the hinge door script.

This problem has been rattling my brain for a few days and no matter how much I think about it I cannot solve it. So time to turn to the community.

Any thoughts guys?

Peace,

MaGuSware™

Add the following component to each hinge joint.

using UnityEngine;

public class HingeJointFix : MonoBehaviour
{
	private Quaternion initialLocalRotation;
	private Vector3 initialLocalPosition;

	private Quaternion localRotationOnDisable;
	private Vector3 localPositionOnDisable;

	private bool hasDisabled;

	void Awake()
	{
		this.initialLocalRotation = this.transform.localRotation;
		this.initialLocalPosition = this.transform.localPosition;
	}

	void OnDisable()
	{
		this.localRotationOnDisable = this.transform.localRotation;
		this.transform.localRotation = this.initialLocalRotation;

		this.localPositionOnDisable = this.transform.localPosition;
		this.transform.localPosition = this.initialLocalPosition;

		this.hasDisabled = true;
	}

	void Update()
	{
		if (this.hasDisabled)
		{
			this.hasDisabled = false;
			this.transform.localRotation = this.localRotationOnDisable;
			this.transform.localPosition = this.localPositionOnDisable;
		}
	}
}

It seems the Hinge class recalculates the limits each time it gets OnEnable. This code patches it by reverting the transform to the same position/rotation from startup. Then the limit calculation at OnEnable will be consistent. Finally, in the next Update it restores the transform to what its supposed to be.

You can see this in action in my Perseus 230 demo: http://perseus230.com/ship_viewer_guardian.html
(When you switch ships back and forth the Guardian Drone is enabled/disabled, hinges in the arms are ok!)

In case the base rigid body to which the last hinge is jointed moves/rotates, add the component to it as well. If you don’t desire this entire behavior to be on the base object–lets say the base rigid body is the wall in a level and the hinge is a door; and for some reason the walls move!–create a rigid body that is a child of the wall and add this component, then hinge the door to it instead of to the wall.

So if the above solutions didn’t work for anyone, this one might. After battling with this Issue for a day or so, I’ve found a method that should work no matter the setup of your parents and objects. The concept is to take the rotation of the joint-object at initialization and transform that quaternion to be relative to the connected body’s coordinate plane. You then cache it as the “Initial Rotation”.

Private Quaternion initialRotation_JointToConnected
initialRotation_JointToConnected = Quaternion.Inverse(joint.transform.rotation) * joint.connectedBody.transform.rotation; //Your new rotation according to the connected bodies coordinate plane. I do this on Awake, and cache it for as long as the object remains in the scene.

Now say your joint has been moved, rotated etc and you now want to enable it in a new location/rotation. Firstly, cache the new current rotation that you want, then take the initial rotation and rotate it to the coordinate plane of the connected body. You then set the Joint rotation to it, reset the axes, and finally put the joint back to the new current rotation.

Quaternion currentRotation = joint.transform.rotation; //caches the new rotation that you want the joint to have.
joint.transform.rotation = Quaternion.Inverse(initialRotation_JointToConnected) * joint.connectedBody.transform.rotation; //Rotates the cached initial rotation to once again match the coordinate plane of the connected body
joint.axis = joint.axis; //Triggers a limit recalculation
joint.transform.rotation = currentRotation; //Puts the joint object back to where you want it

I have a script attached to every joint, and all this happens on enable. It’s a bit confusing but once you understand the core of what is happening, this makes a lot of sense and can fit any sort of hierarchy joint setup. Hope this saves someone wasted time fussing around with the axes.