1
\$\begingroup\$

I am currently attempting to lock a non rigid body transform rotation around on an axis. It is parented to a gameobject that is rotating in 3D space. I want the Original transform to have the same rotation as the parent's but I want it to be locked on a certain certain axis. For the sake of clarifying my question I will provide some code and an example.

Example: There is a 3D model with a plane attached to the left upper arm subsegment(see hierarchy) whose initial normal is aligned with the global x-axis <1,0,0> or the initial right vector of the subsegment. In the context of this post, what I would like is to be able to align the normal of the plane with the subsegment's right vector but keep the plane's up axis locked.

3D humanoid model with plane Hierarchy

For instance, I do not want to the yaw component of the transform to change. Trying the following code does not result in the intended effect.

Vector3 eulerRepresentation = parent.transform.rotation.eulerAngles;
eulerRepresentation.y = 0; 
Quaternion fromEuler = Quaternion.Euler(eulerRepresentation);
transform.rotation = fromEuler;

This is very likely because of the duality of a rotation of Euler angles. An example is (180,0,180) is the same as (0,180,0)

When inspecting the euler values, it is exactly like I suspected. There doesn't seem to be correlation with the object's yaw and the object's euler.y value.

How would I go about locking either Yaw/pitch/roll?

\$\endgroup\$
  • \$\begingroup\$ Perhaps consider locking the rotation using a Transform.LookAt, then modify the lookat vector to suit your needs. Hint: transform.position + parent.transform.forward will give you a point to look at, but then you'd have to specify the up vector yourself to suit your roll. \$\endgroup\$ – Morten Andersen Feb 2 '17 at 14:58
2
\$\begingroup\$

I'd solve this using LookRotation. We can generalize Unity's helper method to let us point any particular local axis we want at any particular global axis, like so:

Quaternion GeneralizedLookRotation(
      Vector3 localExactAxis,
      Vector3 globalExactAxis,
      Vector3 localApproximateAxis,
      Vector3 globalApproximateAxis
    )
{

    // Rotate our chosen local axes into a standard orientation.
    Quaternion standardize = Quaternion.Inverse(
               Quaternion.LookRotation(localExactAxis, localApproximateAxis)
        );

    // Rotate the standard orientation to point to the chosen global axes.
    Quaternion turn = Quaternion.LookRotation(globalExactAxis, globalApproximateAxis);

    // Chain both operations to rotate the local axes to the global axes.
    return turn * standardize;
}

This function will take a vector in local coordinates, localExactAxis, and rotate it to point exactly along globalExactAxis in world coordinates.

That still leaves us with one degree of freedom (twisting around globalExactAxis), so we also provide a localApproximateAxis which should map as close as possible to globalApproximateAxis.

Here's what this looks like:

Animated example of three rotation methods.

The left is normal parenting behaviour. In the other two, I added a LateUpdate() method (so that it runs after any animations or update scripts have rotated the parent) that adjusts the child rotation like so:

if (parentExact) // Middle version
{
    // Point my right vector exactly along my parent's forward,
    // and my up vector as close as possible to world up.
    transform.rotation = GeneralizedLookRotation(
          Vector3.right, transform.parent.forward,
          Vector3.up, Vector3.up
        );

    // (Effectively, yaw & pitch with parent, but don't roll)
}
else  // Rightmost version
{
     // Point my up vector exactly along world up,
     // and my right vector as close as possible to my parent's forward.
     transform.rotation = GeneralizedLookRotation(            
          Vector3.up, Vector3.up,
          Vector3.right, transform.parent.forward
        );

     // (Effectively, yaw with parent, but don't pitch/roll)
}
\$\endgroup\$
1
\$\begingroup\$

There are a few approaches here.

Freeze the axis in the Unity Inspector

Select the object you are rotating, I am assuming a rigidbody, and go into the inspector. Find the constraints property and check off the axis you want to freeze. That will not allow the rotation or movement (whichever you check off since they're both clickable) of your object in that axis.

Make a float to insert and lock the axis

Create a float lockAxis = 0; Add it into your Update()

void Update()
{
     transform.rotation = Quaternion.Euler(transform.rotation.eulerAngles.x, lockPos, lockPos);
} 

This is setting your specific axis to 0 every time so that is effectively locked. Sources.

Freeze the axis using eulerAngles

transform.eulerAngles can be assigned a Vector3.

 Vector3 eulerAngles=new Vector3(0, 0, 0);
 transform.eulerAngles=eulerAngles;

However you cannot directly assign a quaternion.

 Quaternion rotation=new Quaternion(0, 0, 10, 10);
 transform.eulerAngles=q.eulerAngles;

In contrast, transform.rotation can be assigned a Quaternion.

 Quaternion rotation=new Quaternion(0, 0, 10, 10);
 transform.rotation=rotation;

However you cannot directly assign a Vector3.

 Vector3 eulerAngles=new Vector3(0, 0, 0);
 transform.rotation=Quaternion.Euler(eulerAngles);

Sources.

Use Quaternions R/Y/P equations

 roll  = Mathf.Atan2(2*y*w + 2*x*z, 1 - 2*y*y - 2*z*z);
 pitch = Mathf.Atan2(2*x*w + 2*y*z, 1 - 2*x*x - 2*z*z);
 yaw   =  Mathf.Asin(2*x*y + 2*z*w);

You can use those if you directly want the roll/pitch/yaw - that's the formula for them specifically. Sources.

Also make sure that you don't end up with a Gimbal Lock :)

\$\endgroup\$
  • 1
    \$\begingroup\$ Unfortunately the suggested solution doesn't appear to be working well. I think the only solution I might have is to find an appropriate look at vector and lock the up vector according to the parent's up vector. The issue with using Euler angles appears to be the same as my initial issue. If I am correct this is because of Eulers duality and the solution doesn't lie with Euler angles but rather with vectors. I'll update my question soon to represent what I'm truly trying to accomplish(part of it is in fact locking an axis ) \$\endgroup\$ – fryBender Feb 2 '17 at 16:23
  • \$\begingroup\$ Sorry it didn't correct it. But let us know when you've update it or any progress you make and I can see what else I can do to help then. \$\endgroup\$ – n_plum Feb 2 '17 at 17:07
  • \$\begingroup\$ @fryBender go to the source I listed under Freeze the axis using eulerAngles. I might be able to help solve that issue with duality. \$\endgroup\$ – n_plum Feb 2 '17 at 17:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.