Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Unity extension methods for computing a ConfigurableJoint.TargetRotation value from a given local or world rotation.
using UnityEngine;
public static class ConfigurableJointExtensions {
/// <summary>
/// Sets a joint's targetRotation to match a given local rotation.
/// The joint transform's local rotation must be cached on Start and passed into this method.
/// </summary>
public static void SetTargetRotationLocal (this ConfigurableJoint joint, Quaternion targetLocalRotation, Quaternion startLocalRotation)
{
if (joint.configuredInWorldSpace) {
Debug.LogError ("SetTargetRotationLocal should not be used with joints that are configured in world space. For world space joints, use SetTargetRotation.", joint);
}
SetTargetRotationInternal (joint, targetLocalRotation, startLocalRotation, Space.Self);
}
/// <summary>
/// Sets a joint's targetRotation to match a given world rotation.
/// The joint transform's world rotation must be cached on Start and passed into this method.
/// </summary>
public static void SetTargetRotation (this ConfigurableJoint joint, Quaternion targetWorldRotation, Quaternion startWorldRotation)
{
if (!joint.configuredInWorldSpace) {
Debug.LogError ("SetTargetRotation must be used with joints that are configured in world space. For local space joints, use SetTargetRotationLocal.", joint);
}
SetTargetRotationInternal (joint, targetWorldRotation, startWorldRotation, Space.World);
}
static void SetTargetRotationInternal (ConfigurableJoint joint, Quaternion targetRotation, Quaternion startRotation, Space space)
{
// Calculate the rotation expressed by the joint's axis and secondary axis
var right = joint.axis;
var forward = Vector3.Cross (joint.axis, joint.secondaryAxis).normalized;
var up = Vector3.Cross (forward, right).normalized;
Quaternion worldToJointSpace = Quaternion.LookRotation (forward, up);
// Transform into world space
Quaternion resultRotation = Quaternion.Inverse (worldToJointSpace);
// Counter-rotate and apply the new local rotation.
// Joint space is the inverse of world space, so we need to invert our value
if (space == Space.World) {
resultRotation *= startRotation * Quaternion.Inverse (targetRotation);
} else {
resultRotation *= Quaternion.Inverse (targetRotation) * startRotation;
}
// Transform back into joint space
resultRotation *= worldToJointSpace;
// Set target rotation to our newly calculated rotation
joint.targetRotation = resultRotation;
}
}
@CustomPhase
Copy link

CustomPhase commented Feb 15, 2019

So is there no way of setting the world-space target on local-configured joint? Ive tried rotating the target to inverse parent rotation, but it doesnt seem to be correct. Any ideas?

@CustomPhase
Copy link

CustomPhase commented Feb 15, 2019

Nevermind, i was using connectedBody rotation, instead of direct parent. This works perfectly fine for setting world target on local joints:

public static void SetTargetRotation(this ConfigurableJoint cj, Quaternion startRot, Quaternion target, Space space)
{
	Vector3 right = cj.axis;
	Vector3 forward = Vector3.Cross(cj.axis, cj.secondaryAxis).normalized;
	Vector3 up = Vector3.Cross(forward, right).normalized;
	Quaternion localToJointSpace = Quaternion.LookRotation(forward, up);
	if (space == Space.World)
	{
		Quaternion worldToLocal = Quaternion.Inverse(cj.transform.parent.rotation);
		target = worldToLocal * target;
	}
	cj.targetRotation = Quaternion.Inverse(localToJointSpace) * Quaternion.Inverse(target) * startRot * localToJointSpace;
}

@dunky11
Copy link

dunky11 commented Oct 14, 2019

Your script works fine for performing a single rotation, but i always have trouble implementing it in FixedUpdate(). I get the error:
"Skipped updating the transform of this Rigidbody because its components are infinite. Could you have applied infinite forces, acceleration or set huge velocity?"

void Awake() {
        Quaternion startRotation = arm.transform.rotation;
        joint = arm.GetComponent<ConfigurableJoint>();
}

void FixedUpdate() {
        joint.SetTargetRotation(Quaternion.Euler(0, 0.01f, 0), startRotation);
}

I just want to rotate the joint consistently along its joints y-axis. Im currently getting headaches over the simplest tasks using ConfigurableJoints :))

Thanks for any help in advance!

@dunky11
Copy link

dunky11 commented Oct 14, 2019

Nevermind, was my mistake. The problem was that i redefined "startRotation" to be a quaternion in the Awake() call, so always go the default Quaternion value for "startRotation" in FixedUpdate()

@531387937
Copy link

531387937 commented Mar 29, 2020

That's so coool!!!!

@iconnary
Copy link

iconnary commented Mar 30, 2020

Can I request that you add an additional method here to return the Local/World space rotation of the joint's targetRotation? I'm trying to Slerp a joint's targetRotation through to a new orientation but I need to know it's current orientation first.

public static Quaternion GetTargetRotation(ConfigurableJoint joint, Quaternion startRotation, Space space) { ... }

Thanks

@TrueBeef
Copy link

TrueBeef commented Apr 6, 2020

Hey @mstevenson, Quick question with the "StartRotation" quaternion. Is this the local rotation (Global, if not a child to anything) of the transform that the joint is created on at the time of joint-creation? Is this ever updated if connected bodies, anchors, etc change?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment